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 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()
def setColCovars(self, Cn = None, rank = 1): assert Cn is not None, 'Cn has to be specified.' self._rank_c = rank self._dim_c = Cn.dim self._Cr = LowRankCov(self._dim_c, rank) self._Cn = Cn # register self._Cr.register(self.col_covs_have_changed) self._Cn.register(self.col_covs_have_changed) self.col_covs_have_changed()
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 setColCovars(self, Cg=None, Cn=None, rank=1, Cr=None): assert Cg is not None, 'Cov2KronSum: Specify Cg!' assert Cn is not None, 'Cov2KronSum: Specify Cn!' assert Cg.dim==Cn.dim, 'Cov2KronSum: Cg and Cn must have same dimensions!' if Cr is None: Cr = LowRankCov(Cg.dim, rank) Cr.setRandomParams() self._rank_c = rank else: self._rank_c = Cr.X.shape[1] self._dim_c = Cg.dim self._Cr = Cr self._Cg = Cg self._Cn = Cn self._Cg.register(self.col_covs_have_changed) self._Cn.register(self.col_covs_have_changed) self.col_covs_have_changed()
def setColCovars(self, Cg=None, Cn=None, rank=1, Cr=None): assert Cg is not None, 'Cov2KronSum: Specify Cg!' assert Cn is not None, 'Cov2KronSum: Specify Cn!' assert Cg.dim == Cn.dim, 'Cov2KronSum: Cg and Cn must have same dimensions!' if Cr is None: Cr = LowRankCov(Cg.dim, rank) Cr.setRandomParams() self._rank_c = rank else: self._rank_c = Cr.X.shape[1] self._dim_c = Cg.dim self._Cr = Cr self._Cg = Cg self._Cn = Cn self._Cg.register(self.col_covs_have_changed) self._Cn.register(self.col_covs_have_changed) self.col_covs_have_changed()
def setColCovars(self, Cg=None, Cn=None, rank=1, Cr=None): assert Cg is not None, 'Cov2KronSum: Specify Cg!' assert Cn is not None, 'Cov2KronSum: Specify Cn!' assert Cg.dim==Cn.dim, 'Cov2KronSum: Cg and Cn must have same dimensions!' assert Cr is None, '%s: more general covariance matrices are not supported at the moment' % self.__class__.__name__ if Cr is None: Cr = LowRankCov(Cg.dim, rank) Cr.setRandomParams() self._rank_c = rank else: self._rank_c = self.Cr.dim() self._dim_c = Cg.dim self._Cr = Cr self._Cg = Cg self._Cn = Cn self._Cg.register(self.col_covs_have_changed) self._Cn.register(self.col_covs_have_changed) self.col_covs_have_changed()
def setColCovars(self, Cr=None, Cn=None, rank=1): assert Cn is not None, 'Cn has to be specified.' # set noise self._Cn = Cn self._dim_c = Cn.dim # set region if Cr is None: Cr = LowRankCov(self._dim_c, rank) self._Cr = Cr self._rank_c = self.Cr.X.shape[1] # register self._Cr.register(self.col_covs_have_changed) self._Cn.register(self.col_covs_have_changed) self.col_covs_have_changed()
def define_gp(Y, Xr, mean, Ie, type): P = 2 if type == 'null': _Cr = FixedCov(sp.ones([2, 2])) _Cr.scale = 1e-9 _Cr.act_scale = False covar = CategoricalLR(_Cr, sp.ones((Xr.shape[0], 1)), Ie) else: if type == 'block': _Cr = FixedCov(sp.ones((P, P))) elif type == 'rank1': _Cr = LowRankCov(P, 1) elif type == 'full': _Cr = FreeFormCov(P) else: print('poppo') covar = CategoricalLR(_Cr, Xr, Ie) _gp = GP(covar=covar, mean=mean) return _gp
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 Cov2KronSumLR(Covariance): """ Covariance class for sum of 2 Kronecker products with one low rank term: K = Cr \kron GG.T + Cn \kron I Notation: - dim_c: dimension of col covariances - dim_r: dimension of row covariances - rank_c: rank of low-rank col covariance - rank_r: rank of low-rank row covariance """ def __init__(self, Cn = None, G = None, rank = 1): """ Args: Cn: Limix covariance matrix for Cn (dimension dim_c) G: [dim_r, rank_r] numpy covariance matrix for G rank: rank of column low-rank covariance (default = 1) """ Covariance.__init__(self) self._Cr_act = True self._Cn_act = True self.setColCovars(Cn, rank = rank) self.G = G self.dim = self.dim_c * self.dim_r self._use_to_predict = False def G_has_changed(self): self.clear_cache('row_cov') self._notify('row_cov') self.clear_all() def col_covs_have_changed(self): self.clear_cache('col_cov') self._notify('col_cov') self.clear_all() ##################### # Properties ##################### @property def G(self): return self._G @property def Cr(self): return self._Cr @property def Cn(self): return self._Cn @property def dim_r(self): return self._dim_r @property def dim_c(self): return self._dim_c @property def rank_r(self): return self._rank_r @property def rank_c(self): return self._rank_c ##################### # Setters ##################### @G.setter def G(self,value): assert value is not None, 'G cannot be set to None.' self._dim_r = value.shape[0] # perform svd on G # excludes eigh < 1e-8 and recalcs G _value, U, S, V = svd_reduce(value) self._rank_r = _value.shape[1] self._G = _value self._Ug = U self._Sg = S self._Vg = V self.G_has_changed() # normal setter for col covars def setColCovars(self, Cn = None, rank = 1): assert Cn is not None, 'Cn has to be specified.' self._rank_c = rank self._dim_c = Cn.dim self._Cr = LowRankCov(self._dim_c, rank) self._Cn = Cn # register self._Cr.register(self.col_covs_have_changed) self._Cn.register(self.col_covs_have_changed) self.col_covs_have_changed() ##################### # Activation handling ##################### @property def act_Cr(self): return self._Cr_act @act_Cr.setter def act_Cr(self, act): self._Cr_act = bool(act) self._notify() @property def act_Cn(self): return self._Cn_act @act_Cn.setter def act_Cn(self, act): self._Cn_act = bool(act) self._notify() def _actindex2index(self, i): nCr = self.Cr.getNumberParams() i += nCr * int(not self._Cr_act) return i def _index2actindex(self, i): nCr = self.Cr.getNumberParams() i -= nCr * int(not self._Cr_act) return i ##################### # Params handling ##################### def setParams(self, params): nCr = int(self._Cr_act) * self.Cr.getNumberParams() nCn = int(self._Cn_act) * self.Cn.getNumberParams() if len(params) != self.getNumberParams(): raise ValueError("The number of parameters passed to setParams " "differs from the number of active parameters.") if self._Cr_act: self.Cr.setParams(params[:nCr]) if self._Cn_act: self.Cn.setParams(params[nCr:]) def getParams(self): params = [] if self._Cr_act: params.append(self.Cr.getParams()) if self._Cn_act: params.append(self.Cn.getParams()) if len(params) == 0: return np.array([]) return sp.concatenate(params) def getNumberParams(self): return (int(self._Cr_act) * self.Cr.getNumberParams() + int(self._Cn_act) * self.Cn.getNumberParams()) ##################### # Cached ##################### def Sg(self): return self._Sg def Ug(self): return self._Ug def Vg(self): return self._Vg @cached('row_cov') def trSg(self): return self.Sg().sum() @cached('row_cov') def logdetSg(self): return sp.log(self.Sg()).sum() @cached('row_cov') def Wr(self): return self.Ug().T @cached('col_cov') def Lc(self): return self.Cn.USi2().T @cached def Lr(self): return self.eye(N) @cached('col_cov') def Estar(self): E = self.Cr.X # for a general covariance matrix # E = LA.chol(C.K()) # or based on eigh decomposition if this fails return sp.dot(self.Lc(), E) @cached('col_cov') def Se(self): Ue, Seh, Ve = nla.svd(self.Estar(), full_matrices=0) self.fill_cache('Ue',Ue) self.fill_cache('Ve',Ve) return Seh**2 @cached('col_cov') def Ue(self): Ue, Seh, Ve = nla.svd(self.Estar(), full_matrices=0) self.fill_cache('Se',Seh**2) self.fill_cache('Ve',Ve) return Ue @cached('col_cov') def Ve(self): Ue, Seh, Ve = nla.svd(self.Estar(), full_matrices=0) self.fill_cache('Ue',Ue) self.fill_cache('Se',Seh**2) return Ve @cached('col_cov') def Wc(self): return self.Ue().T @cached(['row_cov', 'col_cov']) def SpI(self): return sp.kron(1./self.Se(), 1./self.Sg()) + 1 @cached(['row_cov', 'col_cov']) def d(self): return 1./self.SpI() @cached(['row_cov', 'col_cov']) def D(self): return self.d().reshape((self.rank_r, self.rank_c), order = 'F') @cached(['col_cov']) def Ctilde(self, i): if i < self.Cr.getNumberParams(): C = self.Cr.K_grad_i(i) else: _i = i - self.Cr.getNumberParams() C = self.Cn.K_grad_i(_i) return sp.dot(self.Lc(), sp.dot(C, self.Lc().T)) @cached(['col_cov']) def LcCtildeLc(self, i): return sp.dot(self.Lc().T, sp.dot(self.Ctilde(i), self.Lc())) @cached(['col_cov']) def Cbar(self, i): return sp.dot(self.Wc(), sp.dot(self.Ctilde(i), self.Wc().T)) ##################### # Debug quantities ##################### def L(self): if self.dim > _MAX_DIM: raise TooExpensiveOperationError(msg_too_expensive_dim(my_name(), _MAX_DIM)) return sp.kron(self.Lc(), sp.eye(self.dim_r)) def W(self): return sp.dot(self.Wc(), self.Wr()) def R(self): if self.dim > _MAX_DIM: raise TooExpensiveOperationError(msg_too_expensive_dim(my_name(), _MAX_DIM)) return sp.dot(self.G, self.G.T) ##################### # Overwritten covar_base methods ##################### @cached(['row_cov', 'col_cov', 'covar_base']) def K(self): if self.dim > _MAX_DIM: raise TooExpensiveOperationError(msg_too_expensive_dim(my_name(), _MAX_DIM)) rv = sp.kron(self.Cr.K(), self.R()) + sp.kron(self.Cn.K(), sp.eye(self.dim_r)) return rv @cached(['row_cov', 'col_cov', 'covar_base']) def K_grad_i(self,i): n = self.getNumberParams() if i >= n: raise ValueError("Trying to retrieve the gradient over a " "parameter that is inactive.") if self.dim > _MAX_DIM: raise TooExpensiveOperationError(msg_too_expensive_dim(my_name(), _MAX_DIM)) i = self._actindex2index(i) nCr = self.Cr.getNumberParams() if i < nCr: rv= sp.kron(self.Cr.K_grad_i(i), self.R()) else: _i = i - nCr rv = sp.kron(self.Cn.K_grad_i(_i), sp.eye(self.dim_r)) return rv @cached(['row_cov', 'col_cov', 'covar_base']) def logdet(self): rv = sp.sum(sp.log(self.Cn.S())) * self.dim_r rv+= sp.log(self.SpI()).sum() rv+= sp.log(self.Se()).sum() * self.rank_r rv+= self.logdetSg() * self.rank_c return rv @cached(['row_cov', 'col_cov', 'covar_base']) def logdet_grad_i(self,i): if i >= self.getNumberParams(): raise ValueError("Trying to retrieve the gradient over a " "parameter that is inactive.") i = self._actindex2index(i) if i < self.Cr.getNumberParams(): trR = self.trSg() diagR = self.Sg() else: trR = self.dim_r diagR = sp.ones(self.rank_r) rv = self.Ctilde(i).diagonal().sum() * trR rv-= (self.d() * sp.kron(sp.diag(self.Cbar(i)), diagR)).sum() return rv
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