def __init__( self, metric, max_iter=32, epsilon=EPSILON, point_type=None, method="default", lr=1.0, verbose=False, ): self.metric = metric self.max_iter = max_iter self.epsilon = epsilon self.point_type = point_type self.method = method self.lr = lr self.verbose = verbose self.estimate_ = None if point_type is None: self.point_type = metric.default_point_type error.check_parameter_accepted_values( self.point_type, "point_type", ["vector", "matrix"] )
def __new__(cls, *args, default_coords_type='extrinsic', **kwargs): """Instantiate class that corresponds to the default_coords_type.""" errors.check_parameter_accepted_values( default_coords_type, 'default_coords_type', ['extrinsic', 'ball', 'half-space']) if default_coords_type == 'extrinsic': return Hyperboloid(*args, **kwargs) if default_coords_type == 'ball': return PoincareBall(*args, **kwargs) return PoincareHalfSpace(*args, **kwargs)
def __new__(cls, *args, default_coords_type="extrinsic", **kwargs): """Instantiate class that corresponds to the default_coords_type.""" errors.check_parameter_accepted_values( default_coords_type, "default_coords_type", ["extrinsic", "ball", "half-space"], ) if default_coords_type == "extrinsic": return Hyperboloid(*args, **kwargs) if default_coords_type == "ball": return PoincareBall(*args, **kwargs) return PoincareHalfSpace(*args, **kwargs)
def fit(self, X, y=None, weights=None): """Compute the empirical Frechet mean. Parameters ---------- X : {array-like, sparse matrix}, shape=[..., n_features] Training input samples. y : array-like, shape=[...,] or [..., n_outputs] Target values (class labels in classification, real numbers in regression). Ignored. weights : array-like, shape=[...,] Weights associated to the points. Optional, default: None. Returns ------- self : object Returns self. """ is_linear_metric = isinstance( self.metric, (EuclideanMetric, MatricesMetric, MinkowskiMetric)) error.check_parameter_accepted_values( self.method, 'method', ['default', 'adaptive', 'frechet-poincare-ball']) if is_linear_metric: mean = linear_mean( points=X, weights=weights, point_type=self.point_type) elif self.method == 'default': mean = _default_gradient_descent( points=X, weights=weights, metric=self.metric, max_iter=self.max_iter, initial_step_size=self.lr, point_type=self.point_type, epsilon=self.epsilon, verbose=self.verbose) elif self.method == 'adaptive': mean = _adaptive_gradient_descent( points=X, weights=weights, metric=self.metric, max_iter=self.max_iter, point_type=self.point_type, epsilon=self.epsilon, verbose=self.verbose, initial_tau=self.lr) elif self.method == 'frechet-poincare-ball': mean = _ball_gradient_descent( points=X, weights=weights, metric=self.metric, lr=self.lr, tau=self.epsilon, max_iter=self.max_iter) self.estimate_ = mean return self
def tangent_translation_map(self, point, left_or_right="left", inverse=False): r"""Compute the push-forward map by the left/right translation. Compute the push-forward map, of the left/right translation by the point. It corresponds to the tangent map, or differential of the group multiplication by the point or its inverse. For groups with a vector representation, it is only implemented at identity, but it can be used at other points by passing `inverse=True`. This method wraps the jacobian translation which actually computes the matrix representation of the map. Parameters ---------- point : array-like, shape=[..., {dim, [n, n]] Point. left_or_right : str, {'left', 'right'} Whether to calculate the differential of left or right translations. Optional, default: 'left' inverse : bool, Whether to inverse the jacobian matrix. If True, the push forward by the translation by the inverse of point is returned. Optional, default: False. Returns ------- tangent_map : callable Tangent map of the left/right translation by point. It can be applied to tangent vectors. """ errors.check_parameter_accepted_values(left_or_right, "left_or_right", ["left", "right"]) if self.default_point_type == "matrix": if inverse: point = self.inverse(point) if left_or_right == "left": return lambda tangent_vec: Matrices.mul(point, tangent_vec) return lambda tangent_vec: Matrices.mul(tangent_vec, point) jacobian = self.jacobian_translation(point, left_or_right) if inverse: jacobian = gs.linalg.inv(jacobian) return lambda tangent_vec: gs.einsum("...ij,...j->...i", jacobian, tangent_vec)
def integrate(function, initial_state, end_time=1.0, n_steps=10, step='euler'): """Compute the flow under the vector field using symplectic euler. Integration function to compute flows of vector fields on a regular grid between 0 and a finite time from an initial state. Parameters ---------- function : callable Vector field to integrate. initial_state : tuple of arrays Initial position and speed. end_time : float Final integration time. Optional, default : 1. n_steps : int Number of integration steps to use. Optional, default : 10. step : str, {'euler', 'rk4', 'group_rk2', 'group_rk4'} Numerical scheme to use for elementary integration steps. Optional, default : 'euler'. Returns ------- final_state : tuple sequences of solutions every end_time / n_steps. The shape of each element of the sequence is the same as the vectors passed in initial_state. """ check_parameter_accepted_values(step, 'step', STEP_FUNCTIONS) dt = end_time / n_steps positions = [initial_state[0]] velocities = [initial_state[1]] current_state = (positions[0], velocities[0]) step_function = globals()[STEP_FUNCTIONS[step]] for _ in range(n_steps): current_state = step_function(state=current_state, force=function, dt=dt) positions.append(current_state[0]) velocities.append(current_state[1]) return positions, velocities
def __init__(self, metric, max_iter=32, epsilon=EPSILON, point_type=None, method='default', verbose=False): self.metric = metric self.max_iter = max_iter self.epsilon = epsilon self.point_type = point_type self.method = method self.verbose = verbose self.estimate_ = None if point_type is None: self.point_type = metric.default_point_type error.check_parameter_accepted_values(self.point_type, 'point_type', ['vector', 'matrix'])
def fit(self, X, y=None, weights=None): """Compute the empirical Frechet mean. Parameters ---------- X : {array-like, sparse matrix}, shape=[..., {dim, [n, n]}] Training input samples. y : array-like, shape=[...,] or [..., n_outputs] Target values (class labels in classification, real numbers in regression). Ignored. weights : array-like, shape=[...,] Weights associated to the points. Optional, default: None. Returns ------- self : object Returns self. """ metric_str = self.metric.__str__() is_linear_metric = ( "EuclideanMetric" in metric_str or "MatricesMetric" in metric_str or "MinkowskiMetric" in metric_str ) if "HypersphereMetric" in metric_str and self.metric.dim == 1: mean = Hypersphere.angle_to_extrinsic(_circle_mean(X)) error.check_parameter_accepted_values( self.method, "method", ["default", "adaptive", "batch"] ) if is_linear_metric: mean = linear_mean(points=X, weights=weights, point_type=self.point_type) elif self.method == "default": mean = _default_gradient_descent( points=X, weights=weights, metric=self.metric, max_iter=self.max_iter, initial_step_size=self.lr, point_type=self.point_type, epsilon=self.epsilon, verbose=self.verbose, ) elif self.method == "adaptive": mean = _adaptive_gradient_descent( points=X, weights=weights, metric=self.metric, max_iter=self.max_iter, point_type=self.point_type, epsilon=self.epsilon, verbose=self.verbose, initial_tau=self.lr, ) elif self.method == "batch": mean = _batch_gradient_descent( points=X, weights=weights, metric=self.metric, lr=self.lr, epsilon=self.epsilon, max_iter=self.max_iter, point_type=self.point_type, verbose=self.verbose, ) self.estimate_ = mean return self