def test_multilogm_complex_positive_definite(self): shape = (self.k, self.m, self.m) A = np.random.normal(size=shape) + 1j * np.random.normal(size=shape) A = A @ multihconj(A) # Compare fast path for positive definite matrices vs. general slow # one. np_testing.assert_allclose( multilogm(A, positive_definite=True), multilogm(A, positive_definite=False), )
def test_dist(self): # n = self.n manifold = self.manifold x = manifold.random_point() y = manifold.random_point() z = manifold.random_point() # Test separability np_testing.assert_almost_equal(manifold.dist(x, x), 0.0) # Test symmetry np_testing.assert_almost_equal( manifold.dist(x, y), manifold.dist(y, x) ) # Test triangle inequality assert manifold.dist(x, y) <= manifold.dist(x, z) + manifold.dist(z, y) # Test exponential metric increasing property # (see equation (6.8) in [Bha2007]). logx, logy = multilogm(x), multilogm(y) assert manifold.dist(x, y) >= np.linalg.norm(logx - logy) # check that dist is consistent with log np_testing.assert_almost_equal( manifold.dist(x, y), manifold.norm(x, manifold.log(x, y)) ) # Test invariance under inversion np_testing.assert_almost_equal( manifold.dist(x, y), manifold.dist(np.linalg.inv(y), np.linalg.inv(x)), ) # Test congruence-invariance a = np.random.normal(size=(self.n, self.n)) # must be invertible axa = a @ x @ multitransp(a) aya = a @ y @ multitransp(a) np_testing.assert_almost_equal( manifold.dist(x, y), manifold.dist(axa, aya) ) # Test proportionality (see equation (6.12) in [Bha2007]). alpha = np.random.uniform() np_testing.assert_almost_equal( manifold.dist(x, geodesic(x, y, alpha)), alpha * manifold.dist(x, y), )
def test_multilogm_singlemat(self): a = np.diag(np.random.uniform(size=self.m)) q, _ = np.linalg.qr(np.random.normal(size=(self.m, self.m))) # A is a positive definite matrix A = q @ a @ q.T np_testing.assert_allclose(multilogm(A, positive_definite=True), logm(A))
def dist(self, point_a, point_b): c = np.linalg.cholesky(point_a) c_inv = np.linalg.inv(c) logm = multilogm( c_inv @ point_b @ multitransp(c_inv), positive_definite=True, ) return np.linalg.norm(logm)
def test_multilogm(self): A = np.zeros((self.k, self.m, self.m)) L = np.zeros((self.k, self.m, self.m)) for i in range(self.k): a = np.diag(np.random.uniform(size=self.m)) q, _ = np.linalg.qr(np.random.normal(size=(self.m, self.m))) A[i] = q @ a @ q.T L[i] = logm(A[i]) np_testing.assert_allclose(multilogm(A, positive_definite=True), L)
def geodesic(point_a, point_b, alpha): if alpha < 0 or 1 < alpha: raise ValueError("Exponent must be in [0,1]") c = np.linalg.cholesky(point_a) c_inv = np.linalg.inv(c) log_cbc = multilogm( c_inv @ point_b @ multitransp(c_inv), positive_definite=True, ) powm = multiexpm(alpha * log_cbc, symmetric=False) return c @ powm @ multitransp(c)
def log(self, point_a, point_b): return multiskew(multilogm(multitransp(point_a) @ point_b))