[active dims] kernel active dims now the real active dims

This commit is contained in:
mzwiessele 2015-10-03 19:39:46 +01:00
parent c3afb4eaaf
commit 7d5283314a
4 changed files with 36 additions and 31 deletions

View file

@ -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

View file

@ -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):
"""

View file

@ -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

View file

@ -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