def rotate(self, R, inv=None, logdet=None): if inv is not None: invR = inv else: invR = np.linalg.inv(R) if logdet is not None: logdetR = logdet else: logdetR = np.linalg.slogdet(R)[1] # It would be more efficient and simpler, if you just rotated the # moments and didn't touch phi. However, then you would need to call # update() before lower_bound_contribution. This is more error-safe. # Transform parameters self.phi[0] = linalg.mvdot(invR.T, self.phi[0]) self.phi[1] = linalg.dot(invR.T, self.phi[1], invR) self.phi[2] = linalg.dot(invR.T, self.phi[2], invR) N = self.dims[0][0] if False: self._update_moments_and_cgf() else: # Transform moments and g u0 = linalg.mvdot(R, self.u[0]) u1 = linalg.dot(R, self.u[1], R.T) u2 = linalg.dot(R, self.u[2], R.T) self.u = [u0, u1, u2] self.g -= N*logdetR
def rotate(self, R, inv=None, logdet=None): if inv is not None: invR = inv else: invR = np.linalg.inv(R) if logdet is not None: logdetR = logdet else: logdetR = np.linalg.slogdet(R)[1] # It would be more efficient and simpler, if you just rotated the # moments and didn't touch phi. However, then you would need to call # update() before lower_bound_contribution. This is more error-safe. #print('rotate debug in gmc', self.phi[0]) #print(R, invR, np.shape(self.phi[0])) # Transform parameters self.phi[0] = mvdot(invR.T, self.phi[0]) self.phi[1] = dot(invR.T, self.phi[1], invR) self.phi[2] = dot(invR.T, self.phi[2], invR) N = self.dims[0][0] if False: #print(self.phi[0]) self._update_moments_and_cgf() else: # Transform moments and g u0 = mvdot(R, self.u[0]) u1 = dot(R, self.u[1], R.T) u2 = dot(R, self.u[2], R.T) self.u = [u0, u1, u2] self.g -= N * logdetR
def rotate(self, R, inv=None, logdet=None, Q=None): raise NotImplementedError() if inv is not None: invR = inv else: invR = np.linalg.inv(R) if logdet is not None: logdetR = logdet else: logdetR = np.linalg.slogdet(R)[1] # It would be more efficient and simpler, if you just rotated the # moments and didn't touch phi. However, then you would need to call # update() before lower_bound_contribution. This is more error-safe. # Rotate plates, if plate rotation matrix is given. Assume that there's # only one plate-axis #logdet_old = np.sum(utils.linalg.logdet_cov(-2*self.phi[1])) if Q is not None: # Rotate moments using Q self.u[0] = np.einsum('ik,kj->ij', Q, self.u[0]) sumQ = np.sum(Q, axis=0) # Rotate natural parameters using Q self.phi[1] = np.einsum('d,dij->dij', sumQ**(-2), self.phi[1]) self.phi[0] = np.einsum('dij,dj->di', -2*self.phi[1], self.u[0]) # Transform parameters using R self.phi[0] = mvdot(invR.T, self.phi[0]) self.phi[1] = dot(invR.T, self.phi[1], invR) if Q is not None: self._update_moments_and_cgf() else: # Transform moments and g using R self.u[0] = mvdot(R, self.u[0]) self.u[1] = dot(R, self.u[1], R.T) self.g -= logdetR
def rotate(self, R, inv=None, logdet=None, Q=None): if inv is not None: invR = inv else: invR = np.linalg.inv(R) if logdet is not None: logdetR = logdet else: logdetR = np.linalg.slogdet(R)[1] # It would be more efficient and simpler, if you just rotated the # moments and didn't touch phi. However, then you would need to call # update() before lower_bound_contribution. This is more error-safe. # Rotate plates, if plate rotation matrix is given. Assume that there's # only one plate-axis #logdet_old = np.sum(utils.linalg.logdet_cov(-2*self.phi[1])) if Q is not None: # Rotate moments using Q self.u[0] = np.einsum('ik,kj->ij', Q, self.u[0]) sumQ = np.sum(Q, axis=0) # Rotate natural parameters using Q self.phi[1] = np.einsum('d,dij->dij', sumQ**(-2), self.phi[1]) self.phi[0] = np.einsum('dij,dj->di', -2 * self.phi[1], self.u[0]) # Transform parameters using R self.phi[0] = mvdot(invR.T, self.phi[0]) self.phi[1] = dot(invR.T, self.phi[1], invR) if Q is not None: self._update_moments_and_cgf() else: # Transform moments and g using R self.u[0] = mvdot(R, self.u[0]) self.u[1] = dot(R, self.u[1], R.T) self.g -= logdetR
def _compute_message_to_parent(self, index, m, *u_parents): """ Compute the message to a parent node. .. math:: (\sum_i \mathbf{x}_i)^T \mathbf{M}_2 (\sum_j \mathbf{x}_j) + (\sum_i \mathbf{x}_i)^T \mathbf{m}_1 Moments of the parents are .. math:: u_1^{(i)} = \langle \mathbf{x}_i \rangle \\ u_2^{(i)} = \langle \mathbf{x}_i \mathbf{x}_i^T \rangle Thus, the message for :math:`i`-th parent is .. math:: \phi_{x_i}^{(1)} = \mathbf{m}_1 + 2 \mathbf{M}_2 \sum_{j\neq i} \mathbf{x}_j \\ \phi_{x_i}^{(2)} = \mathbf{M}_2 """ # Remove the moments of the parent that receives the message u_parents = u_parents[:index] + u_parents[(index + 1):] m0 = (m[0] + linalg.mvdot(2 * m[1], functools.reduce(np.add, (u_parent[0] for u_parent in u_parents)), ndim=self.ndim)) m1 = m[1] return [m0, m1]
def _compute_message_to_parent(self, index, m, *u_parents): """ Compute the message to a parent node. .. math:: (\sum_i \mathbf{x}_i)^T \mathbf{M}_2 (\sum_j \mathbf{x}_j) + (\sum_i \mathbf{x}_i)^T \mathbf{m}_1 Moments of the parents are .. math:: u_1^{(i)} = \langle \mathbf{x}_i \rangle \\ u_2^{(i)} = \langle \mathbf{x}_i \mathbf{x}_i^T \rangle Thus, the message for :math:`i`-th parent is .. math:: \phi_{x_i}^{(1)} = \mathbf{m}_1 + 2 \mathbf{M}_2 \sum_{j\neq i} \mathbf{x}_j \\ \phi_{x_i}^{(2)} = \mathbf{M}_2 """ # Remove the moments of the parent that receives the message u_parents = u_parents[:index] + u_parents[(index+1):] m0 = (m[0] + linalg.mvdot( 2*m[1], functools.reduce(np.add, (u_parent[0] for u_parent in u_parents)), ndim=self.ndim)) m1 = m[1] return [m0, m1]