Ejemplo n.º 1
0
def test_types_vector_fix():
    a = Vector([1.0, 2.0])

    assert_(not a.isfixed)

    a.fix()
    assert_(a.isfixed)
Ejemplo n.º 2
0
class FreeFormCov(Function):
    """
    General definite positive matrix, K = LLᵀ + ϵI.

    A d×d covariance matrix K will have ((d+1)⋅d)/2 parameters defining the lower
    triangular elements of a Cholesky matrix L such that:

        K = LLᵀ + ϵI,

    for a very small positive number ϵ. That additional term is necessary to avoid
    singular and ill conditioned covariance matrices.

    Example
    -------

    .. doctest::

        >>> from glimix_core.cov import FreeFormCov
        >>>
        >>> cov = FreeFormCov(2)
        >>> cov.L = [[1., .0], [0.5, 2.]]
        >>> print(cov.gradient()["Lu"])
        [[[0.  2.  0. ]
          [1.  0.5 0. ]]
        <BLANKLINE>
         [[1.  0.5 0. ]
          [1.  0.  8. ]]]
        >>> cov.name = "K"
        >>> print(cov)
        FreeFormCov(dim=2): K
          L: [[1.  0. ]
              [0.5 2. ]]
    """
    def __init__(self, dim):
        """
        Constructor.

        Parameters
        ----------
        dim : int
            Dimension d of the free-form covariance matrix.
        """
        from numpy_sugar import epsilon

        dim = int(dim)
        tsize = ((dim + 1) * dim) // 2
        self._L = zeros((dim, dim))
        self._tril1 = tril_indices_from(self._L, k=-1)
        self._diag = diag_indices_from(self._L)
        self._L[self._tril1] = 1
        self._L[self._diag] = 0
        self._epsilon = epsilon.small * 1000
        self._Lu = Vector(zeros(tsize))
        self._Lu.value[:tsize - dim] = 1
        n = self.L.shape[0]
        self._grad_Lu = zeros((n, n, self._Lu.shape[0]))
        Function.__init__(self, "FreeCov", Lu=self._Lu)
        bounds = [(-inf, +inf)] * (tsize - dim)
        bounds += [(log(epsilon.small * 1000), +11)] * dim
        self._Lu.bounds = bounds
        self._cache = {"eig": None}
        self.listen(self._parameters_update)
        self._nparams = tsize

    def _parameters_update(self):
        self._cache["eig"] = None

    @property
    def nparams(self):
        """
        Number of parameters.
        """
        return self._nparams

    def listen(self, func):
        """
        Listen to parameters change.

        Parameters
        ----------
        func : callable
            Function to be called when a parameter changes.
        """
        self._Lu.listen(func)

    @property
    def shape(self):
        """
        Array shape.
        """
        n = self._L.shape[0]
        return (n, n)

    def fix(self):
        """
        Disable parameter optimisation.
        """
        self._Lu.fix()

    def unfix(self):
        """
        Enable parameter optimisation.
        """
        self._Lu.unfix()

    def eigh(self):
        """
        Eigen decomposition of K.

        Returns
        -------
        S : ndarray
            The eigenvalues in ascending order, each repeated according to its
            multiplicity.
        U : ndarray
            Normalized eigenvectors.
        """
        from numpy.linalg import svd

        if self._cache["eig"] is not None:
            return self._cache["eig"]

        U, S = svd(self.L)[:2]
        S *= S
        S += self._epsilon
        self._cache["eig"] = S, U

        return self._cache["eig"]

    @property
    def Lu(self):
        """
        Lower-triangular, flat part of L.
        """
        return self._Lu.value

    @Lu.setter
    def Lu(self, v):
        self._Lu.value = v

    @property
    def L(self):
        """
        Lower-triangular matrix L such that K = LLᵀ + ϵI.

        Returns
        -------
        L : (d, d) ndarray
            Lower-triangular matrix.
        """
        m = len(self._tril1[0])
        self._L[self._tril1] = self._Lu.value[:m]
        self._L[self._diag] = exp(self._Lu.value[m:])
        return self._L

    @L.setter
    def L(self, value):
        self._L[:] = value
        m = len(self._tril1[0])
        self._Lu.value[:m] = self._L[self._tril1]
        self._Lu.value[m:] = log(self._L[self._diag])

    def logdet(self):
        """
        Log of |K|.

        Returns
        -------
        float
            Log-determinant of K.
        """
        from numpy.linalg import slogdet

        K = self.value()

        sign, logdet = slogdet(K)
        if sign != 1.0:
            msg = "The estimated determinant of K is not positive: "
            msg += f" ({sign}, {logdet})."
            raise RuntimeError(msg)

        return logdet

    def value(self):
        """
        Covariance matrix.

        Returns
        -------
        K : ndarray
            Matrix K = LLᵀ + ϵI, for a very small positive number ϵ.
        """
        K = dot(self.L, self.L.T)
        return K + self._epsilon * eye(K.shape[0])

    def gradient(self):
        """
        Derivative of the covariance matrix over the parameters of L.

        Returns
        -------
        Lu : ndarray
            Derivative of K over the lower triangular part of L.
        """
        L = self.L
        self._grad_Lu[:] = 0

        for i in range(len(self._tril1[0])):
            row = self._tril1[0][i]
            col = self._tril1[1][i]
            self._grad_Lu[row, :, i] = L[:, col]
            self._grad_Lu[:, row, i] += L[:, col]

        m = len(self._tril1[0])
        for i in range(len(self._diag[0])):
            row = self._diag[0][i]
            col = self._diag[1][i]
            self._grad_Lu[row, :, m + i] = L[row, col] * L[:, col]
            self._grad_Lu[:, row, m + i] += L[row, col] * L[:, col]

        return {"Lu": self._grad_Lu}

    def __str__(self):
        return format_function(self, {"dim": self._L.shape[0]},
                               [("L", self.L)])
Ejemplo n.º 3
0
class LRFreeFormCov(Function):
    """
    General semi-definite positive matrix of low rank, K = LLᵀ.

    The covariance matrix K is given by LLᵀ, where L is a n×m matrix and n≥m. Therefore,
    K will have rank(K) ≤ m.

    Example
    -------

    .. doctest::

        >>> from glimix_core.cov import LRFreeFormCov
        >>> cov = LRFreeFormCov(3, 2)
        >>> print(cov.L)
        [[1. 1.]
         [1. 1.]
         [1. 1.]]
        >>> cov.L = [[1, 2], [0, 3], [1, 3]]
        >>> print(cov.L)
        [[1. 2.]
         [0. 3.]
         [1. 3.]]
        >>> cov.name = "F"
        >>> print(cov)
        LRFreeFormCov(n=3, m=2): F
          L: [[1. 2.]
              [0. 3.]
              [1. 3.]]
    """

    def __init__(self, n, m):
        """
        Constructor.

        Parameters
        ----------
        n : int
            Covariance dimension.
        m : int
            Upper limit of the covariance matrix rank.
        """
        self._L = ones((n, m))
        self._Lu = Vector(self._L.ravel())
        Function.__init__(self, "LRFreeFormCov", Lu=self._Lu)

    @property
    def nparams(self):
        """
        Number of parameters.
        """
        return self._L.size

    def listen(self, func):
        """
        Listen to parameters change.

        Parameters
        ----------
        func : callable
            Function to be called when a parameter changes.
        """
        self._Lu.listen(func)

    def fix(self):
        """
        Disable parameter optimisation.
        """
        self._Lu.fix()

    def unfix(self):
        """
        Enable parameter optimisation.
        """
        self._Lu.unfix()

    @property
    def Lu(self):
        """
        Lower-triangular, flat part of L.
        """
        return self._Lu.value

    @Lu.setter
    def Lu(self, v):
        self._Lu.value = v

    @property
    def L(self):
        """
        Matrix L from K = LLᵀ.

        Returns
        -------
        L : (n, m) ndarray
            Parametric matrix.
        """
        return self._L

    @L.setter
    def L(self, value):
        self._Lu.value = asarray(value, float).ravel()

    @property
    def shape(self):
        """
        Array shape.
        """
        n = self._L.shape[0]
        return (n, n)

    def value(self):
        """
        Covariance matrix.

        Returns
        -------
        K : (n, n) ndarray
            K = LLᵀ.
        """
        return dot(self.L, self.L.T)

    def gradient(self):
        """
        Derivative of the covariance matrix over the lower triangular, flat part of L.

        It is equal to

            ∂K/∂Lᵢⱼ = ALᵀ + LAᵀ,

        where Aᵢⱼ is an n×m matrix of zeros except at [Aᵢⱼ]ᵢⱼ=1.

        Returns
        -------
        Lu : ndarray
            Derivative of K over the lower-triangular, flat part of L.
        """
        L = self.L
        n = self.L.shape[0]
        grad = {"Lu": zeros((n, n, n * self._L.shape[1]))}
        for ii in range(self._L.shape[0] * self._L.shape[1]):
            row = ii // self._L.shape[1]
            col = ii % self._L.shape[1]
            grad["Lu"][row, :, ii] = L[:, col]
            grad["Lu"][:, row, ii] += L[:, col]

        return grad

    def __str__(self):
        return format_function(
            self, {"n": self._L.shape[0], "m": self._L.shape[1]}, [("L", self._L)]
        )