From 7d5283314a24642a76005de996d12da6b370cfad Mon Sep 17 00:00:00 2001 From: mzwiessele Date: Sat, 3 Oct 2015 19:39:46 +0100 Subject: [PATCH] [active dims] kernel active dims now the real active dims --- GPy/kern/_src/add.py | 16 +++++----- GPy/kern/_src/kern.py | 39 ++++++++++++------------ GPy/kern/_src/kernel_slice_operations.py | 8 ++--- GPy/testing/kernel_tests.py | 4 +++ 4 files changed, 36 insertions(+), 31 deletions(-) diff --git a/GPy/kern/_src/add.py b/GPy/kern/_src/add.py index 6893011d..41b4febb 100644 --- a/GPy/kern/_src/add.py +++ b/GPy/kern/_src/add.py @@ -121,7 +121,7 @@ class Add(CombinationKernel): #ffrom fixed import Fixed for p1, p2 in itertools.combinations(self.parts, 2): - # i1, i2 = p1.active_dims, p2.active_dims + # i1, i2 = p1._all_dims_active, p2._all_dims_active # white doesn;t combine with anything if isinstance(p1, White) or isinstance(p2, White): pass @@ -135,7 +135,7 @@ class Add(CombinationKernel): tmp = p1.psi1(Z, variational_posterior).sum(axis=0) psi2 += p2.variance * (tmp[:,None]+tmp[None,:]) #(tmp[:, :, None] + tmp[:, None, :]) elif isinstance(p2, (RBF, Linear)) and isinstance(p1, (RBF, Linear)): - assert np.intersect1d(p1.active_dims, p2.active_dims).size == 0, "only non overlapping kernel dimensions allowed so far" + assert np.intersect1d(p1._all_dims_active, p2._all_dims_active).size == 0, "only non overlapping kernel dimensions allowed so far" tmp1 = p1.psi1(Z, variational_posterior) tmp2 = p2.psi1(Z, variational_posterior) psi2 += np.einsum('nm,no->mo',tmp1,tmp2)+np.einsum('nm,no->mo',tmp2,tmp1) @@ -157,7 +157,7 @@ class Add(CombinationKernel): #ffrom fixed import Fixed for p1, p2 in itertools.combinations(self.parts, 2): - # i1, i2 = p1.active_dims, p2.active_dims + # i1, i2 = p1._all_dims_active, p2._all_dims_active # white doesn;t combine with anything if isinstance(p1, White) or isinstance(p2, White): pass @@ -171,7 +171,7 @@ class Add(CombinationKernel): tmp = p1.psi1(Z, variational_posterior) psi2 += p2.variance * (tmp[:, :, None] + tmp[:, None, :]) elif isinstance(p2, (RBF, Linear)) and isinstance(p1, (RBF, Linear)): - assert np.intersect1d(p1.active_dims, p2.active_dims).size == 0, "only non overlapping kernel dimensions allowed so far" + assert np.intersect1d(p1._all_dims_active, p2._all_dims_active).size == 0, "only non overlapping kernel dimensions allowed so far" tmp1 = p1.psi1(Z, variational_posterior) tmp2 = p2.psi1(Z, variational_posterior) psi2 += np.einsum('nm,no->nmo',tmp1,tmp2)+np.einsum('nm,no->nmo',tmp2,tmp1) @@ -193,7 +193,7 @@ class Add(CombinationKernel): continue elif isinstance(p2, Bias): eff_dL_dpsi1 += dL_dpsi2.sum(1) * p2.variance * 2. - else:# np.setdiff1d(p1.active_dims, ar2, assume_unique): # TODO: Careful, not correct for overlapping active_dims + else:# np.setdiff1d(p1._all_dims_active, ar2, assume_unique): # TODO: Careful, not correct for overlapping _all_dims_active eff_dL_dpsi1 += dL_dpsi2.sum(1) * p2.psi1(Z, variational_posterior) * 2. p1.update_gradients_expectations(dL_dpsi0, eff_dL_dpsi1, dL_dpsi2, Z, variational_posterior) @@ -244,17 +244,17 @@ class Add(CombinationKernel): self.link_parameters(*other_params) else: self.link_parameter(other) - self.input_dim, self.active_dims = self.get_input_dim_active_dims(self.parts) + self.input_dim, self._all_dims_active = self.get_input_dim_active_dims(self.parts) return self def input_sensitivity(self, summarize=True): if summarize: i_s = np.zeros((self.input_dim)) for k in self.parts: - i_s[k.active_dims] += k.input_sensitivity(summarize) + i_s[k._all_dims_active] += k.input_sensitivity(summarize) return i_s else: i_s = np.zeros((len(self.parts), self.input_dim)) from operator import setitem - [setitem(i_s, (i, k.active_dims), k.input_sensitivity(summarize)) for i, k in enumerate(self.parts)] + [setitem(i_s, (i, k._all_dims_active), k.input_sensitivity(summarize)) for i, k in enumerate(self.parts)] return i_s diff --git a/GPy/kern/_src/kern.py b/GPy/kern/_src/kern.py index 1cc0c0ba..0bb7f787 100644 --- a/GPy/kern/_src/kern.py +++ b/GPy/kern/_src/kern.py @@ -34,15 +34,15 @@ class Kern(Parameterized): If this is not an integer (!) we will work on the whole input matrix X, and not check whether dimensions match or not (!). - active_dims: + _all_dims_active: is the active_dimensions of inputs X we will work on. - All kernels will get sliced Xes as inputs, if active_dims is not None - Only positive integers are allowed in active_dims! - if active_dims is None, slicing is switched off and all X will be passed through as given. + All kernels will get sliced Xes as inputs, if _all_dims_active is not None + Only positive integers are allowed in _all_dims_active! + if _all_dims_active is None, slicing is switched off and all X will be passed through as given. :param int input_dim: the number of input dimensions to the function - :param array-like|None active_dims: list of indices on which dimensions this kernel works on, or none if no slicing + :param array-like|None _all_dims_active: list of indices on which dimensions this kernel works on, or none if no slicing Do not instantiate. """ @@ -52,19 +52,20 @@ class Kern(Parameterized): if active_dims is None: active_dims = np.arange(input_dim) - self.active_dims = np.atleast_1d(active_dims).astype(int) + self.active_dims = active_dims + self._all_dims_active = np.atleast_1d(active_dims).astype(int) - assert self.active_dims.size == self.input_dim, "input_dim={} does not match len(active_dim)={}, active_dims={}".format(self.input_dim, self.active_dims.size, self.active_dims) + assert self._all_dims_active.size == self.input_dim, "input_dim={} does not match len(active_dim)={}, _all_dims_active={}".format(self.input_dim, self._all_dims_active.size, self._all_dims_active) self._sliced_X = 0 self.useGPU = self._support_GPU and useGPU from .psi_comp import PSICOMP_GH - self.psicomp = PSICOMP_GH() + self.psicomp = PSICOMP_GH() @Cache_this(limit=20) def _slice_X(self, X): - return X[:, self.active_dims] + return X[:, self._all_dims_active] def K(self, X, X2): """ @@ -244,9 +245,9 @@ class Kern(Parameterized): """ Shortcut for tensor `prod`. """ - assert np.all(self.active_dims == range(self.input_dim)), "Can only use kernels, which have their input_dims defined from 0" - assert np.all(other.active_dims == range(other.input_dim)), "Can only use kernels, which have their input_dims defined from 0" - other.active_dims += self.input_dim + assert np.all(self._all_dims_active == range(self.input_dim)), "Can only use kernels, which have their input_dims defined from 0" + assert np.all(other._all_dims_active == range(other.input_dim)), "Can only use kernels, which have their input_dims defined from 0" + other._all_dims_active += self.input_dim return self.prod(other) def prod(self, other, name='mul'): @@ -268,10 +269,10 @@ class Kern(Parameterized): return Prod([self, other], name) def _check_input_dim(self, X): - assert X.shape[1] == self.input_dim, "{} did not specify active_dims and X has wrong shape: X_dim={}, whereas input_dim={}".format(self.name, X.shape[1], self.input_dim) + assert X.shape[1] == self.input_dim, "{} did not specify _all_dims_active and X has wrong shape: X_dim={}, whereas input_dim={}".format(self.name, X.shape[1], self.input_dim) def _check_active_dims(self, X): - assert X.shape[1] >= len(self.active_dims), "At least {} dimensional X needed, X.shape={!s}".format(len(self.active_dims), X.shape) + assert X.shape[1] >= len(self._all_dims_active), "At least {} dimensional X needed, X.shape={!s}".format(len(self._all_dims_active), X.shape) class CombinationKernel(Kern): @@ -303,15 +304,15 @@ class CombinationKernel(Kern): return self.parameters def get_input_dim_active_dims(self, kernels, extra_dims = None): - #active_dims = reduce(np.union1d, (np.r_[x.active_dims] for x in kernels), np.array([], dtype=int)) - #active_dims = np.array(np.concatenate((active_dims, extra_dims if extra_dims is not None else [])), dtype=int) - input_dim = reduce(max, (k.active_dims.max() for k in kernels)) + 1 + self.active_dims = reduce(np.union1d, (np.r_[x.active_dims] for x in kernels), np.array([], dtype=int)) + #_all_dims_active = np.array(np.concatenate((_all_dims_active, extra_dims if extra_dims is not None else [])), dtype=int) + input_dim = reduce(max, (k._all_dims_active.max() for k in kernels)) + 1 if extra_dims is not None: input_dim += extra_dims.size - active_dims = np.arange(input_dim) - return input_dim, active_dims + _all_dims_active = np.arange(input_dim) + return input_dim, _all_dims_active def input_sensitivity(self, summarize=True): """ diff --git a/GPy/kern/_src/kernel_slice_operations.py b/GPy/kern/_src/kernel_slice_operations.py index 7d4e540b..2bd1f923 100644 --- a/GPy/kern/_src/kernel_slice_operations.py +++ b/GPy/kern/_src/kernel_slice_operations.py @@ -5,7 +5,7 @@ Created on 11 Mar 2014 This module provides a meta class for the kernels. The meta class is for slicing the inputs (X, X2) for the kernels, before K (or any other method involving X) -gets calls. The `active_dims` of a kernel decide which dimensions the kernel works on. +gets calls. The `_all_dims_active` of a kernel decide which dimensions the kernel works on. ''' from ...core.parameterization.parameterized import ParametersChangedMeta import numpy as np @@ -47,7 +47,7 @@ class _Slice_wrap(object): assert X.ndim == 2, "only matrices are allowed as inputs to kernels for now, given X.shape={!s}".format(X.shape) if X2 is not None: assert X2.ndim == 2, "only matrices are allowed as inputs to kernels for now, given X2.shape={!s}".format(X2.shape) - if (self.k.active_dims is not None) and (self.k._sliced_X == 0): + if (self.k._all_dims_active is not None) and (self.k._sliced_X == 0): self.k._check_active_dims(X) self.X = self.k._slice_X(X) self.X2 = self.k._slice_X(X2) if X2 is not None else X2 @@ -66,9 +66,9 @@ class _Slice_wrap(object): if self.ret: ret = np.zeros(self.shape) if len(self.shape) == 2: - ret[:, self.k.active_dims] = return_val + ret[:, self.k._all_dims_active] = return_val elif len(self.shape) == 3: - ret[:, :, self.k.active_dims] = return_val + ret[:, :, self.k._all_dims_active] = return_val return ret return return_val diff --git a/GPy/testing/kernel_tests.py b/GPy/testing/kernel_tests.py index 1fd92d98..e81ce2cf 100644 --- a/GPy/testing/kernel_tests.py +++ b/GPy/testing/kernel_tests.py @@ -346,6 +346,10 @@ class KernelTestsMiscellaneous(unittest.TestCase): self.assertTrue(np.allclose(self.sumkern.K(self.X, which_parts=[self.linear, self.rbf]), self.linear.K(self.X)+self.rbf.K(self.X))) self.assertTrue(np.allclose(self.sumkern.K(self.X, which_parts=self.sumkern.parts[0]), self.rbf.K(self.X))) + def test_active_dims(self): + np.testing.assert_array_equal(self.sumkern.active_dims, [0,1,2,3,7,9]) + np.testing.assert_array_equal(self.sumkern._all_dims_active, range(10)) + class KernelTestsNonContinuous(unittest.TestCase): def setUp(self): N0 = 3