New operator: the kernels can be multiplied directly with the '*' character

This commit is contained in:
Nicolas 2013-01-30 17:41:51 +00:00
parent c4f0a9bbc2
commit f4b6568ee9
5 changed files with 79 additions and 7 deletions

View file

@ -94,7 +94,7 @@ class parameterised(object):
Other objects are passed through - i.e. integers which were'nt meant for grepping 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) expr = re.compile(expr)
return np.nonzero([expr.search(name) for name in self._get_param_names()])[0] return np.nonzero([expr.search(name) for name in self._get_param_names()])[0]
elif type(expr) is re._pattern_type: elif type(expr) is re._pattern_type:

View file

@ -31,14 +31,14 @@ class Matern52(kernpart):
self.ARD = ARD self.ARD = ARD
if ARD == False: if ARD == False:
self.Nparam = 2 self.Nparam = 2
self.name = 'Mat32' self.name = 'Mat52'
if lengthscale is not None: if lengthscale is not None:
assert lengthscale.shape == (1,) assert lengthscale.shape == (1,)
else: else:
lengthscale = np.ones(1) lengthscale = np.ones(1)
else: else:
self.Nparam = self.D + 1 self.Nparam = self.D + 1
self.name = 'Mat32_ARD' self.name = 'Mat52_ARD'
if lengthscale is not None: if lengthscale is not None:
assert lengthscale.shape == (self.D,) assert lengthscale.shape == (self.D,)
else: else:

View file

@ -2,5 +2,5 @@
# Licensed under the BSD 3-clause license (see LICENSE.txt) # 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 from kern import kern

View file

@ -18,7 +18,7 @@ from Brownian import Brownian as Brownianpart
from periodic_exponential import periodic_exponential as periodic_exponentialpart from periodic_exponential import periodic_exponential as periodic_exponentialpart
from periodic_Matern32 import periodic_Matern32 as periodic_Matern32part from periodic_Matern32 import periodic_Matern32 as periodic_Matern32part
from periodic_Matern52 import periodic_Matern52 as periodic_Matern52part 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 #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. #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) part = periodic_Matern52part(D,variance, lengthscale, period, n_freq, lower, upper)
return kern(D, [part]) 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])

View file

@ -6,7 +6,8 @@ import numpy as np
from ..core.parameterised import parameterised from ..core.parameterised import parameterised
from functools import partial from functools import partial
from kernpart import kernpart from kernpart import kernpart
import itertools
import GPy
class kern(parameterised): class kern(parameterised):
def __init__(self,D,parts=[], input_slices=None): def __init__(self,D,parts=[], input_slices=None):
@ -45,7 +46,6 @@ class kern(parameterised):
for p in self.parts: for p in self.parts:
assert isinstance(p,kernpart), "bad kernel part" assert isinstance(p,kernpart), "bad kernel part"
self.compute_param_slices() self.compute_param_slices()
parameterised.__init__(self) 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] newkern.tied_indices = self.tied_indices + [self.Nparam + x for x in other.tied_indices]
return newkern 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): def _get_params(self):
return np.hstack([p._get_params() for p in self.parts]) return np.hstack([p._get_params() for p in self.parts])