glimix_core/cov/_kron2sum.py
def logdet_gradient(self):
        """
        Implements ∂log|K| = Tr[K⁻¹∂K].

        It can be shown that::

            ∂log|K| = diag(D)ᵀdiag(L(∂K)Lᵀ) = diag(D)ᵀ(diag(Lₕ∂C₀Lₕᵀ)⊗diag(LₓGGᵀLₓᵀ)),

        when the derivative is over the parameters of C₀. Similarly,

            ∂log|K| = diag(D)ᵀdiag(L(∂K)Lᵀ) = diag(D)ᵀ(diag(Lₕ∂C₁Lₕᵀ)⊗diag(I)),

        over the parameters of C₁.

        Returns
        -------
        C0 : ndarray
            Derivative of C₀ over its parameters.
        C1 : ndarray
            Derivative of C₁ over its parameters.
        """
        from numpy_sugar.linalg import dotd

        self._init_svd()

        dC0 = self._C0.gradient()["Lu"]
        grad_C0 = zeros_like(self._C0.Lu)
        for i in range(self._C0.Lu.shape[0]):
            t = kron(dotd(self.Lh, dC0[..., i] @ self.Lh.T), self._diag_LxGGLxe)
            grad_C0[i] = (self._De * t).sum()

        dC1 = self._C1.gradient()["Lu"]
        grad_C1 = zeros_like(self._C1.Lu)
        p = self._Sxe.shape[0]
        np = self._G.shape[0] - p
        for i in range(self._C1.Lu.shape[0]):
            t = (dotd(self.Lh, dC1[..., i] @ self.Lh.T) * np).sum()
            t1 = kron(dotd(self.Lh, dC1[..., i] @ self.Lh.T), eye(p))
            t += (self._De * t1).sum()
            grad_C1[i] = t

        return {"C0.Lu": grad_C0, "C1.Lu": grad_C1}
def logdet_gradient(self):
        """
        Implements ∂log|K| = Tr[K⁻¹∂K].

        It can be shown that::

            ∂log|K| = diag(D)ᵀdiag(L(∂K)Lᵀ) = diag(D)ᵀ(diag(Lₕ∂C₀Lₕᵀ)⊗diag(LₓGGᵀLₓᵀ)),

        when the derivative is over the parameters of C₀. Similarly,

            ∂log|K| = diag(D)ᵀdiag(L(∂K)Lᵀ) = diag(D)ᵀ(diag(Lₕ∂C₁Lₕᵀ)⊗diag(I)),

        over the parameters of C₁.

        Returns
        -------
        C0 : ndarray
            Derivative of C₀ over its parameters.
        C1 : ndarray
            Derivative of C₁ over its parameters.
        """
        from numpy_sugar.linalg import dotd

        self._init_svd()

        dC0 = self._C0.gradient()["Lu"]
        grad_C0 = zeros_like(self._C0.Lu)
        for i in range(self._C0.Lu.shape[0]):
            t = kron(dotd(self.Lh, dC0[..., i] @ self.Lh.T), self._diag_LxGGLxe)
            grad_C0[i] = (self._De * t).sum()

        dC1 = self._C1.gradient()["Lu"]
        grad_C1 = zeros_like(self._C1.Lu)
        p = self._Sxe.shape[0]
        np = self._G.shape[0] - p
        for i in range(self._C1.Lu.shape[0]):
            t = (dotd(self.Lh, dC1[..., i] @ self.Lh.T) * np).sum()
            t1 = kron(dotd(self.Lh, dC1[..., i] @ self.Lh.T), eye(p))
            t += (self._De * t1).sum()
            grad_C1[i] = t

        return {"C0.Lu": grad_C0, "C1.Lu": grad_C1}
Implements ∂log|K| = Tr[K⁻¹∂K].

        It can be shown that::

            ∂log|K| = diag(D)ᵀdiag(L(∂K)Lᵀ) = diag(D)ᵀ(diag(Lₕ∂C₀Lₕᵀ)⊗diag(LₓGGᵀLₓᵀ)),

        when the derivative is over the parameters of C₀. Similarly,

            ∂log|K| = diag(D)ᵀdiag(L(∂K)Lᵀ) = diag(D)ᵀ(diag(Lₕ∂C₁Lₕᵀ)⊗diag(I)),

        over the parameters of C₁.

        Returns
        -------
        C0 : ndarray
            Derivative of C₀ over its parameters.
        C1 : ndarray
            Derivative of C₁ over its parameters.