def test_frechet_mean(): # TODO: test suite and split in several tests """Testing frechet_mean function""" n = random.randint(3, 50) shape = random.randint(1, 50) spds = [] for k in xrange(n): eig_min = random.uniform(1e-7, 1.) eig_max = random.uniform(1., 1e7) spds.append(my_mfd.random_spd(shape, eig_min, eig_max)) fre = my_mfd.frechet_mean(spds) # Generic assert_tuple_equal(fre.shape, spds[0].shape) assert_array_almost_equal(fre, fre.T) assert_array_less(0., linalg.eigvals(fre)) # Check error for non spd enteries mat1 = np.ones((shape, shape)) with assert_raises_regexp(ValueError, "at least one matrix is not real " +\ "spd"): my_mfd.frechet_mean([mat1]) # Check warning if gradient norm in the last step is less than tolerance for (tol, max_iter) in [(1e-10, 1), (1e-3, 50)]: with warnings.catch_warnings(record=True) as w: fre = my_mfd.frechet_mean(spds, max_iter=max_iter, tol=tol) grad_norm = my_mfd.grad_frechet_mean(spds, max_iter=max_iter, tol=tol) if grad_norm[-1] > tol: assert_equal(len(w), 2) assert_equal(len(grad_norm), max_iter) # Gradient norm is decreasing assert_array_less(np.diff(grad_norm), 0.) decimal = 5 tol = 10 ** (-2 * decimal) fre = my_mfd.frechet_mean(spds, tol=tol) # Adaptative version requires less iterations than non adaptaive grad_norm = my_mfd.grad_frechet_mean(spds, tol=tol) grad_norm_fast = my_mfd.grad_frechet_mean(spds, tol=tol, adaptative=True) assert(len(grad_norm_fast) == len(grad_norm)) # Invariance under reordering spds.reverse() spds.insert(0, spds[2]) spds.pop(3) fre_new = my_mfd.frechet_mean(spds, tol=tol) assert_array_almost_equal(fre_new, fre) # Invariance under congruant transformation c = my_mfd.random_non_singular(shape) spds_cong = [c.dot(spd).dot(c.T) for spd in spds] fre_new = my_mfd.frechet_mean(spds_cong, tol=tol) assert_array_almost_equal(fre_new, c.dot(fre).dot(c.T)) # Invariance under inversion spds_inv = [linalg.inv(spd) for spd in spds] fre_new = my_mfd.frechet_mean(spds_inv, tol=tol) assert_array_almost_equal(fre_new, linalg.inv(fre), decimal=decimal) # Approximate Frechet mean is close to the exact one decimal = 7 tol = 0.5 * 10 ** (-decimal) # Diagonal matrices: exact Frechet mean is geometric mean diags = [] for k in xrange(n): diags.append(my_mfd.random_diagonal_spd(shape, 1e-5, 1e5)) exact_fre = np.prod(my_mfd.my_stack(diags), axis=0) ** \ (1 / float(len(diags))) app_fre = my_mfd.frechet_mean(diags, max_iter=10, tol=tol) assert_array_almost_equal(app_fre, exact_fre, decimal) # 2 matrices spd1 = my_mfd.random_spd(shape) spd2 = my_mfd.random_spd(shape) spd2_sqrt = my_mfd.sqrtm(spd2) spd2_inv_sqrt = my_mfd.inv_sqrtm(spd2) exact_fre = spd2_sqrt.dot( my_mfd.sqrtm(spd2_inv_sqrt.dot(spd1).dot(spd2_inv_sqrt))).dot(spd2_sqrt) app_fre = my_mfd.frechet_mean([spd1, spd2], tol=tol) assert_array_almost_equal(app_fre, exact_fre, decimal) # Single geodesic matrices : TODO S = np.random.rand(shape, shape) S = S + S.T times = np.empty(n, dtype=float) spds = [] for k in xrange(n): t_k = random.uniform(-2., -1.) times[k] = t_k spds.append(c.dot(my_mfd.expm(t_k * S)).dot(c.T))
def test_random_diagonal_spd(): """Testing random_diagonal_spd function""" d = my_mfd.random_diagonal_spd(15) diag = np.diag(d) assert_array_almost_equal(d, np.diag(diag)) assert_array_less(0.0, diag)