diff --git a/GPy/core/parameterised.py b/GPy/core/parameterised.py index 81b6aa8a..6e5493ad 100644 --- a/GPy/core/parameterised.py +++ b/GPy/core/parameterised.py @@ -94,7 +94,7 @@ class parameterised(object): Other objects are passed through - i.e. integers which were'nt meant for grepping """ - if type(expr) is str: + if type(expr) in [str, np.string_, np.str]: expr = re.compile(expr) return np.nonzero([expr.search(name) for name in self._get_param_names()])[0] elif type(expr) is re._pattern_type: diff --git a/GPy/kern/Matern52.py b/GPy/kern/Matern52.py index 84c71089..b3365ec9 100644 --- a/GPy/kern/Matern52.py +++ b/GPy/kern/Matern52.py @@ -31,14 +31,14 @@ class Matern52(kernpart): self.ARD = ARD if ARD == False: self.Nparam = 2 - self.name = 'Mat32' + self.name = 'Mat52' if lengthscale is not None: assert lengthscale.shape == (1,) else: lengthscale = np.ones(1) else: self.Nparam = self.D + 1 - self.name = 'Mat32_ARD' + self.name = 'Mat52_ARD' if lengthscale is not None: assert lengthscale.shape == (self.D,) else: diff --git a/GPy/kern/__init__.py b/GPy/kern/__init__.py index 20d84949..17bc607e 100644 --- a/GPy/kern/__init__.py +++ b/GPy/kern/__init__.py @@ -2,5 +2,5 @@ # Licensed under the BSD 3-clause license (see LICENSE.txt) -from constructors import rbf, Matern32, Matern52, exponential, linear, white, bias, finite_dimensional, spline, Brownian, rbf_sympy, sympykern, periodic_exponential, periodic_Matern32, periodic_Matern52 +from constructors import rbf, Matern32, Matern52, exponential, linear, white, bias, finite_dimensional, spline, Brownian, rbf_sympy, sympykern, periodic_exponential, periodic_Matern32, periodic_Matern52, product_orthogonal from kern import kern diff --git a/GPy/kern/constructors.py b/GPy/kern/constructors.py index b483c823..1e76f559 100644 --- a/GPy/kern/constructors.py +++ b/GPy/kern/constructors.py @@ -18,7 +18,7 @@ from Brownian import Brownian as Brownianpart from periodic_exponential import periodic_exponential as periodic_exponentialpart from periodic_Matern32 import periodic_Matern32 as periodic_Matern32part from periodic_Matern52 import periodic_Matern52 as periodic_Matern52part - +from product_orthogonal import product_orthogonal as product_orthogonalpart #TODO these s=constructors are not as clean as we'd like. Tidy the code up #using meta-classes to make the objects construct properly wthout them. @@ -241,3 +241,14 @@ def periodic_Matern52(D,variance=1., lengthscale=None, period=2*np.pi,n_freq=10, """ part = periodic_Matern52part(D,variance, lengthscale, period, n_freq, lower, upper) return kern(D, [part]) + +def product_orthogonal(k1,k2): + """ + Construct a product kernel + + :param k1, k2: the kernels to multiply + :type k1, k2: kernpart + :rtype: kernel object + """ + part = product_orthogonalpart(k1,k2) + return kern(k1.D+k2.D, [part]) diff --git a/GPy/kern/kern.py b/GPy/kern/kern.py index 5f259f55..692c3874 100644 --- a/GPy/kern/kern.py +++ b/GPy/kern/kern.py @@ -6,7 +6,8 @@ import numpy as np from ..core.parameterised import parameterised from functools import partial from kernpart import kernpart - +import itertools +import GPy class kern(parameterised): def __init__(self,D,parts=[], input_slices=None): @@ -45,7 +46,6 @@ class kern(parameterised): for p in self.parts: assert isinstance(p,kernpart), "bad kernel part" - self.compute_param_slices() parameterised.__init__(self) @@ -133,6 +133,67 @@ class kern(parameterised): newkern.tied_indices = self.tied_indices + [self.Nparam + x for x in other.tied_indices] return newkern + def __mul__(self,other): + """ + Shortcut for `prod_orthogonal`. Note that `+` assumes that we sum 2 kernels defines on the same space whereas `*` assumes that the kernels are defined on different subspaces. + """ + return self.prod_orthogonal(other) + + def prod_orthogonal(self,other): + """ + multiply two kernels. Both kernels are defined on separate spaces. Note that the constrains on the parameters of the kernels to multiply will be lost. + :param other: the other kernel to be added + :type other: GPy.kern + """ + K1 = self.copy() + K2 = other.copy() + K1.unconstrain('') + K2.unconstrain('') + + prev_ties = K1.tied_indices + [arr + K1.Nparam for arr in K2.tied_indices] + K1.untie_everything() + K2.untie_everything() + + D = K1.D + K2.D + + newkernparts = [GPy.kern.product_orthogonal(k1,k2).parts[0] for k1, k2 in itertools.product(K1.parts,K2.parts)] + + slices = [] + for sl1, sl2 in itertools.product(K1.input_slices,K2.input_slices): + s1, s2 = [False]*K1.D, [False]*K2.D + s1[sl1], s2[sl2] = [True], [True] + slices += [s1+s2] + + newkern = kern(D, newkernparts, slices) + + # create the ties + K1_param = [] + n = 0 + for k1 in K1.parts: + K1_param += [range(n,n+k1.Nparam)] + n += k1.Nparam + n = 0 + K2_param = [] + for k2 in K2.parts: + K2_param += [range(K1.Nparam+n,K1.Nparam+n+k2.Nparam)] + n += k2.Nparam + index_param = [] + for p1 in K1_param: + for p2 in K2_param: + index_param += [0] + p1[1:] + p2[1:] + index_param = np.array(index_param) + + # follow the previous ties + for arr in prev_ties: + for j in arr: + index_param[np.where(index_param==j)[0]] = arr[0] + + # tie + for i in np.unique(index_param)[1:]: + newkern.tie_param(np.where(index_param==i)[0]) + + return newkern + def _get_params(self): return np.hstack([p._get_params() for p in self.parts])