Esempio n. 1
0
 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()
Esempio n. 2
0
    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()
Esempio n. 3
0
    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()
Esempio n. 4
0
 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()
Esempio n. 5
0
 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()
Esempio n. 6
0
 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()
Esempio n. 7
0
 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()
Esempio n. 9
0
 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()
Esempio n. 10
0
 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()
Esempio n. 11
0
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
Esempio n. 12
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
Esempio n. 13
0
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
Esempio n. 14
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)
Esempio n. 15
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)
Esempio n. 16
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
Esempio n. 17
0
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