def test_geometric_mean_couple(): n_features = 7 spd1 = np.ones((n_features, n_features)) spd1 = spd1.dot(spd1) + n_features * np.eye(n_features) spd2 = np.tril(np.ones((n_features, n_features))) spd2 = spd2.dot(spd2.T) vals_spd2, vecs_spd2 = np.linalg.eigh(spd2) spd2_sqrt = _form_symmetric(np.sqrt, vals_spd2, vecs_spd2) spd2_inv_sqrt = _form_symmetric(np.sqrt, 1. / vals_spd2, vecs_spd2) geo = spd2_sqrt.dot(_map_eigenvalues(np.sqrt, spd2_inv_sqrt.dot(spd1).dot( spd2_inv_sqrt))).dot(spd2_sqrt) assert_array_almost_equal(_geometric_mean([spd1, spd2]), geo)
def grad_geometric_mean(mats, init=None, max_iter=10, tol=1e-7): """Return the norm of the covariant derivative at each iteration step of geometric_mean. See its docstring for details. Norm is intrinsic norm on the tangent space of the manifold of symmetric positive definite matrices. Returns ------- grad_norm : list of float Norm of the covariant derivative in the tangent space at each step. """ mats = np.array(mats) # Initialization if init is None: gmean = np.mean(mats, axis=0) else: gmean = init norm_old = np.inf step = 1. grad_norm = [] for n in range(max_iter): # Computation of the gradient vals_gmean, vecs_gmean = linalg.eigh(gmean) gmean_inv_sqrt = _form_symmetric(np.sqrt, 1. / vals_gmean, vecs_gmean) whitened_mats = [ gmean_inv_sqrt.dot(mat).dot(gmean_inv_sqrt) for mat in mats ] logs = [_map_eigenvalues(np.log, w_mat) for w_mat in whitened_mats] logs_mean = np.mean(logs, axis=0) # Covariant derivative is # - gmean.dot(logms_mean) norm = np.linalg.norm(logs_mean) # Norm of the covariant derivative on # the tangent space at point gmean # Update of the minimizer vals_log, vecs_log = linalg.eigh(logs_mean) gmean_sqrt = _form_symmetric(np.sqrt, vals_gmean, vecs_gmean) gmean = gmean_sqrt.dot( _form_symmetric(np.exp, vals_log * step, vecs_log)).dot(gmean_sqrt) # Update the norm and the step size if norm < norm_old: norm_old = norm if norm > norm_old: step = step / 2. norm = norm_old grad_norm.append(norm / gmean.size) if tol is not None and norm / gmean.size < tol: break return grad_norm
def grad_geometric_mean(mats, init=None, max_iter=10, tol=1e-7): """Return the norm of the covariant derivative at each iteration step of geometric_mean. See its docstring for details. Norm is intrinsic norm on the tangent space of the manifold of symmetric positive definite matrices. Returns ------- grad_norm : list of float Norm of the covariant derivative in the tangent space at each step. """ mats = np.array(mats) # Initialization if init is None: gmean = np.mean(mats, axis=0) else: gmean = init norm_old = np.inf step = 1. grad_norm = [] for n in range(max_iter): # Computation of the gradient vals_gmean, vecs_gmean = linalg.eigh(gmean) gmean_inv_sqrt = _form_symmetric(np.sqrt, 1. / vals_gmean, vecs_gmean) whitened_mats = [gmean_inv_sqrt.dot(mat).dot(gmean_inv_sqrt) for mat in mats] logs = [_map_eigenvalues(np.log, w_mat) for w_mat in whitened_mats] logs_mean = np.mean(logs, axis=0) # Covariant derivative is # - gmean.dot(logms_mean) norm = np.linalg.norm(logs_mean) # Norm of the covariant derivative on # the tangent space at point gmean # Update of the minimizer vals_log, vecs_log = linalg.eigh(logs_mean) gmean_sqrt = _form_symmetric(np.sqrt, vals_gmean, vecs_gmean) gmean = gmean_sqrt.dot( _form_symmetric(np.exp, vals_log * step, vecs_log)).dot(gmean_sqrt) # Update the norm and the step size if norm < norm_old: norm_old = norm if norm > norm_old: step = step / 2. norm = norm_old grad_norm.append(norm / gmean.size) if tol is not None and norm / gmean.size < tol: break return grad_norm