class TestLowRank(unittest.TestCase): """test class for CLowRankCF""" def setUp(self): sp.random.seed(1) self.n = 4 self.rank = 2 self.C = LowRankCov(self.n, self.rank) self.name = 'lowrank' self.C.setRandomParams() def test_grad(self): def func(x, i): self.C.setParams(x) return self.C.K() def grad(x, i): self.C.setParams(x) return self.C.K_grad_i(i) x0 = self.C.getParams() err = mcheck_grad(func, grad, x0) np.testing.assert_almost_equal(err, 0., decimal=6) def test_param_activation(self): self.assertEqual(len(self.C.getParams()), 8) self.C.act_X = False self.assertEqual(len(self.C.getParams()), 0) self.C.setParams(np.array([])) with self.assertRaises(ValueError): self.C.setParams(np.array([0])) with self.assertRaises(ValueError): self.C.K_grad_i(0)
class TestLowRank(unittest.TestCase): """test class for CLowRankCF""" def setUp(self): sp.random.seed(1) self.n = 4 self.rank = 2 self.C = LowRankCov(self.n,self.rank) self.name = 'lowrank' self.C.setRandomParams() def test_grad(self): def func(x, i): self.C.setParams(x) return self.C.K() def grad(x, i): self.C.setParams(x) return self.C.K_grad_i(i) x0 = self.C.getParams() err = mcheck_grad(func, grad, x0) np.testing.assert_almost_equal(err, 0., decimal = 6) def test_param_activation(self): self.assertEqual(len(self.C.getParams()), 8) self.C.act_X = False self.assertEqual(len(self.C.getParams()), 0) self.C.setParams(np.array([])) with self.assertRaises(ValueError): self.C.setParams(np.array([0])) with self.assertRaises(ValueError): self.C.K_grad_i(0)
class CategoricalCov(Covariance): """ Categorical covariance. cov_{i,j} depends on the category of i and j only Main members of the class: - categories: a vector of length the dimension of the cov matrix with the categories to which every sample belongs - cat_cov, a covariance matrix between categories, whose dimensions are determined by the number of categories -> can be free form or low rank """ def __init__(self, categories, rank=None, cat_star=None, jitter= 1e-4): Covariance.__init__(self) self.cat = categories self.jitter = jitter self.rank = rank self.cat_star = cat_star ##################### # properties ##################### @property def cat(self): return self._cat @property def rank(self): return self._rank @property def cat_star(self): return self._cat_star ##################### # Setters ##################### @cat.setter def cat(self, value): self._cat = value # initialise related members self.dim = len(value) self.unique_cats = np.unique(value) self.n_cats = len(self.unique_cats) # initialise int indexed categories self._i_cat = np.zeros(len(value), dtype=int) for i in range(self.n_cats): self._i_cat += (value == self.unique_cats[i])*i self.clear_all() self._notify() @rank.setter def rank(self, value): self._rank = value if value is None: self.cat_cov = FreeFormCov(self.n_cats, jitter=self.jitter) else: assert value <= self.n_cats, 'rank cant be higher than number of categories' self.cat_cov = LowRankCov(self.n_cats, value) self.clear_all() self._notify() @cat_star.setter def cat_star(self, value): self._cat_star = value if value is None: self._use_to_predict = False else: self._use_to_predict = True # check that all categories in cat_star are also found in cats cat_star_uq = np.unique(self.cat_star) assert all(np.in1d(cat_star_uq, self.unique_cats)), 'all categories used for prediction must be seen during training' # build the int category vector # TODO private and change names higher up self._i_cat_star = np.zeros(len(self.cat_star)) for i in range(self.n_cats): self._i_cat_star += (self.cat_star == self.unique_cats[i])*i self.clear_all() self._notify() @Covariance.use_to_predict.setter def use_to_predict(self,value): if value: assert self.cat_star is not None, 'set cat_star!' self._use_to_predict = value self._notify() ##################### # Params handling ##################### def setParams(self, params): self.cat_cov.setParams(params) self.clear_cache('K', 'K_grad_i') self._notify() def getParams(self): return self.cat_cov.getParams() def getInterParams(self): return self.cat_cov.getInterParams() def getNumberParams(self): return self.cat_cov.getNumberParams() ##################### # cov and gradient ##################### @cached def K(self): R = self.cat_cov.K() return self.expand(R) @cached def K_grad_i(self, i): R = self.cat_cov.K_grad_i(i) return self.expand(R) @cached def Kcross(self): R = self.cat_cov.K() return self.expand_star(R) # TODO: hessian ? not implemented for lowrank ##################### # Expanding the category * category matrices ##################### # for the covariance or its gradient def expand(self, mat): R = np.zeros([self.dim, self.dim]) for i in range(self.n_cats): for j in range(self.n_cats): tmp_i = (self._i_cat == i)[:, None] tmp_j = (self._i_cat == j)[None, :] R += tmp_i.dot(tmp_j) * mat[i,j] return R # for the cross covariance def expand_star(self, mat): n_star = len(self.cat_star) R = np.zeros([n_star, self.dim]) for i in range(self.n_cats): for j in range(self.n_cats): tmp_i = (self._i_cat_star == i)[:, None] tmp_j = (self._i_cat == j)[None, :] R += tmp_i.dot(tmp_j) * mat[i,j] return R
class CategoricalCov(Covariance): """ Categorical covariance. cov_{i,j} depends on the category of i and j only Main members of the class: - categories: a vector of length the dimension of the cov matrix with the categories to which every sample belongs - cat_cov, a covariance matrix between categories, whose dimensions are determined by the number of categories -> can be free form or low rank """ def __init__(self, categories, rank=None, cat_star=None, jitter=1e-4): Covariance.__init__(self) self.cat = categories self.jitter = jitter self.rank = rank self.cat_star = cat_star ##################### # properties ##################### @property def cat(self): return self._cat @property def rank(self): return self._rank @property def cat_star(self): return self._cat_star ##################### # Setters ##################### @cat.setter def cat(self, value): self._cat = value # initialise related members self.dim = len(value) self.unique_cats = np.unique(value) self.n_cats = len(self.unique_cats) # initialise int indexed categories self._i_cat = np.zeros(len(value), dtype=int) for i in range(self.n_cats): self._i_cat += (value == self.unique_cats[i]) * i self.clear_all() self._notify() @rank.setter def rank(self, value): self._rank = value if value is None: self.cat_cov = FreeFormCov(self.n_cats, jitter=self.jitter) else: assert value <= self.n_cats, 'rank cant be higher than number of categories' self.cat_cov = LowRankCov(self.n_cats, value) self.clear_all() self._notify() @cat_star.setter def cat_star(self, value): self._cat_star = value if value is None: self._use_to_predict = False else: self._use_to_predict = True # check that all categories in cat_star are also found in cats cat_star_uq = np.unique(self.cat_star) assert all( np.in1d(cat_star_uq, self.unique_cats) ), 'all categories used for prediction must be seen during training' # build the int category vector # TODO private and change names higher up self._i_cat_star = np.zeros(len(self.cat_star)) for i in range(self.n_cats): self._i_cat_star += (self.cat_star == self.unique_cats[i]) * i self.clear_all() self._notify() @Covariance.use_to_predict.setter def use_to_predict(self, value): if value: assert self.cat_star is not None, 'set cat_star!' self._use_to_predict = value self._notify() ##################### # Params handling ##################### def setParams(self, params): self.cat_cov.setParams(params) self.clear_cache('K', 'K_grad_i') self._notify() def getParams(self): return self.cat_cov.getParams() def getInterParams(self): return self.cat_cov.getInterParams() def getNumberParams(self): return self.cat_cov.getNumberParams() ##################### # cov and gradient ##################### @cached def K(self): R = self.cat_cov.K() return self.expand(R) @cached def K_grad_i(self, i): R = self.cat_cov.K_grad_i(i) return self.expand(R) @cached def Kcross(self): R = self.cat_cov.K() return self.expand_star(R) # TODO: hessian ? not implemented for lowrank ##################### # Expanding the category * category matrices ##################### # for the covariance or its gradient def expand(self, mat): R = np.zeros([self.dim, self.dim]) for i in range(self.n_cats): for j in range(self.n_cats): tmp_i = (self._i_cat == i)[:, None] tmp_j = (self._i_cat == j)[None, :] R += tmp_i.dot(tmp_j) * mat[i, j] return R # for the cross covariance def expand_star(self, mat): n_star = len(self.cat_star) R = np.zeros([n_star, self.dim]) for i in range(self.n_cats): for j in range(self.n_cats): tmp_i = (self._i_cat_star == i)[:, None] tmp_j = (self._i_cat == j)[None, :] R += tmp_i.dot(tmp_j) * mat[i, j] return R