def _default_gradient_descent(points, metric, weights, max_iter, point_type, epsilon, verbose): """Perform default gradient descent.""" if point_type == 'vector': points = gs.to_ndarray(points, to_ndim=2) einsum_str = 'n,nj->j' if point_type == 'matrix': points = gs.to_ndarray(points, to_ndim=3) einsum_str = 'n,nij->ij' n_points = gs.shape(points)[0] if weights is None: weights = gs.ones((n_points, )) mean = points[0] if n_points == 1: return mean sum_weights = gs.sum(weights) sq_dists_between_iterates = [] iteration = 0 sq_dist = 0. var = 0. while iteration < max_iter: var_is_0 = gs.isclose(var, 0.) sq_dist_is_small = gs.less_equal(sq_dist, epsilon * var) condition = ~gs.logical_or(var_is_0, sq_dist_is_small) if not (condition or iteration == 0): break logs = metric.log(point=points, base_point=mean) tangent_mean = gs.einsum(einsum_str, weights, logs) tangent_mean /= sum_weights estimate_next = metric.exp(tangent_vec=tangent_mean, base_point=mean) sq_dist = metric.squared_dist(estimate_next, mean) sq_dists_between_iterates.append(sq_dist) var = variance(points=points, weights=weights, metric=metric, base_point=estimate_next, point_type=point_type) mean = estimate_next iteration += 1 if iteration == max_iter: logging.warning('Maximum number of iterations {} reached. ' 'The mean may be inaccurate'.format(max_iter)) if verbose: logging.info('n_iter: {}, final variance: {}, final dist: {}'.format( iteration, var, sq_dist)) return mean
def projection(self, point, atol=gs.atol): """Project a point in ambient space to the parameter set. The parameter is floored to `gs.atol` if it is negative and to '1-gs.atol' if it is greater than 1. Parameters ---------- point : array-like, shape=[...,] Point in ambient space. atol : float Tolerance to evaluate positivity. Returns ------- projected : array-like, shape=[...,] Projected point. """ point = gs.cast(gs.array(point), dtype=gs.float32) projected = gs.where( gs.logical_or(point < atol, point > 1 - atol), (1 - atol) * gs.cast( (point > 1 - atol), gs.float32) + atol * gs.cast( (point < atol), gs.float32), point, ) return gs.squeeze(projected)
def while_loop_cond(iteration, mean, var, sq_dist): result = ~gs.logical_or(gs.isclose(var, 0.), gs.less_equal(sq_dist, epsilon * var)) return result[0, 0] or iteration == 0
def _default_gradient_descent(points, metric, weights, max_iter, point_type, epsilon, initial_step_size, verbose): """Perform default gradient descent.""" if point_type == 'vector': points = gs.to_ndarray(points, to_ndim=2) einsum_str = 'n,nj->j' else: points = gs.to_ndarray(points, to_ndim=3) einsum_str = 'n,nij->ij' n_points = gs.shape(points)[0] if weights is None: weights = gs.ones((n_points, )) mean = points[0] if n_points == 1: return mean sum_weights = gs.sum(weights) sq_dists_between_iterates = [] iteration = 0 sq_dist = 0. var = 0. norm_old = gs.linalg.norm(points) step = initial_step_size while iteration < max_iter: logs = metric.log(point=points, base_point=mean) var = gs.sum( metric.squared_norm(logs, mean) * weights) / gs.sum(weights) tangent_mean = gs.einsum(einsum_str, weights, logs) tangent_mean /= sum_weights norm = gs.linalg.norm(tangent_mean) sq_dist = metric.squared_norm(tangent_mean, mean) sq_dists_between_iterates.append(sq_dist) var_is_0 = gs.isclose(var, 0.) sq_dist_is_small = gs.less_equal(sq_dist, epsilon * metric.dim) condition = ~gs.logical_or(var_is_0, sq_dist_is_small) if not (condition or iteration == 0): break estimate_next = metric.exp(step * tangent_mean, mean) mean = estimate_next iteration += 1 if norm < norm_old: norm_old = norm elif norm > norm_old: step = step / 2. if iteration == max_iter: logging.warning('Maximum number of iterations {} reached. ' 'The mean may be inaccurate'.format(max_iter)) if verbose: logging.info('n_iter: {}, final variance: {}, final dist: {}'.format( iteration, var, sq_dist)) return mean