Merge branch 'devel' of github.com:SheffieldML/GPy into devel

This commit is contained in:
James Hensman 2013-04-25 12:52:13 +01:00
commit 5dd343e89d
8 changed files with 617 additions and 264 deletions

View file

@ -7,7 +7,7 @@ from scipy import optimize
import sys, pdb import sys, pdb
import multiprocessing as mp import multiprocessing as mp
from GPy.util.misc import opt_wrapper from GPy.util.misc import opt_wrapper
#import numdifftools as ndt # import numdifftools as ndt
from parameterised import parameterised, truncate_pad from parameterised import parameterised, truncate_pad
import priors import priors
from ..util.linalg import jitchol from ..util.linalg import jitchol
@ -25,14 +25,14 @@ class model(parameterised):
self.preferred_optimizer = 'tnc' self.preferred_optimizer = 'tnc'
def _get_params(self): def _get_params(self):
raise NotImplementedError, "this needs to be implemented to use the model class" raise NotImplementedError, "this needs to be implemented to use the model class"
def _set_params(self,x): def _set_params(self, x):
raise NotImplementedError, "this needs to be implemented to use the model class" raise NotImplementedError, "this needs to be implemented to use the model class"
def log_likelihood(self): def log_likelihood(self):
raise NotImplementedError, "this needs to be implemented to use the model class" raise NotImplementedError, "this needs to be implemented to use the model class"
def _log_likelihood_gradients(self): def _log_likelihood_gradients(self):
raise NotImplementedError, "this needs to be implemented to use the model class" raise NotImplementedError, "this needs to be implemented to use the model class"
def set_prior(self,which,what): def set_prior(self, which, what):
""" """
Sets priors on the model parameters. Sets priors on the model parameters.
@ -53,84 +53,59 @@ class model(parameterised):
which = self.grep_param_names(which) which = self.grep_param_names(which)
#check tied situation # check tied situation
tie_partial_matches = [tie for tie in self.tied_indices if (not set(tie).isdisjoint(set(which))) & (not set(tie)==set(which))] tie_partial_matches = [tie for tie in self.tied_indices if (not set(tie).isdisjoint(set(which))) & (not set(tie) == set(which))]
if len(tie_partial_matches): if len(tie_partial_matches):
raise ValueError, "cannot place prior across partial ties" raise ValueError, "cannot place prior across partial ties"
tie_matches = [tie for tie in self.tied_indices if set(which)==set(tie) ] tie_matches = [tie for tie in self.tied_indices if set(which) == set(tie) ]
if len(tie_matches)>1: if len(tie_matches) > 1:
raise ValueError, "cannot place prior across multiple ties" raise ValueError, "cannot place prior across multiple ties"
elif len(tie_matches)==1: elif len(tie_matches) == 1:
which = which[:1]# just place a prior object on the first parameter which = which[:1] # just place a prior object on the first parameter
#check constraints are okay # check constraints are okay
if isinstance(what, (priors.gamma, priors.log_Gaussian)): if isinstance(what, (priors.gamma, priors.log_Gaussian)):
assert not np.any(which[:,None]==self.constrained_negative_indices), "constraint and prior incompatible" assert not np.any(which[:, None] == self.constrained_negative_indices), "constraint and prior incompatible"
assert not np.any(which[:,None]==self.constrained_bounded_indices), "constraint and prior incompatible" assert not np.any(which[:, None] == self.constrained_bounded_indices), "constraint and prior incompatible"
unconst = np.setdiff1d(which, self.constrained_positive_indices) unconst = np.setdiff1d(which, self.constrained_positive_indices)
if len(unconst): if len(unconst):
print "Warning: constraining parameters to be positive:" print "Warning: constraining parameters to be positive:"
print '\n'.join([n for i,n in enumerate(self._get_param_names()) if i in unconst]) print '\n'.join([n for i, n in enumerate(self._get_param_names()) if i in unconst])
print '\n' print '\n'
self.constrain_positive(unconst) self.constrain_positive(unconst)
elif isinstance(what,priors.Gaussian): elif isinstance(what, priors.Gaussian):
assert not np.any(which[:,None]==self.all_constrained_indices()), "constraint and prior incompatible" assert not np.any(which[:, None] == self.all_constrained_indices()), "constraint and prior incompatible"
else: else:
raise ValueError, "prior not recognised" raise ValueError, "prior not recognised"
#store the prior in a local list # store the prior in a local list
for w in which: for w in which:
self.priors[w] = what self.priors[w] = what
def get(self,name, return_names=False): def get_gradient(self, name, return_names=False):
"""
Get a model parameter by name. The name is applied as a regular expression and all parameters that match that regular expression are returned.
"""
matches = self.grep_param_names(name)
if len(matches):
if return_names:
return self._get_params()[matches], np.asarray(self._get_param_names())[matches].tolist()
else:
return self._get_params()[matches]
else:
raise AttributeError, "no parameter matches %s"%name
def set(self,name,val):
"""
Set model parameter(s) by name. The name is provided as a regular expression. All parameters matching that regular expression are set to ghe given value.
"""
matches = self.grep_param_names(name)
if len(matches):
x = self._get_params()
x[matches] = val
self._set_params(x)
else:
raise AttributeError, "no parameter matches %s"%name
def get_gradient(self,name, return_names=False):
""" """
Get model gradient(s) by name. The name is applied as a regular expression and all parameters that match that regular expression are returned. Get model gradient(s) by name. The name is applied as a regular expression and all parameters that match that regular expression are returned.
""" """
matches = self.grep_param_names(name) matches = self.grep_param_names(name)
if len(matches): if len(matches):
if return_names: if return_names:
return self._log_likelihood_gradients()[matches], np.asarray(self._get_param_names())[matches].tolist() return self._log_likelihood_gradients()[matches], np.asarray(self._get_param_names())[matches].tolist()
else: else:
return self._log_likelihood_gradients()[matches] return self._log_likelihood_gradients()[matches]
else: else:
raise AttributeError, "no parameter matches %s"%name raise AttributeError, "no parameter matches %s" % name
def log_prior(self): def log_prior(self):
"""evaluate the prior""" """evaluate the prior"""
return np.sum([p.lnpdf(x) for p, x in zip(self.priors,self._get_params()) if p is not None]) return np.sum([p.lnpdf(x) for p, x in zip(self.priors, self._get_params()) if p is not None])
def _log_prior_gradients(self): def _log_prior_gradients(self):
"""evaluate the gradients of the priors""" """evaluate the gradients of the priors"""
x = self._get_params() x = self._get_params()
ret = np.zeros(x.size) ret = np.zeros(x.size)
[np.put(ret,i,p.lnpdf_grad(xx)) for i,(p,xx) in enumerate(zip(self.priors,x)) if not p is None] [np.put(ret, i, p.lnpdf_grad(xx)) for i, (p, xx) in enumerate(zip(self.priors, x)) if not p is None]
return ret return ret
def _transform_gradients(self, g): def _transform_gradients(self, g):
@ -139,13 +114,13 @@ class model(parameterised):
""" """
x = self._get_params() x = self._get_params()
g[self.constrained_positive_indices] = g[self.constrained_positive_indices]*x[self.constrained_positive_indices] g[self.constrained_positive_indices] = g[self.constrained_positive_indices] * x[self.constrained_positive_indices]
g[self.constrained_negative_indices] = g[self.constrained_negative_indices]*x[self.constrained_negative_indices] g[self.constrained_negative_indices] = g[self.constrained_negative_indices] * x[self.constrained_negative_indices]
[np.put(g,i,g[i]*(x[i]-l)*(h-x[i])/(h-l)) for i,l,h in zip(self.constrained_bounded_indices, self.constrained_bounded_lowers, self.constrained_bounded_uppers)] [np.put(g, i, g[i] * (x[i] - l) * (h - x[i]) / (h - l)) for i, l, h in zip(self.constrained_bounded_indices, self.constrained_bounded_lowers, self.constrained_bounded_uppers)]
[np.put(g,i,v) for i,v in [(t[0],np.sum(g[t])) for t in self.tied_indices]] [np.put(g, i, v) for i, v in [(t[0], np.sum(g[t])) for t in self.tied_indices]]
if len(self.tied_indices) or len(self.constrained_fixed_indices): if len(self.tied_indices) or len(self.constrained_fixed_indices):
to_remove = np.hstack((self.constrained_fixed_indices+[t[1:] for t in self.tied_indices])) to_remove = np.hstack((self.constrained_fixed_indices + [t[1:] for t in self.tied_indices]))
return np.delete(g,to_remove) return np.delete(g, to_remove)
else: else:
return g return g
@ -155,15 +130,15 @@ class model(parameterised):
Randomize the model. Randomize the model.
Make this draw from the prior if one exists, else draw from N(0,1) Make this draw from the prior if one exists, else draw from N(0,1)
""" """
#first take care of all parameters (from N(0,1)) # first take care of all parameters (from N(0,1))
x = self._get_params_transformed() x = self._get_params_transformed()
x = np.random.randn(x.size) x = np.random.randn(x.size)
self._set_params_transformed(x) self._set_params_transformed(x)
#now draw from prior where possible # now draw from prior where possible
x = self._get_params() x = self._get_params()
[np.put(x,i,p.rvs(1)) for i,p in enumerate(self.priors) if not p is None] [np.put(x, i, p.rvs(1)) for i, p in enumerate(self.priors) if not p is None]
self._set_params(x) self._set_params(x)
self._set_params_transformed(self._get_params_transformed())#makes sure all of the tied parameters get the same init (since there's only one prior object...) self._set_params_transformed(self._get_params_transformed()) # makes sure all of the tied parameters get the same init (since there's only one prior object...)
def optimize_restarts(self, Nrestarts=10, robust=False, verbose=True, parallel=False, num_processes=None, **kwargs): def optimize_restarts(self, Nrestarts=10, robust=False, verbose=True, parallel=False, num_processes=None, **kwargs):
@ -197,10 +172,10 @@ class model(parameterised):
pool = mp.Pool(processes=num_processes) pool = mp.Pool(processes=num_processes)
for i in range(Nrestarts): for i in range(Nrestarts):
self.randomize() self.randomize()
job = pool.apply_async(opt_wrapper, args = (self,), kwds = kwargs) job = pool.apply_async(opt_wrapper, args=(self,), kwds=kwargs)
jobs.append(job) jobs.append(job)
pool.close() # signal that no more data coming in pool.close() # signal that no more data coming in
pool.join() # wait for all the tasks to complete pool.join() # wait for all the tasks to complete
except KeyboardInterrupt: except KeyboardInterrupt:
print "Ctrl+c received, terminating and joining pool." print "Ctrl+c received, terminating and joining pool."
@ -216,10 +191,10 @@ class model(parameterised):
self.optimization_runs.append(jobs[i].get()) self.optimization_runs.append(jobs[i].get())
if verbose: if verbose:
print("Optimization restart {0}/{1}, f = {2}".format(i+1, Nrestarts, self.optimization_runs[-1].f_opt)) print("Optimization restart {0}/{1}, f = {2}".format(i + 1, Nrestarts, self.optimization_runs[-1].f_opt))
except Exception as e: except Exception as e:
if robust: if robust:
print("Warning - optimization restart {0}/{1} failed".format(i+1, Nrestarts)) print("Warning - optimization restart {0}/{1} failed".format(i + 1, Nrestarts))
else: else:
raise e raise e
@ -229,11 +204,11 @@ class model(parameterised):
else: else:
self._set_params_transformed(initial_parameters) self._set_params_transformed(initial_parameters)
def ensure_default_constraints(self,warn=False): def ensure_default_constraints(self, warn=False):
""" """
Ensure that any variables which should clearly be positive have been constrained somehow. Ensure that any variables which should clearly be positive have been constrained somehow.
""" """
positive_strings = ['variance','lengthscale', 'precision'] positive_strings = ['variance', 'lengthscale', 'precision']
param_names = self._get_param_names() param_names = self._get_param_names()
currently_constrained = self.all_constrained_indices() currently_constrained = self.all_constrained_indices()
to_make_positive = [] to_make_positive = []
@ -242,9 +217,9 @@ class model(parameterised):
if not (i in currently_constrained): if not (i in currently_constrained):
to_make_positive.append(re.escape(param_names[i])) to_make_positive.append(re.escape(param_names[i]))
if warn: if warn:
print "Warning! constraining %s postive"%name print "Warning! constraining %s postive" % name
if len(to_make_positive): if len(to_make_positive):
self.constrain_positive('('+'|'.join(to_make_positive)+')') self.constrain_positive('(' + '|'.join(to_make_positive) + ')')
@ -262,14 +237,14 @@ class model(parameterised):
self._set_params_transformed(x) self._set_params_transformed(x)
LL_gradients = self._transform_gradients(self._log_likelihood_gradients()) LL_gradients = self._transform_gradients(self._log_likelihood_gradients())
prior_gradients = self._transform_gradients(self._log_prior_gradients()) prior_gradients = self._transform_gradients(self._log_prior_gradients())
return - LL_gradients - prior_gradients return -LL_gradients - prior_gradients
def objective_and_gradients(self, x): def objective_and_gradients(self, x):
self._set_params_transformed(x) self._set_params_transformed(x)
obj_f = -self.log_likelihood() - self.log_prior() obj_f = -self.log_likelihood() - self.log_prior()
LL_gradients = self._transform_gradients(self._log_likelihood_gradients()) LL_gradients = self._transform_gradients(self._log_likelihood_gradients())
prior_gradients = self._transform_gradients(self._log_prior_gradients()) prior_gradients = self._transform_gradients(self._log_prior_gradients())
obj_grads = - LL_gradients - prior_gradients obj_grads = -LL_gradients - prior_gradients
return obj_f, obj_grads return obj_f, obj_grads
def optimize(self, optimizer=None, start=None, **kwargs): def optimize(self, optimizer=None, start=None, **kwargs):
@ -289,13 +264,13 @@ class model(parameterised):
start = self._get_params_transformed() start = self._get_params_transformed()
optimizer = optimization.get_optimizer(optimizer) optimizer = optimization.get_optimizer(optimizer)
opt = optimizer(start, model = self, **kwargs) opt = optimizer(start, model=self, **kwargs)
opt.run(f_fp=self.objective_and_gradients, f=self.objective_function, fp=self.objective_function_gradients) opt.run(f_fp=self.objective_and_gradients, f=self.objective_function, fp=self.objective_function_gradients)
self.optimization_runs.append(opt) self.optimization_runs.append(opt)
self._set_params_transformed(opt.x_opt) self._set_params_transformed(opt.x_opt)
def optimize_SGD(self, momentum = 0.1, learning_rate = 0.01, iterations = 20, **kwargs): def optimize_SGD(self, momentum=0.1, learning_rate=0.01, iterations=20, **kwargs):
# assert self.Y.shape[1] > 1, "SGD only works with D > 1" # assert self.Y.shape[1] > 1, "SGD only works with D > 1"
sgd = SGD.StochasticGD(self, iterations, learning_rate, momentum, **kwargs) sgd = SGD.StochasticGD(self, iterations, learning_rate, momentum, **kwargs)
sgd.run() sgd.run()
@ -303,8 +278,8 @@ class model(parameterised):
def Laplace_covariance(self): def Laplace_covariance(self):
"""return the covariance matric of a Laplace approximatino at the current (stationary) point""" """return the covariance matric of a Laplace approximatino at the current (stationary) point"""
#TODO add in the prior contributions for MAP estimation # TODO add in the prior contributions for MAP estimation
#TODO fix the hessian for tied, constrained and fixed components # TODO fix the hessian for tied, constrained and fixed components
if hasattr(self, 'log_likelihood_hessian'): if hasattr(self, 'log_likelihood_hessian'):
A = -self.log_likelihood_hessian() A = -self.log_likelihood_hessian()
@ -318,8 +293,8 @@ class model(parameterised):
A = -h(x) A = -h(x)
self._set_params(x) self._set_params(x)
# check for almost zero components on the diagonal which screw up the cholesky # check for almost zero components on the diagonal which screw up the cholesky
aa = np.nonzero((np.diag(A)<1e-6) & (np.diag(A)>0.))[0] aa = np.nonzero((np.diag(A) < 1e-6) & (np.diag(A) > 0.))[0]
A[aa,aa] = 0. A[aa, aa] = 0.
return A return A
def Laplace_evidence(self): def Laplace_evidence(self):
@ -330,11 +305,11 @@ class model(parameterised):
hld = np.sum(np.log(np.diag(jitchol(A)[0]))) hld = np.sum(np.log(np.diag(jitchol(A)[0])))
except: except:
return np.nan return np.nan
return 0.5*self._get_params().size*np.log(2*np.pi) + self.log_likelihood() - hld return 0.5 * self._get_params().size * np.log(2 * np.pi) + self.log_likelihood() - hld
def __str__(self): def __str__(self):
s = parameterised.__str__(self).split('\n') s = parameterised.__str__(self).split('\n')
#add priors to the string # add priors to the string
strs = [str(p) if p is not None else '' for p in self.priors] strs = [str(p) if p is not None else '' for p in self.priors]
width = np.array(max([len(p) for p in strs] + [5])) + 4 width = np.array(max([len(p) for p in strs] + [5])) + 4
@ -345,16 +320,16 @@ class model(parameterised):
obj_funct += ', Log prior: {0:.3e}, LL+prior = {0:.3e}'.format(log_prior, log_like + log_prior) obj_funct += ', Log prior: {0:.3e}, LL+prior = {0:.3e}'.format(log_prior, log_like + log_prior)
obj_funct += '\n\n' obj_funct += '\n\n'
s[0] = obj_funct + s[0] s[0] = obj_funct + s[0]
s[0] += "|{h:^{col}}".format(h = 'Prior', col = width) s[0] += "|{h:^{col}}".format(h='Prior', col=width)
s[1] += '-'*(width + 1) s[1] += '-' * (width + 1)
for p in range(2, len(strs)+2): for p in range(2, len(strs) + 2):
s[p] += '|{prior:^{width}}'.format(prior = strs[p-2], width = width) s[p] += '|{prior:^{width}}'.format(prior=strs[p - 2], width=width)
return '\n'.join(s) return '\n'.join(s)
def checkgrad(self, target_param = None, verbose=False, step=1e-6, tolerance = 1e-3): def checkgrad(self, target_param=None, verbose=False, step=1e-6, tolerance=1e-3):
""" """
Check the gradient of the model by comparing to a numerical estimate. Check the gradient of the model by comparing to a numerical estimate.
If the verbose flag is passed, invividual components are tested (and printed) If the verbose flag is passed, invividual components are tested (and printed)
@ -374,27 +349,27 @@ class model(parameterised):
x = self._get_params_transformed().copy() x = self._get_params_transformed().copy()
if not verbose: if not verbose:
#just check the global ratio # just check the global ratio
dx = step*np.sign(np.random.uniform(-1,1,x.size)) dx = step * np.sign(np.random.uniform(-1, 1, x.size))
#evaulate around the point x # evaulate around the point x
f1, g1 = self.objective_and_gradients(x+dx) f1, g1 = self.objective_and_gradients(x + dx)
f2, g2 = self.objective_and_gradients(x-dx) f2, g2 = self.objective_and_gradients(x - dx)
gradient = self.objective_function_gradients(x) gradient = self.objective_function_gradients(x)
numerical_gradient = (f1-f2)/(2*dx) numerical_gradient = (f1 - f2) / (2 * dx)
global_ratio = (f1-f2)/(2*np.dot(dx,gradient)) global_ratio = (f1 - f2) / (2 * np.dot(dx, gradient))
if (np.abs(1.-global_ratio)<tolerance) and not np.isnan(global_ratio): if (np.abs(1. - global_ratio) < tolerance) and not np.isnan(global_ratio):
return True return True
else: else:
return False return False
else: else:
#check the gradient of each parameter individually, and do some pretty printing # check the gradient of each parameter individually, and do some pretty printing
try: try:
names = self._get_param_names_transformed() names = self._get_param_names_transformed()
except NotImplementedError: except NotImplementedError:
names = ['Variable %i'%i for i in range(len(x))] names = ['Variable %i' % i for i in range(len(x))]
# Prepare for pretty-printing # Prepare for pretty-printing
header = ['Name', 'Ratio', 'Difference', 'Analytical', 'Numerical'] header = ['Name', 'Ratio', 'Difference', 'Analytical', 'Numerical']
@ -403,9 +378,9 @@ class model(parameterised):
cols = [max_names] cols = [max_names]
cols.extend([max(float_len, len(header[i])) for i in range(1, len(header))]) cols.extend([max(float_len, len(header[i])) for i in range(1, len(header))])
cols = np.array(cols) + 5 cols = np.array(cols) + 5
header_string = ["{h:^{col}}".format(h = header[i], col = cols[i]) for i in range(len(cols))] header_string = ["{h:^{col}}".format(h=header[i], col=cols[i]) for i in range(len(cols))]
header_string = map(lambda x: '|'.join(x), [header_string]) header_string = map(lambda x: '|'.join(x), [header_string])
separator = '-'*len(header_string[0]) separator = '-' * len(header_string[0])
print '\n'.join([header_string[0], separator]) print '\n'.join([header_string[0], separator])
if target_param is None: if target_param is None:
@ -421,11 +396,11 @@ class model(parameterised):
f2, g2 = self.objective_and_gradients(xx) f2, g2 = self.objective_and_gradients(xx)
gradient = self.objective_function_gradients(x)[i] gradient = self.objective_function_gradients(x)[i]
numerical_gradient = (f1-f2)/(2*step) numerical_gradient = (f1 - f2) / (2 * step)
ratio = (f1-f2)/(2*step*gradient) ratio = (f1 - f2) / (2 * step * gradient)
difference = np.abs((f1-f2)/2/step - gradient) difference = np.abs((f1 - f2) / 2 / step - gradient)
if (np.abs(ratio-1)<tolerance): if (np.abs(ratio - 1) < tolerance):
formatted_name = "\033[92m {0} \033[0m".format(names[i]) formatted_name = "\033[92m {0} \033[0m".format(names[i])
else: else:
formatted_name = "\033[91m {0} \033[0m".format(names[i]) formatted_name = "\033[91m {0} \033[0m".format(names[i])
@ -433,7 +408,7 @@ class model(parameterised):
d = '%.6f' % float(difference) d = '%.6f' % float(difference)
g = '%.6f' % gradient g = '%.6f' % gradient
ng = '%.6f' % float(numerical_gradient) ng = '%.6f' % float(numerical_gradient)
grad_string = "{0:^{c0}}|{1:^{c1}}|{2:^{c2}}|{3:^{c3}}|{4:^{c4}}".format(formatted_name,r,d,g, ng, c0 = cols[0]+9, c1 = cols[1], c2 = cols[2], c3 = cols[3], c4 = cols[4]) grad_string = "{0:^{c0}}|{1:^{c1}}|{2:^{c2}}|{3:^{c3}}|{4:^{c4}}".format(formatted_name, r, d, g, ng, c0=cols[0] + 9, c1=cols[1], c2=cols[2], c3=cols[3], c4=cols[4])
print grad_string print grad_string
def input_sensitivity(self): def input_sensitivity(self):
@ -444,21 +419,21 @@ class model(parameterised):
TODO: proper sensitivity analysis TODO: proper sensitivity analysis
""" """
if not hasattr(self,'kern'): if not hasattr(self, 'kern'):
raise ValueError, "this model has no kernel" raise ValueError, "this model has no kernel"
k = [p for p in self.kern.parts if p.name in ['rbf','linear']] k = [p for p in self.kern.parts if p.name in ['rbf', 'linear']]
if (not len(k)==1) or (not k[0].ARD): if (not len(k) == 1) or (not k[0].ARD):
raise ValueError, "cannot determine sensitivity for this kernel" raise ValueError, "cannot determine sensitivity for this kernel"
k = k[0] k = k[0]
if k.name=='rbf': if k.name == 'rbf':
return k.lengthscale return k.lengthscale
elif k.name=='linear': elif k.name == 'linear':
return 1./k.variances return 1. / k.variances
def pseudo_EM(self,epsilon=.1,**kwargs): def pseudo_EM(self, epsilon=.1, **kwargs):
""" """
TODO: Should this not bein the GP class? TODO: Should this not bein the GP class?
EM - like algorithm for Expectation Propagation and Laplace approximation EM - like algorithm for Expectation Propagation and Laplace approximation
@ -472,7 +447,7 @@ class model(parameterised):
:type optimzer: string TODO: valid strings? :type optimzer: string TODO: valid strings?
""" """
assert isinstance(self.likelihood,likelihoods.EP), "EPEM is only available for EP likelihoods" assert isinstance(self.likelihood, likelihoods.EP), "EPEM is only available for EP likelihoods"
ll_change = epsilon + 1. ll_change = epsilon + 1.
iteration = 0 iteration = 0
last_ll = -np.exp(1000) last_ll = -np.exp(1000)
@ -492,9 +467,9 @@ class model(parameterised):
ll_change = new_ll - last_ll ll_change = new_ll - last_ll
if ll_change < 0: if ll_change < 0:
self.likelihood = last_approximation #restore previous likelihood approximation self.likelihood = last_approximation # restore previous likelihood approximation
self._set_params(last_params) #restore model parameters self._set_params(last_params) # restore model parameters
print "Log-likelihood decrement: %s \nLast likelihood update discarded." %ll_change print "Log-likelihood decrement: %s \nLast likelihood update discarded." % ll_change
stop = True stop = True
else: else:
self.optimize(**kwargs) self.optimize(**kwargs)
@ -503,5 +478,5 @@ class model(parameterised):
stop = True stop = True
iteration += 1 iteration += 1
if stop: if stop:
print "%s iterations." %iteration print "%s iterations." % iteration

View file

@ -8,24 +8,25 @@ import copy
import cPickle import cPickle
import os import os
from ..util.squashers import sigmoid from ..util.squashers import sigmoid
import warnings
def truncate_pad(string,width,align='m'): def truncate_pad(string, width, align='m'):
""" """
A helper function to make aligned strings for parameterised.__str__ A helper function to make aligned strings for parameterised.__str__
""" """
width=max(width,4) width = max(width, 4)
if len(string)>width: if len(string) > width:
return string[:width-3]+'...' return string[:width - 3] + '...'
elif len(string)==width: elif len(string) == width:
return string return string
elif len(string)<width: elif len(string) < width:
diff = width-len(string) diff = width - len(string)
if align=='m': if align == 'm':
return ' '*np.floor(diff/2.) + string + ' '*np.ceil(diff/2.) return ' ' * np.floor(diff / 2.) + string + ' ' * np.ceil(diff / 2.)
elif align=='l': elif align == 'l':
return string + ' '*diff return string + ' ' * diff
elif align=='r': elif align == 'r':
return ' '*diff + string return ' ' * diff + string
else: else:
raise ValueError raise ValueError
@ -37,15 +38,15 @@ class parameterised(object):
self.tied_indices = [] self.tied_indices = []
self.constrained_fixed_indices = [] self.constrained_fixed_indices = []
self.constrained_fixed_values = [] self.constrained_fixed_values = []
self.constrained_positive_indices = np.empty(shape=(0,),dtype=np.int64) self.constrained_positive_indices = np.empty(shape=(0,), dtype=np.int64)
self.constrained_negative_indices = np.empty(shape=(0,),dtype=np.int64) self.constrained_negative_indices = np.empty(shape=(0,), dtype=np.int64)
self.constrained_bounded_indices = [] self.constrained_bounded_indices = []
self.constrained_bounded_uppers = [] self.constrained_bounded_uppers = []
self.constrained_bounded_lowers = [] self.constrained_bounded_lowers = []
def pickle(self,filename,protocol=-1): def pickle(self, filename, protocol= -1):
f = file(filename,'w') f = file(filename, 'w')
cPickle.dump(self,f,protocol) cPickle.dump(self, f, protocol)
f.close() f.close()
def copy(self): def copy(self):
@ -55,18 +56,85 @@ class parameterised(object):
return copy.deepcopy(self) return copy.deepcopy(self)
@property
def params(self):
"""
Returns a **copy** of parameters in non transformed space
:see_also: :py:func:`GPy.core.parameterised.params_transformed`
"""
return self._get_params()
@params.setter
def params(self, params):
self._set_params(params)
@property
def params_transformed(self):
"""
Returns a **copy** of parameters in transformed space
:see_also: :py:func:`GPy.core.parameterised.params`
"""
return self._get_params_transformed()
@params_transformed.setter
def params_transformed(self, params):
self._set_params_transformed(params)
_get_set_deprecation = """get and set methods wont be available at next minor release
in the next releases you will get and set with following syntax:
Assume m is a model class:
print m['var'] # > prints all parameters matching 'var'
m['var'] = 2. # > sets all parameters matching 'var' to 2.
m['var'] = <array-like> # > sets parameters matching 'var' to <array-like>
"""
def get(self, name):
warnings.warn(self._get_set_deprecation, FutureWarning, stacklevel=2)
return self[name]
def set(self, name, val):
warnings.warn(self._get_set_deprecation, FutureWarning, stacklevel=2)
self[name] = val
def __getitem__(self, name, return_names=False):
"""
Get a model parameter by name. The name is applied as a regular expression and all parameters that match that regular expression are returned.
"""
matches = self.grep_param_names(name)
if len(matches):
if return_names:
return self._get_params()[matches], np.asarray(self._get_param_names())[matches].tolist()
else:
return self._get_params()[matches]
else:
raise AttributeError, "no parameter matches %s" % name
def __setitem__(self, name, val):
"""
Set model parameter(s) by name. The name is provided as a regular expression. All parameters matching that regular expression are set to ghe given value.
"""
matches = self.grep_param_names(name)
if len(matches):
val = np.array(val)
assert (val.size == 1) or val.size == len(matches), "Shape mismatch: {}:({},)".format(val.size, len(matches))
x = self.params
x[matches] = val
self.params = x
# import ipdb;ipdb.set_trace()
# self.params[matches] = val
else:
raise AttributeError, "no parameter matches %s" % name
def tie_params(self, which): def tie_params(self, which):
matches = self.grep_param_names(which) matches = self.grep_param_names(which)
assert matches.size > 0, "need at least something to tie together" assert matches.size > 0, "need at least something to tie together"
if len(self.tied_indices): if len(self.tied_indices):
assert not np.any(matches[:,None]==np.hstack(self.tied_indices)), "Some indices are already tied!" assert not np.any(matches[:, None] == np.hstack(self.tied_indices)), "Some indices are already tied!"
self.tied_indices.append(matches) self.tied_indices.append(matches)
#TODO only one of the priors will be evaluated. Give a warning message if the priors are not identical # TODO only one of the priors will be evaluated. Give a warning message if the priors are not identical
if hasattr(self,'prior'): if hasattr(self, 'prior'):
pass pass
self._set_params_transformed(self._get_params_transformed())# sets tied parameters to single value self._set_params_transformed(self._get_params_transformed()) # sets tied parameters to single value
def untie_everything(self): def untie_everything(self):
"""Unties all parameters by setting tied_indices to an empty list.""" """Unties all parameters by setting tied_indices to an empty list."""
@ -74,7 +142,7 @@ class parameterised(object):
def all_constrained_indices(self): def all_constrained_indices(self):
"""Return a np array of all the constrained indices""" """Return a np array of all the constrained indices"""
ret = [np.hstack(i) for i in [self.constrained_bounded_indices, self.constrained_positive_indices, self.constrained_negative_indices, self.constrained_fixed_indices] if len(i)] ret = [np.hstack(i) for i in [self.constrained_bounded_indices, self.constrained_positive_indices, self.constrained_negative_indices, self.constrained_fixed_indices] if len(i)]
if len(ret): if len(ret):
return np.hstack(ret) return np.hstack(ret)
else: else:
@ -117,44 +185,44 @@ class parameterised(object):
which -- np.array(dtype=int), or regular expression object or string which -- np.array(dtype=int), or regular expression object or string
""" """
matches = self.grep_param_names(which) matches = self.grep_param_names(which)
assert not np.any(matches[:,None]==self.all_constrained_indices()), "Some indices are already constrained" assert not np.any(matches[:, None] == self.all_constrained_indices()), "Some indices are already constrained"
self.constrained_positive_indices = np.hstack((self.constrained_positive_indices, matches)) self.constrained_positive_indices = np.hstack((self.constrained_positive_indices, matches))
#check to ensure constraint is in place # check to ensure constraint is in place
x = self._get_params() x = self._get_params()
for i,xx in enumerate(x): for i, xx in enumerate(x):
if (xx<0) & (i in matches): if (xx < 0) & (i in matches):
x[i] = -xx x[i] = -xx
self._set_params(x) self._set_params(x)
def unconstrain(self,which): def unconstrain(self, which):
"""Unconstrain matching parameters. does not untie parameters""" """Unconstrain matching parameters. does not untie parameters"""
matches = self.grep_param_names(which) matches = self.grep_param_names(which)
#positive/negative # positive/negative
self.constrained_positive_indices = np.delete(self.constrained_positive_indices,np.nonzero(np.sum(self.constrained_positive_indices[:,None]==matches[None,:],1))[0]) self.constrained_positive_indices = np.delete(self.constrained_positive_indices, np.nonzero(np.sum(self.constrained_positive_indices[:, None] == matches[None, :], 1))[0])
self.constrained_negative_indices = np.delete(self.constrained_negative_indices,np.nonzero(np.sum(self.constrained_negative_indices[:,None]==matches[None,:],1))[0]) self.constrained_negative_indices = np.delete(self.constrained_negative_indices, np.nonzero(np.sum(self.constrained_negative_indices[:, None] == matches[None, :], 1))[0])
#bounded # bounded
if len(self.constrained_bounded_indices): if len(self.constrained_bounded_indices):
self.constrained_bounded_indices = [np.delete(a,np.nonzero(np.sum(a[:,None]==matches[None,:],1))[0]) for a in self.constrained_bounded_indices] self.constrained_bounded_indices = [np.delete(a, np.nonzero(np.sum(a[:, None] == matches[None, :], 1))[0]) for a in self.constrained_bounded_indices]
if np.hstack(self.constrained_bounded_indices).size: if np.hstack(self.constrained_bounded_indices).size:
self.constrained_bounded_uppers, self.constrained_bounded_lowers, self.constrained_bounded_indices = zip(*[(u,l,i) for u,l,i in zip(self.constrained_bounded_uppers, self.constrained_bounded_lowers, self.constrained_bounded_indices) if i.size]) self.constrained_bounded_uppers, self.constrained_bounded_lowers, self.constrained_bounded_indices = zip(*[(u, l, i) for u, l, i in zip(self.constrained_bounded_uppers, self.constrained_bounded_lowers, self.constrained_bounded_indices) if i.size])
self.constrained_bounded_uppers, self.constrained_bounded_lowers, self.constrained_bounded_indices = list(self.constrained_bounded_uppers), list(self.constrained_bounded_lowers), list(self.constrained_bounded_indices) self.constrained_bounded_uppers, self.constrained_bounded_lowers, self.constrained_bounded_indices = list(self.constrained_bounded_uppers), list(self.constrained_bounded_lowers), list(self.constrained_bounded_indices)
else: else:
self.constrained_bounded_uppers, self.constrained_bounded_lowers, self.constrained_bounded_indices = [],[],[] self.constrained_bounded_uppers, self.constrained_bounded_lowers, self.constrained_bounded_indices = [], [], []
#fixed: # fixed:
for i,indices in enumerate(self.constrained_fixed_indices): for i, indices in enumerate(self.constrained_fixed_indices):
self.constrained_fixed_indices[i] = np.delete(indices,np.nonzero(np.sum(indices[:,None]==matches[None,:],1))[0]) self.constrained_fixed_indices[i] = np.delete(indices, np.nonzero(np.sum(indices[:, None] == matches[None, :], 1))[0])
#remove empty elements # remove empty elements
tmp = [(i,v) for i,v in zip(self.constrained_fixed_indices, self.constrained_fixed_values) if len(i)] tmp = [(i, v) for i, v in zip(self.constrained_fixed_indices, self.constrained_fixed_values) if len(i)]
if tmp: if tmp:
self.constrained_fixed_indices, self.constrained_fixed_values = zip(*tmp) self.constrained_fixed_indices, self.constrained_fixed_values = zip(*tmp)
self.constrained_fixed_indices, self.constrained_fixed_values = list(self.constrained_fixed_indices), list(self.constrained_fixed_values) self.constrained_fixed_indices, self.constrained_fixed_values = list(self.constrained_fixed_indices), list(self.constrained_fixed_values)
else: else:
self.constrained_fixed_indices, self.constrained_fixed_values = [],[] self.constrained_fixed_indices, self.constrained_fixed_values = [], []
def constrain_negative(self,which): def constrain_negative(self, which):
""" """
Set negative constraints. Set negative constraints.
@ -163,12 +231,12 @@ class parameterised(object):
""" """
matches = self.grep_param_names(which) matches = self.grep_param_names(which)
assert not np.any(matches[:,None]==self.all_constrained_indices()), "Some indices are already constrained" assert not np.any(matches[:, None] == self.all_constrained_indices()), "Some indices are already constrained"
self.constrained_negative_indices = np.hstack((self.constrained_negative_indices, matches)) self.constrained_negative_indices = np.hstack((self.constrained_negative_indices, matches))
#check to ensure constraint is in place # check to ensure constraint is in place
x = self._get_params() x = self._get_params()
for i,xx in enumerate(x): for i, xx in enumerate(x):
if (xx>0.) and (i in matches): if (xx > 0.) and (i in matches):
x[i] = -xx x[i] = -xx
self._set_params(x) self._set_params(x)
@ -184,20 +252,20 @@ class parameterised(object):
lower -- (float) the lower bound on the constraint lower -- (float) the lower bound on the constraint
""" """
matches = self.grep_param_names(which) matches = self.grep_param_names(which)
assert not np.any(matches[:,None]==self.all_constrained_indices()), "Some indices are already constrained" assert not np.any(matches[:, None] == self.all_constrained_indices()), "Some indices are already constrained"
assert lower < upper, "lower bound must be smaller than upper bound!" assert lower < upper, "lower bound must be smaller than upper bound!"
self.constrained_bounded_indices.append(matches) self.constrained_bounded_indices.append(matches)
self.constrained_bounded_uppers.append(upper) self.constrained_bounded_uppers.append(upper)
self.constrained_bounded_lowers.append(lower) self.constrained_bounded_lowers.append(lower)
#check to ensure constraint is in place # check to ensure constraint is in place
x = self._get_params() x = self._get_params()
for i,xx in enumerate(x): for i, xx in enumerate(x):
if ((xx<=lower)|(xx>=upper)) & (i in matches): if ((xx <= lower) | (xx >= upper)) & (i in matches):
x[i] = sigmoid(xx)*(upper-lower) + lower x[i] = sigmoid(xx) * (upper - lower) + lower
self._set_params(x) self._set_params(x)
def constrain_fixed(self, which, value = None): def constrain_fixed(self, which, value=None):
""" """
Arguments Arguments
--------- ---------
@ -211,14 +279,14 @@ class parameterised(object):
To fix multiple parameters to the same value, simply pass a regular expression which matches both parameter names, or pass both of the indexes To fix multiple parameters to the same value, simply pass a regular expression which matches both parameter names, or pass both of the indexes
""" """
matches = self.grep_param_names(which) matches = self.grep_param_names(which)
assert not np.any(matches[:,None]==self.all_constrained_indices()), "Some indices are already constrained" assert not np.any(matches[:, None] == self.all_constrained_indices()), "Some indices are already constrained"
self.constrained_fixed_indices.append(matches) self.constrained_fixed_indices.append(matches)
if value != None: if value != None:
self.constrained_fixed_values.append(value) self.constrained_fixed_values.append(value)
else: else:
self.constrained_fixed_values.append(self._get_params()[self.constrained_fixed_indices[-1]]) self.constrained_fixed_values.append(self._get_params()[self.constrained_fixed_indices[-1]])
#self.constrained_fixed_values.append(value) # self.constrained_fixed_values.append(value)
self._set_params_transformed(self._get_params_transformed()) self._set_params_transformed(self._get_params_transformed())
def _get_params_transformed(self): def _get_params_transformed(self):
@ -226,40 +294,40 @@ class parameterised(object):
x = self._get_params() x = self._get_params()
x[self.constrained_positive_indices] = np.log(x[self.constrained_positive_indices]) x[self.constrained_positive_indices] = np.log(x[self.constrained_positive_indices])
x[self.constrained_negative_indices] = np.log(-x[self.constrained_negative_indices]) x[self.constrained_negative_indices] = np.log(-x[self.constrained_negative_indices])
[np.put(x,i,np.log(np.clip(x[i]-l,1e-10,np.inf)/np.clip(h-x[i],1e-10,np.inf))) for i,l,h in zip(self.constrained_bounded_indices, self.constrained_bounded_lowers, self.constrained_bounded_uppers)] [np.put(x, i, np.log(np.clip(x[i] - l, 1e-10, np.inf) / np.clip(h - x[i], 1e-10, np.inf))) for i, l, h in zip(self.constrained_bounded_indices, self.constrained_bounded_lowers, self.constrained_bounded_uppers)]
to_remove = self.constrained_fixed_indices+[t[1:] for t in self.tied_indices] to_remove = self.constrained_fixed_indices + [t[1:] for t in self.tied_indices]
if len(to_remove): if len(to_remove):
return np.delete(x,np.hstack(to_remove)) return np.delete(x, np.hstack(to_remove))
else: else:
return x return x
def _set_params_transformed(self,x): def _set_params_transformed(self, x):
""" takes the vector x, which is then modified (by untying, reparameterising or inserting fixed values), and then call self._set_params""" """ takes the vector x, which is then modified (by untying, reparameterising or inserting fixed values), and then call self._set_params"""
#work out how many places are fixed, and where they are. tricky logic! # work out how many places are fixed, and where they are. tricky logic!
Nfix_places = 0. Nfix_places = 0.
if len(self.tied_indices): if len(self.tied_indices):
Nfix_places += np.hstack(self.tied_indices).size-len(self.tied_indices) Nfix_places += np.hstack(self.tied_indices).size - len(self.tied_indices)
if len(self.constrained_fixed_indices): if len(self.constrained_fixed_indices):
Nfix_places += np.hstack(self.constrained_fixed_indices).size Nfix_places += np.hstack(self.constrained_fixed_indices).size
if Nfix_places: if Nfix_places:
fix_places = np.hstack(self.constrained_fixed_indices+[t[1:] for t in self.tied_indices]) fix_places = np.hstack(self.constrained_fixed_indices + [t[1:] for t in self.tied_indices])
else: else:
fix_places = [] fix_places = []
free_places = np.setdiff1d(np.arange(Nfix_places+x.size,dtype=np.int),fix_places) free_places = np.setdiff1d(np.arange(Nfix_places + x.size, dtype=np.int), fix_places)
#put the models values in the vector xx # put the models values in the vector xx
xx = np.zeros(Nfix_places+free_places.size,dtype=np.float64) xx = np.zeros(Nfix_places + free_places.size, dtype=np.float64)
xx[free_places] = x xx[free_places] = x
[np.put(xx,i,v) for i,v in zip(self.constrained_fixed_indices, self.constrained_fixed_values)] [np.put(xx, i, v) for i, v in zip(self.constrained_fixed_indices, self.constrained_fixed_values)]
[np.put(xx,i,v) for i,v in [(t[1:],xx[t[0]]) for t in self.tied_indices] ] [np.put(xx, i, v) for i, v in [(t[1:], xx[t[0]]) for t in self.tied_indices] ]
xx[self.constrained_positive_indices] = np.exp(xx[self.constrained_positive_indices]) xx[self.constrained_positive_indices] = np.exp(xx[self.constrained_positive_indices])
xx[self.constrained_negative_indices] = -np.exp(xx[self.constrained_negative_indices]) xx[self.constrained_negative_indices] = -np.exp(xx[self.constrained_negative_indices])
[np.put(xx,i,low+sigmoid(xx[i])*(high-low)) for i,low,high in zip(self.constrained_bounded_indices, self.constrained_bounded_lowers, self.constrained_bounded_uppers)] [np.put(xx, i, low + sigmoid(xx[i]) * (high - low)) for i, low, high in zip(self.constrained_bounded_indices, self.constrained_bounded_lowers, self.constrained_bounded_uppers)]
self._set_params(xx) self._set_params(xx)
def _get_param_names_transformed(self): def _get_param_names_transformed(self):
@ -267,33 +335,33 @@ class parameterised(object):
Returns the parameter names as propagated after constraining, Returns the parameter names as propagated after constraining,
tying or fixing, i.e. a list of the same length as _get_params_transformed() tying or fixing, i.e. a list of the same length as _get_params_transformed()
""" """
n = self._get_param_names() n = self._get_param_names()
#remove/concatenate the tied parameter names # remove/concatenate the tied parameter names
if len(self.tied_indices): if len(self.tied_indices):
for t in self.tied_indices: for t in self.tied_indices:
n[t[0]] = "<tie>".join([n[tt] for tt in t]) n[t[0]] = "<tie>".join([n[tt] for tt in t])
remove = np.hstack([t[1:] for t in self.tied_indices]) remove = np.hstack([t[1:] for t in self.tied_indices])
else: else:
remove=np.empty(shape=(0,),dtype=np.int) remove = np.empty(shape=(0,), dtype=np.int)
#also remove the fixed params # also remove the fixed params
if len(self.constrained_fixed_indices): if len(self.constrained_fixed_indices):
remove = np.hstack((remove, np.hstack(self.constrained_fixed_indices))) remove = np.hstack((remove, np.hstack(self.constrained_fixed_indices)))
#add markers to show that some variables are constrained # add markers to show that some variables are constrained
for i in self.constrained_positive_indices: for i in self.constrained_positive_indices:
n[i] = n[i]+'(+ve)' n[i] = n[i] + '(+ve)'
for i in self.constrained_negative_indices: for i in self.constrained_negative_indices:
n[i] = n[i]+'(-ve)' n[i] = n[i] + '(-ve)'
for i,l,h in zip(self.constrained_bounded_indices, self.constrained_bounded_lowers, self.constrained_bounded_uppers): for i, l, h in zip(self.constrained_bounded_indices, self.constrained_bounded_lowers, self.constrained_bounded_uppers):
for ii in i: for ii in i:
n[ii] = n[ii]+'(bounded)' n[ii] = n[ii] + '(bounded)'
n = [nn for i,nn in enumerate(n) if not i in remove] n = [nn for i, nn in enumerate(n) if not i in remove]
return n return n
def __str__(self,nw=30): def __str__(self, nw=30):
""" """
Return a string describing the parameter names and their ties and constraints Return a string describing the parameter names and their ties and constraints
""" """
@ -302,10 +370,10 @@ class parameterised(object):
if not N: if not N:
return "This object has no free parameters." return "This object has no free parameters."
header = ['Name','Value','Constraints','Ties'] header = ['Name', 'Value', 'Constraints', 'Ties']
values = self._get_params() #map(str,self._get_params()) values = self._get_params() # map(str,self._get_params())
#sort out the constraints # sort out the constraints
constraints = ['']*len(names) constraints = [''] * len(names)
for i in self.constrained_positive_indices: for i in self.constrained_positive_indices:
constraints[i] = '(+ve)' constraints[i] = '(+ve)'
for i in self.constrained_negative_indices: for i in self.constrained_negative_indices:
@ -313,14 +381,14 @@ class parameterised(object):
for i in self.constrained_fixed_indices: for i in self.constrained_fixed_indices:
for ii in i: for ii in i:
constraints[ii] = 'Fixed' constraints[ii] = 'Fixed'
for i,u,l in zip(self.constrained_bounded_indices, self.constrained_bounded_uppers, self.constrained_bounded_lowers): for i, u, l in zip(self.constrained_bounded_indices, self.constrained_bounded_uppers, self.constrained_bounded_lowers):
for ii in i: for ii in i:
constraints[ii] = '('+str(l)+', '+str(u)+')' constraints[ii] = '(' + str(l) + ', ' + str(u) + ')'
#sort out the ties # sort out the ties
ties = ['']*len(names) ties = [''] * len(names)
for i,tie in enumerate(self.tied_indices): for i, tie in enumerate(self.tied_indices):
for j in tie: for j in tie:
ties[j] = '('+str(i)+')' ties[j] = '(' + str(i) + ')'
values = ['%.4f' % float(v) for v in values] values = ['%.4f' % float(v) for v in values]
max_names = max([len(names[i]) for i in range(len(names))] + [len(header[0])]) max_names = max([len(names[i]) for i in range(len(names))] + [len(header[0])])
@ -330,10 +398,10 @@ class parameterised(object):
cols = np.array([max_names, max_values, max_constraint, max_ties]) + 4 cols = np.array([max_names, max_values, max_constraint, max_ties]) + 4
columns = cols.sum() columns = cols.sum()
header_string = ["{h:^{col}}".format(h = header[i], col = cols[i]) for i in range(len(cols))] header_string = ["{h:^{col}}".format(h=header[i], col=cols[i]) for i in range(len(cols))]
header_string = map(lambda x: '|'.join(x), [header_string]) header_string = map(lambda x: '|'.join(x), [header_string])
separator = '-'*len(header_string[0]) separator = '-' * len(header_string[0])
param_string = ["{n:^{c0}}|{v:^{c1}}|{c:^{c2}}|{t:^{c3}}".format(n = names[i], v = values[i], c = constraints[i], t = ties[i], c0 = cols[0], c1 = cols[1], c2 = cols[2], c3 = cols[3]) for i in range(len(values))] param_string = ["{n:^{c0}}|{v:^{c1}}|{c:^{c2}}|{t:^{c3}}".format(n=names[i], v=values[i], c=constraints[i], t=ties[i], c0=cols[0], c1=cols[1], c2=cols[2], c3=cols[3]) for i in range(len(values))]
return ('\n'.join([header_string[0], separator]+param_string)) + '\n' return ('\n'.join([header_string[0], separator] + param_string)) + '\n'

View file

@ -112,14 +112,14 @@ def _simulate_sincos(D1, D2, D3, N, M, Q, plot_sim=False):
s3 = s3(x) s3 = s3(x)
sS = sS(x) sS = sS(x)
s1 -= s1.mean() # s1 -= s1.mean()
s2 -= s2.mean() # s2 -= s2.mean()
s3 -= s3.mean() # s3 -= s3.mean()
sS -= sS.mean() # sS -= sS.mean()
s1 /= .5 * (np.abs(s1).max() - np.abs(s1).min()) # s1 /= .5 * (np.abs(s1).max() - np.abs(s1).min())
s2 /= .5 * (np.abs(s2).max() - np.abs(s2).min()) # s2 /= .5 * (np.abs(s2).max() - np.abs(s2).min())
s3 /= .5 * (np.abs(s3).max() - np.abs(s3).min()) # s3 /= .5 * (np.abs(s3).max() - np.abs(s3).min())
sS /= .5 * (np.abs(sS).max() - np.abs(sS).min()) # sS /= .5 * (np.abs(sS).max() - np.abs(sS).min())
S1 = np.hstack([s1, sS]) S1 = np.hstack([s1, sS])
S2 = np.hstack([s2, sS]) S2 = np.hstack([s2, sS])
@ -129,9 +129,9 @@ def _simulate_sincos(D1, D2, D3, N, M, Q, plot_sim=False):
Y2 = S2.dot(np.random.randn(S2.shape[1], D2)) Y2 = S2.dot(np.random.randn(S2.shape[1], D2))
Y3 = S3.dot(np.random.randn(S3.shape[1], D3)) Y3 = S3.dot(np.random.randn(S3.shape[1], D3))
Y1 += .5 * np.random.randn(*Y1.shape) Y1 += .3 * np.random.randn(*Y1.shape)
Y2 += .5 * np.random.randn(*Y2.shape) Y2 += .3 * np.random.randn(*Y2.shape)
Y3 += .5 * np.random.randn(*Y3.shape) Y3 += .3 * np.random.randn(*Y3.shape)
Y1 -= Y1.mean(0) Y1 -= Y1.mean(0)
Y2 -= Y2.mean(0) Y2 -= Y2.mean(0)
@ -162,8 +162,11 @@ def _simulate_sincos(D1, D2, D3, N, M, Q, plot_sim=False):
return slist, [S1, S2, S3], Ylist return slist, [S1, S2, S3], Ylist
def bgplvm_simulation(burnin='scg', plot_sim=False, max_f_eval=12): def bgplvm_simulation(burnin='scg', plot_sim=False,
D1, D2, D3, N, M, Q = 2000, 8, 8, 500, 2, 6 max_burnin=100, true_X=False,
do_opt=True,
max_f_eval=1000):
D1, D2, D3, N, M, Q = 10, 8, 8, 50, 30, 5
slist, Slist, Ylist = _simulate_sincos(D1, D2, D3, N, M, Q, plot_sim) slist, Slist, Ylist = _simulate_sincos(D1, D2, D3, N, M, Q, plot_sim)
from GPy.models import mrd from GPy.models import mrd
@ -171,53 +174,73 @@ def bgplvm_simulation(burnin='scg', plot_sim=False, max_f_eval=12):
reload(mrd); reload(kern) reload(mrd); reload(kern)
Y = Ylist[1] Y = Ylist[0]
k = kern.linear(Q, ARD=True) + kern.white(Q, .00001) # + kern.bias(Q) k = kern.linear(Q, ARD=True) + kern.white(Q, .00001) # + kern.bias(Q)
m = Bayesian_GPLVM(Y, Q, init="PCA", M=M, kernel=k) # k = kern.white(Q, .00001) + kern.bias(Q)
m = Bayesian_GPLVM(Y, Q, init="PCA", M=M, kernel=k, _debug=True)
# m.set('noise',) # m.set('noise',)
m.ensure_default_constraints()
# m.auto_scale_factor = True # m.auto_scale_factor = True
# m.scale_factor = 1. # m.scale_factor = 1.
m.ensure_default_constraints()
if burnin: if burnin:
print "initializing beta" print "initializing beta"
cstr = "noise" cstr = "noise"
m.unconstrain(cstr); m.constrain_fixed(cstr, Y.var() / 100.) m.unconstrain(cstr); m.constrain_fixed(cstr, Y.var() / 70.)
m.optimize(burnin, messages=1, max_f_eval=max_f_eval) m.optimize(burnin, messages=1, max_f_eval=max_burnin)
print "releasing beta" print "releasing beta"
cstr = "noise" cstr = "noise"
m.unconstrain(cstr); m.constrain_positive(cstr) m.unconstrain(cstr); m.constrain_positive(cstr)
true_X = np.hstack((slist[1], slist[3], 0. * np.ones((N, Q - 2)))) if true_X:
m.set('X_\d', true_X) true_X = np.hstack((slist[0], slist[3], 0. * np.ones((N, Q - 2))))
m.constrain_fixed("X_\d") m.set('X_\d', true_X)
m.constrain_fixed("X_\d")
# # cstr = 'variance' cstr = 'X_variance'
# # m.unconstrain(cstr), m.constrain_bounded(cstr, 1e-10, 1.) # m.unconstrain(cstr), m.constrain_fixed(cstr, .0001)
m.unconstrain(cstr), m.constrain_bounded(cstr, 1e-7, .1)
# cstr = 'X_variance'
# m.unconstrain(cstr), m.constrain_bounded(cstr, 1e-3, 1.)
m['X_var'] = np.ones(N * Q) * .5 + np.random.randn(N * Q) * .01
# cstr = "iip"
# m.unconstrain(cstr); m.constrain_fixed(cstr)
# cstr = 'variance'
# m.unconstrain(cstr), m.constrain_bounded(cstr, 1e-10, 1.)
# cstr = 'X_\d' # cstr = 'X_\d'
# m.unconstrain(cstr), m.constrain_bounded(cstr, -100., 100.) # m.unconstrain(cstr), m.constrain_bounded(cstr, -10., 10.)
# #
# cstr = 'noise' # cstr = 'noise'
# m.unconstrain(cstr), m.constrain_bounded(cstr, 1e-3, 1.) # m.unconstrain(cstr), m.constrain_bounded(cstr, 1e-5, 1.)
# #
# cstr = 'white' # cstr = 'white'
# m.unconstrain(cstr), m.constrain_bounded(cstr, 1e-6, 1.) # m.unconstrain(cstr), m.constrain_bounded(cstr, 1e-6, 1.)
# #
# cstr = 'linear_variance' # cstr = 'linear_variance'
# m.unconstrain(cstr), m.constrain_bounded(cstr, 1e-10, 10.) # m.constrain_positive(cstr) # m.unconstrain(cstr), m.constrain_bounded(cstr, 1e-10, 10.)
#
# cstr = 'X_variance' # cstr = 'variance'
# m.unconstrain(cstr), m.constrain_bounded(cstr, 1e-10, 1.) # m.constrain_positive(cstr) # m.unconstrain(cstr), m.constrain_bounded(cstr, 1e-10, 10.)
# np.seterr(all='call') # np.seterr(all='call')
# def ipdbonerr(errtype, flags): # def ipdbonerr(errtype, flags):
# import ipdb; ipdb.set_trace() # import ipdb; ipdb.set_trace()
# np.seterrcall(ipdbonerr) # np.seterrcall(ipdbonerr)
if do_opt and burnin:
try:
m.optimize(burnin, messages=1, max_f_eval=max_f_eval)
except:
pass
finally:
return m
return m return m
def mrd_simulation(plot_sim=False): def mrd_simulation(plot_sim=False):
@ -261,6 +284,7 @@ def mrd_simulation(plot_sim=False):
m.set('{}_noise'.format(i + 1), Y.var() / 100.) m.set('{}_noise'.format(i + 1), Y.var() / 100.)
m.ensure_default_constraints() m.ensure_default_constraints()
m.auto_scale_factor = True
# cstr = 'variance' # cstr = 'variance'
# m.unconstrain(cstr), m.constrain_bounded(cstr, 1e-12, 1.) # m.unconstrain(cstr), m.constrain_bounded(cstr, 1e-12, 1.)

View file

@ -0,0 +1,146 @@
#Copyright I. Nabney, N.Lawrence and James Hensman (1996 - 2012)
#Scaled Conjuagte Gradients, originally in Matlab as part of the Netlab toolbox by I. Nabney, converted to python N. Lawrence and given a pythonic interface by James Hensman
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
# HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
# NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
import numpy as np
import sys
def SCG(f, gradf, x, optargs=(), maxiters=500, max_f_eval=500, display=True, xtol=1e-6, ftol=1e-6):
"""
Optimisation through Scaled Conjugate Gradients (SCG)
f: the objective function
gradf : the gradient function (should return a 1D np.ndarray)
x : the initial condition
Returns
x the optimal value for x
flog : a list of all the objective values
"""
sigma0 = 1.0e-4
fold = f(x, *optargs) # Initial function value.
function_eval = 1
fnow = fold
gradnew = gradf(x, *optargs) # Initial gradient.
gradold = gradnew.copy()
d = -gradnew # Initial search direction.
success = True # Force calculation of directional derivs.
nsuccess = 0 # nsuccess counts number of successes.
beta = 1.0 # Initial scale parameter.
betamin = 1.0e-15 # Lower bound on scale.
betamax = 1.0e100 # Upper bound on scale.
status = "Not converged"
flog = [fold]
iteration = 0
# Main optimization loop.
while iteration < maxiters:
# Calculate first and second directional derivatives.
if success:
mu = np.dot(d, gradnew)
if mu >= 0:
d = -gradnew
mu = np.dot(d, gradnew)
kappa = np.dot(d, d)
sigma = sigma0/np.sqrt(kappa)
xplus = x + sigma*d
gplus = gradf(xplus, *optargs)
theta = np.dot(d, (gplus - gradnew))/sigma
# Increase effective curvature and evaluate step size alpha.
delta = theta + beta*kappa
if delta <= 0:
delta = beta*kappa
beta = beta - theta/kappa
alpha = - mu/delta
# Calculate the comparison ratio.
xnew = x + alpha*d
fnew = f(xnew, *optargs)
function_eval += 1
if function_eval >= max_f_eval:
status = "Maximum number of function evaluations exceeded"
return x, flog, function_eval, status
Delta = 2.*(fnew - fold)/(alpha*mu)
if Delta >= 0.:
success = True
nsuccess += 1
x = xnew
fnow = fnew
else:
success = False
fnow = fold
# Store relevant variables
flog.append(fnow) # Current function value
iteration += 1
if display:
print '\r',
print 'Iteration: {0:>5g} Objective:{1:> 12e} Scale:{2:> 12e}'.format(iteration, fnow, beta),
# print 'Iteration:', iteration, ' Objective:', fnow, ' Scale:', beta, '\r',
sys.stdout.flush()
if success:
# Test for termination
if (np.max(np.abs(alpha*d)) < xtol) or (np.abs(fnew-fold) < ftol):
status='converged'
return x, flog, function_eval, status
else:
# Update variables for new position
fold = fnew
gradold = gradnew
gradnew = gradf(x, *optargs)
# If the gradient is zero then we are done.
if np.dot(gradnew,gradnew) == 0:
return x, flog, function_eval, status
# Adjust beta according to comparison ratio.
if Delta < 0.25:
beta = min(4.0*beta, betamax)
if Delta > 0.75:
beta = max(0.5*beta, betamin)
# Update search direction using Polak-Ribiere formula, or re-start
# in direction of negative gradient after nparams steps.
if nsuccess == x.size:
d = -gradnew
nsuccess = 0
elif success:
gamma = np.dot(gradold - gradnew,gradnew)/(mu)
d = gamma*d - gradnew
# If we get here, then we haven't terminated in the given number of
# iterations.
status = "maxiter exceeded"
return x, flog, function_eval, status

View file

@ -70,8 +70,8 @@ class kern(parameterised):
ard_params = 1. / p.lengthscale ard_params = 1. / p.lengthscale
ax.bar(np.arange(len(ard_params)) - 0.4, ard_params) ax.bar(np.arange(len(ard_params)) - 0.4, ard_params)
ax.set_xticks(np.arange(len(ard_params)), ax.set_xticks(np.arange(len(ard_params)))
["${}$".format(i + 1) for i in range(len(ard_params))]) ax.set_xticklabels([r"${}$".format(i + 1) for i in range(len(ard_params))])
return ax return ax
def _transform_gradients(self, g): def _transform_gradients(self, g):

View file

@ -10,6 +10,7 @@ from GPy.util.linalg import pdinv
from ..likelihoods import Gaussian from ..likelihoods import Gaussian
from .. import kern from .. import kern
from numpy.linalg.linalg import LinAlgError from numpy.linalg.linalg import LinAlgError
import itertools
class Bayesian_GPLVM(sparse_GP, GPLVM): class Bayesian_GPLVM(sparse_GP, GPLVM):
""" """
@ -23,7 +24,9 @@ class Bayesian_GPLVM(sparse_GP, GPLVM):
:type init: 'PCA'|'random' :type init: 'PCA'|'random'
""" """
def __init__(self, Y, Q, X=None, X_variance=None, init='PCA', M=10, Z=None, kernel=None, oldpsave=5, **kwargs): def __init__(self, Y, Q, X=None, X_variance=None, init='PCA', M=10,
Z=None, kernel=None, oldpsave=5, _debug=False,
**kwargs):
if X == None: if X == None:
X = self.initialise_latent(init, Q, Y) X = self.initialise_latent(init, Q, Y)
@ -39,6 +42,12 @@ class Bayesian_GPLVM(sparse_GP, GPLVM):
self.oldpsave = oldpsave self.oldpsave = oldpsave
self._oldps = [] self._oldps = []
self._debug = _debug
if self._debug:
self._count = itertools.count()
self._savedklll = []
self._savedparams = []
sparse_GP.__init__(self, X, Gaussian(Y), kernel, Z=Z, X_variance=X_variance, **kwargs) sparse_GP.__init__(self, X, Gaussian(Y), kernel, Z=Z, X_variance=X_variance, **kwargs)
@ -70,16 +79,18 @@ class Bayesian_GPLVM(sparse_GP, GPLVM):
x = np.hstack((self.X.flatten(), self.X_variance.flatten(), sparse_GP._get_params(self))) x = np.hstack((self.X.flatten(), self.X_variance.flatten(), sparse_GP._get_params(self)))
return x return x
def _set_params(self, x, save_old=True): def _set_params(self, x, save_old=True, save_count=0):
try: try:
N, Q = self.N, self.Q N, Q = self.N, self.Q
self.X = x[:self.X.size].reshape(N, Q).copy() self.X = x[:self.X.size].reshape(N, Q).copy()
self.X_variance = x[(N * Q):(2 * N * Q)].reshape(N, Q).copy() self.X_variance = x[(N * Q):(2 * N * Q)].reshape(N, Q).copy()
sparse_GP._set_params(self, x[(2 * N * Q):]) sparse_GP._set_params(self, x[(2 * N * Q):])
self.oldps = x self.oldps = x
except (LinAlgError, FloatingPointError): except (LinAlgError, FloatingPointError, ZeroDivisionError):
print "\rWARNING: Caught LinAlgError, reconstructing old state " print "\rWARNING: Caught LinAlgError, continueing without setting "
self._set_params(self.oldps[-1], save_old=False) # if save_count > 10:
# raise
# self._set_params(self.oldps[-1], save_old=False, save_count=save_count + 1)
def dKL_dmuS(self): def dKL_dmuS(self):
dKL_dS = (1. - (1. / (self.X_variance))) * 0.5 dKL_dS = (1. - (1. / (self.X_variance))) * 0.5
@ -103,15 +114,29 @@ class Bayesian_GPLVM(sparse_GP, GPLVM):
def log_likelihood(self): def log_likelihood(self):
ll = sparse_GP.log_likelihood(self) ll = sparse_GP.log_likelihood(self)
kl = self.KL_divergence() kl = self.KL_divergence()
return ll + kl
# if ll < -2E4:
# ll = -2E4 + np.random.randn()
# if kl > 5E4:
# kl = 5E4 + np.random.randn()
if self._debug:
f_call = self._count.next()
self._savedklll.append([f_call, ll, kl])
if f_call % 1 == 0:
self._savedparams.append([f_call, self._get_params()])
# print "\nkl:", kl, "ll:", ll
return ll - kl
def _log_likelihood_gradients(self): def _log_likelihood_gradients(self):
dKL_dmu, dKL_dS = self.dKL_dmuS() dKL_dmu, dKL_dS = self.dKL_dmuS()
dL_dmu, dL_dS = self.dL_dmuS() dL_dmu, dL_dS = self.dL_dmuS()
# TODO: find way to make faster # TODO: find way to make faster
d_dmu = (dL_dmu + dKL_dmu).flatten() d_dmu = (dL_dmu - dKL_dmu).flatten()
d_dS = (dL_dS + dKL_dS).flatten() d_dS = (dL_dS - dKL_dS).flatten()
# TEST KL: ==================== # TEST KL: ====================
# d_dmu = (dKL_dmu).flatten() # d_dmu = (dKL_dmu).flatten()
# d_dS = (dKL_dS).flatten() # d_dS = (dKL_dS).flatten()
@ -135,3 +160,140 @@ class Bayesian_GPLVM(sparse_GP, GPLVM):
ax = GPLVM.plot_latent(self, which_indices=[input_1, input_2], *args, **kwargs) ax = GPLVM.plot_latent(self, which_indices=[input_1, input_2], *args, **kwargs)
ax.plot(self.Z[:, input_1], self.Z[:, input_2], '^w') ax.plot(self.Z[:, input_1], self.Z[:, input_2], '^w')
return ax return ax
def plot_X_1d(self, fig_num="MRD X 1d", axes=None, colors=None):
import pylab
fig = pylab.figure(num=fig_num, figsize=(min(8, (3 * len(self.bgplvms))), min(12, (2 * self.X.shape[1]))))
if colors is None:
colors = pylab.gca()._get_lines.color_cycle
pylab.clf()
plots = []
for i in range(self.X.shape[1]):
if axes is None:
ax = fig.add_subplot(self.X.shape[1], 1, i + 1)
else:
ax = axes[i]
ax.plot(self.X, c='k', alpha=.3)
plots.extend(ax.plot(self.X.T[i], c=colors.next(), label=r"$\mathbf{{X_{}}}$".format(i)))
ax.fill_between(np.arange(self.X.shape[0]),
self.X.T[i] - 2 * np.sqrt(self.X_variance.T[i]),
self.X.T[i] + 2 * np.sqrt(self.X_variance.T[i]),
facecolor=plots[-1].get_color(),
alpha=.3)
ax.legend(borderaxespad=0.)
if i < self.X.shape[1] - 1:
ax.set_xticklabels('')
pylab.draw()
fig.tight_layout(h_pad=.01) # , rect=(0, 0, 1, .95))
return fig
def _debug_filter_params(self, x):
start, end = 0, self.X.size,
X = x[start:end].reshape(self.N, self.Q)
start, end = end, end + self.X_variance.size
X_v = x[start:end].reshape(self.N, self.Q)
start, end = end, end + (self.M * self.Q)
Z = x[start:end].reshape(self.M, self.Q)
start, end = end, end + self.Q
theta = x[start:]
return X, X_v, Z, theta
def _debug_plot(self):
assert self._debug, "must enable _debug, to debug-plot"
import pylab
from mpl_toolkits.mplot3d import Axes3D
fig = pylab.figure('BGPLVM DEBUG', figsize=(12, 10))
fig.clf()
# log like
splotshape = (6, 4)
ax1 = pylab.subplot2grid(splotshape, (0, 0), 1, 4)
ax1.text(.5, .5, "Optimization", alpha=.3, transform=ax1.transAxes,
ha='center', va='center')
kllls = np.array(self._savedklll)
LL, = ax1.plot(kllls[:, 0], kllls[:, 1] - kllls[:, 2], label=r'$\log p(\mathbf{Y})$', mew=1.5)
KL, = ax1.plot(kllls[:, 0], kllls[:, 2], label=r'$\mathcal{KL}(p||q)$', mew=1.5)
L, = ax1.plot(kllls[:, 0], kllls[:, 1], label=r'$L$', mew=1.5) # \mathds{E}_{q(\mathbf{X})}[p(\mathbf{Y|X})\frac{p(\mathbf{X})}{q(\mathbf{X})}]
drawn = dict(self._savedparams)
iters = np.array(drawn.keys())
self.showing = 0
ax2 = pylab.subplot2grid(splotshape, (1, 0), 2, 4)
ax2.text(.5, .5, r"$\mathbf{X}$", alpha=.5, transform=ax2.transAxes,
ha='center', va='center')
ax3 = pylab.subplot2grid(splotshape, (3, 0), 2, 4, sharex=ax2)
ax3.text(.5, .5, r"$\mathbf{S}$", alpha=.5, transform=ax3.transAxes,
ha='center', va='center')
ax4 = pylab.subplot2grid(splotshape, (5, 0), 2, 2)
ax4.text(.5, .5, r"$\mathbf{Z}$", alpha=.5, transform=ax4.transAxes,
ha='center', va='center')
ax5 = pylab.subplot2grid(splotshape, (5, 2), 2, 2)
ax5.text(.5, .5, r"${\theta}$", alpha=.5, transform=ax5.transAxes,
ha='center', va='center')
X, S, Z, theta = self._debug_filter_params(drawn[self.showing])
Xlatentplts = ax2.plot(X, ls="-", marker="x")
Slatentplts = ax3.plot(S, ls="-", marker="x")
Zplts = ax4.plot(Z, ls="-", marker="x")
thetaplts = ax5.bar(np.arange(len(theta)) - .4, theta)
ax5.set_xticks(np.arange(len(theta)))
ax5.set_xticklabels(self._get_param_names()[-len(theta):], rotation=17)
Qleg = ax1.legend(Xlatentplts, [r"$Q_{}$".format(i + 1) for i in range(self.Q)],
loc=3, ncol=self.Q, bbox_to_anchor=(0, 1.15, 1, 1.15),
borderaxespad=0, mode="expand")
Lleg = ax1.legend()
Lleg.draggable()
ax1.add_artist(Qleg)
indicatorKL, = ax1.plot(kllls[self.showing, 0], kllls[self.showing, 2], 'o', c=KL.get_color())
indicatorLL, = ax1.plot(kllls[self.showing, 0], kllls[self.showing, 1] - kllls[self.showing, 2], 'o', c=LL.get_color())
indicatorL, = ax1.plot(kllls[self.showing, 0], kllls[self.showing, 1], 'o', c=L.get_color())
try:
pylab.draw()
pylab.tight_layout(box=(0, .1, 1, .9))
except:
pass
# parameter changes
# ax2 = pylab.subplot2grid((4, 1), (1, 0), 3, 1, projection='3d')
def onclick(event):
if event.inaxes is ax1 and event.button == 1:
# event.button, event.x, event.y, event.xdata, event.ydata)
tmp = np.abs(iters - event.xdata)
closest_hit = iters[tmp == tmp.min()][0]
if closest_hit != self.showing:
self.showing = closest_hit
# print closest_hit, iters, event.xdata
indicatorLL.set_data(self.showing, kllls[self.showing, 1] - kllls[self.showing, 2])
indicatorKL.set_data(self.showing, kllls[self.showing, 2])
indicatorL.set_data(self.showing, kllls[self.showing, 1])
X, S, Z, theta = self._debug_filter_params(drawn[self.showing])
for i, Xlatent in enumerate(Xlatentplts):
Xlatent.set_ydata(X[:, i])
for i, Slatent in enumerate(Slatentplts):
Slatent.set_ydata(S[:, i])
for i, Zlatent in enumerate(Zplts):
Zlatent.set_ydata(Z[:, i])
for p, t in zip(thetaplts, theta):
p.set_height(t)
ax2.relim()
ax3.relim()
ax4.relim()
ax5.relim()
ax2.autoscale()
ax3.autoscale()
ax4.autoscale()
ax5.autoscale()
fig.canvas.draw()
cid = fig.canvas.mpl_connect('button_press_event', onclick)
return ax1, ax2, ax3, ax4, ax5

View file

@ -287,29 +287,6 @@ class MRD(model):
else: else:
return pylab.gcf() return pylab.gcf()
def plot_X_1d(self, fig_num="MRD X 1d", axes=None, colors=None):
fig = pylab.figure(num=fig_num, figsize=(min(8, (3 * len(self.bgplvms))), min(12, (2 * self.X.shape[1]))))
if colors is None:
colors = pylab.gca()._get_lines.color_cycle
pylab.clf()
plots = []
for i in range(self.X.shape[1]):
if axes is None:
ax = fig.add_subplot(self.X.shape[1], 1, i + 1)
ax.plot(self.X, c='k', alpha=.3)
plots.extend(ax.plot(self.X.T[i], c=colors.next(), label=r"$\mathbf{{X_{}}}$".format(i)))
ax.fill_between(numpy.arange(self.X.shape[0]),
self.X.T[i] - 2 * numpy.sqrt(self.gref.X_variance.T[i]),
self.X.T[i] + 2 * numpy.sqrt(self.gref.X_variance.T[i]),
facecolor=plots[-1].get_color(),
alpha=.3)
ax.legend(borderaxespad=0.)
if i < self.X.shape[1] - 1:
ax.set_xticklabels('')
pylab.draw()
fig.tight_layout(h_pad=.01) # , rect=(0, 0, 1, .95))
return fig
def plot_X(self, fig_num="MRD Predictions", axes=None): def plot_X(self, fig_num="MRD Predictions", axes=None):
fig = self._handle_plotting(fig_num, axes, lambda i, g, ax: ax.imshow(g.X)) fig = self._handle_plotting(fig_num, axes, lambda i, g, ax: ax.imshow(g.X))
return fig return fig

View file

@ -57,6 +57,7 @@ class Test(unittest.TestCase):
X_var = .5 * numpy.ones_like(X) + .4 * numpy.clip(numpy.random.randn(*X.shape), 0, 1) X_var = .5 * numpy.ones_like(X) + .4 * numpy.clip(numpy.random.randn(*X.shape), 0, 1)
Z = numpy.random.permutation(X)[:M] Z = numpy.random.permutation(X)[:M]
Y = X.dot(numpy.random.randn(Q, D)) Y = X.dot(numpy.random.randn(Q, D))
kernels = [GPy.kern.linear(Q), GPy.kern.rbf(Q), GPy.kern.bias(Q)]
kernels = [GPy.kern.linear(Q), GPy.kern.rbf(Q), GPy.kern.bias(Q), kernels = [GPy.kern.linear(Q), GPy.kern.rbf(Q), GPy.kern.bias(Q),
GPy.kern.linear(Q) + GPy.kern.bias(Q), GPy.kern.linear(Q) + GPy.kern.bias(Q),