From 675d4987948568d2ad176eee1794c61eefa085f8 Mon Sep 17 00:00:00 2001 From: Max Zwiessele Date: Fri, 9 May 2014 14:05:22 +0100 Subject: [PATCH] [active_dims] all kernels now have int arrays as active_dims --- GPy/kern/_src/add.py | 5 +---- GPy/kern/_src/kern.py | 45 +++++++++++++++---------------------- GPy/testing/kernel_tests.py | 12 +--------- 3 files changed, 20 insertions(+), 42 deletions(-) diff --git a/GPy/kern/_src/add.py b/GPy/kern/_src/add.py index 88b8e40c..f5b54797 100644 --- a/GPy/kern/_src/add.py +++ b/GPy/kern/_src/add.py @@ -170,7 +170,4 @@ class Add(CombinationKernel): return self def input_sensitivity(self): - in_sen = np.zeros(self.input_dim) - for i, p in enumerate(self.parts): - in_sen[p.active_dims] += p.input_sensitivity() - return in_sen + return reduce(np.add, [k.input_sensitivity() for k in self.parts]) \ No newline at end of file diff --git a/GPy/kern/_src/kern.py b/GPy/kern/_src/kern.py index 4028e16b..368a9c87 100644 --- a/GPy/kern/_src/kern.py +++ b/GPy/kern/_src/kern.py @@ -34,36 +34,24 @@ class Kern(Parameterized): 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. :param int input_dim: the number of input dimensions to the function - :param array-like|slice|None active_dims: list of indices on which dimensions this kernel works on, or none if no slicing + :param array-like|None active_dims: list of indices on which dimensions this kernel works on, or none if no slicing Do not instantiate. """ super(Kern, self).__init__(name=name, *a, **kw) - try: - self.input_dim = int(input_dim) - self.active_dims = active_dims# if active_dims is not None else slice(0, input_dim, 1) - except ValueError: - # input_dim is something else then an integer - self.input_dim = input_dim - if active_dims is not None: - print "WARNING: given input_dim={} is not an integer and active_dims={} is given, switching off slicing" - self.active_dims = None + self.input_dim = int(input_dim) + + if active_dims is None: + active_dims = np.arange(input_dim) + + self.active_dims = np.array(active_dims, dtype=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) - if self.active_dims is not None and self.input_dim is not None: - assert isinstance(self.active_dims, (slice, list, tuple, np.ndarray)), 'active_dims needs to be an array-like or slice object over dimensions, {} given'.format(self.active_dims.__class__) - if isinstance(self.active_dims, slice): - self.active_dims = slice(self.active_dims.start or 0, self.active_dims.stop or self.input_dim, self.active_dims.step or 1) - active_dim_size = int(np.round((self.active_dims.stop-self.active_dims.start)/self.active_dims.step)) - elif isinstance(self.active_dims, np.ndarray): - #assert np.all(self.active_dims >= 0), 'active dimensions need to be positive. negative indexing is not allowed' - assert self.active_dims.ndim == 1, 'only flat indices allowed, given active_dims.shape={}, provide only indexes to the dimensions (columns) of the input'.format(self.active_dims.shape) - active_dim_size = self.active_dims.size - else: - active_dim_size = len(self.active_dims) - assert active_dim_size == self.input_dim, "input_dim={} does not match len(active_dim)={}, active_dims={}".format(self.input_dim, active_dim_size, self.active_dims) self._sliced_X = 0 self.useGPU = self._support_GPU and useGPU @@ -205,7 +193,7 @@ class Kern(Parameterized): 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) def _check_active_dims(self, X): - assert X.shape[1] >= len(np.r_[self.active_dims]), "At least {} dimensional X needed, X.shape={!s}".format(len(np.r_[self.active_dims]), X.shape) + assert X.shape[1] >= len(self.active_dims), "At least {} dimensional X needed, X.shape={!s}".format(len(self.active_dims), X.shape) class CombinationKernel(Kern): @@ -222,9 +210,10 @@ class CombinationKernel(Kern): :param list kernels: List of kernels to combine (can be only one element) :param str name: name of the combination kernel - :param array-like|slice extra_dims: if needed extra dimensions for the combination kernel to work on + :param array-like extra_dims: if needed extra dimensions for the combination kernel to work on """ assert all([isinstance(k, Kern) for k in kernels]) + extra_dims = np.array(extra_dims, dtype=int) input_dim, active_dims = self.get_input_dim_active_dims(kernels, extra_dims) # initialize the kernel with the full input_dim super(CombinationKernel, self).__init__(input_dim, active_dims, name) @@ -238,10 +227,12 @@ class CombinationKernel(Kern): 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 = " ".join(map(lambda k: "{!s}:{!s}".format(k.name, k.input_dim), kernels)) + input_dim = reduce(max, (k.active_dims.max() for k in kernels)) + 1 + if extra_dims is not None: - input_dim += " + extra:{!s}".format(extra_dims) - active_dims = None + input_dim += extra_dims.size + + active_dims = np.arange(input_dim) return input_dim, active_dims def input_sensitivity(self): diff --git a/GPy/testing/kernel_tests.py b/GPy/testing/kernel_tests.py index 8c48d37f..a942dc49 100644 --- a/GPy/testing/kernel_tests.py +++ b/GPy/testing/kernel_tests.py @@ -304,23 +304,13 @@ class KernelTestsMiscellaneous(unittest.TestCase): def setUp(self): N, D = 100, 10 self.X = np.linspace(-np.pi, +np.pi, N)[:,None] * np.random.uniform(-10,10,D) - self.rbf = GPy.kern.RBF(2, active_dims=slice(0,4,2)) + self.rbf = GPy.kern.RBF(2, active_dims=np.arange(0,4,2)) self.linear = GPy.kern.Linear(2, active_dims=(3,9)) self.matern = GPy.kern.Matern32(3, active_dims=np.array([1,7,9])) self.sumkern = self.rbf + self.linear self.sumkern += self.matern self.sumkern.randomize() - def test_active_dims(self): - # test the automatic dim detection expression for slices: - start, stop = 0, 277 - for i in range(start,stop,7): - for j in range(1,4): - GPy.kern.Kern(int(np.round((i+1)/j)), slice(0, i+1, j), "testkern") - # test the ability to have only one dim - sk = GPy.kern.RBF(2) + GPy.kern.Matern32(2) - self.assertEqual(sk.input_dim, "rbf:2 Mat32:2 + extra:[]") - def test_which_parts(self): self.assertTrue(np.allclose(self.sumkern.K(self.X, which_parts=[self.linear, self.matern]), self.linear.K(self.X)+self.matern.K(self.X))) 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)))