def inner_product(self, tangent_vec_a, tangent_vec_b, base_point): """Compute the inner-product of two tangent vectors at a base point. Inner product defined by the Riemannian metric at point `base_point` between tangent vectors `tangent_vec_a` and `tangent_vec_b`. Parameters ---------- tangent_vec_a : array-like, shape=[..., n_copies, *base_shape] First tangent vector at base point. tangent_vec_b : array-like, shape=[..., n_copies, *base_shape] Second tangent vector at base point. base_point : array-like, shape=[..., n_copies, *base_shape] Point on the manifold. Optional, default: None. Returns ------- inner_prod : array-like, shape=[...,] Inner-product of the two tangent vectors. """ tangent_vec_a_, tangent_vec_b_, point_ = gs.broadcast_arrays( tangent_vec_a, tangent_vec_b, base_point ) point_ = gs.reshape(point_, (-1, *self.base_shape)) vector_a = gs.reshape(tangent_vec_a_, (-1, *self.base_shape)) vector_b = gs.reshape(tangent_vec_b_, (-1, *self.base_shape)) inner_each = self.base_metric.inner_product(vector_a, vector_b, point_) reshaped = gs.reshape(inner_each, (-1, self.n_copies)) return gs.squeeze(gs.sum(reshaped, axis=-1))
def to_tangent(self, vector, base_point): """Project a vector to a tangent space of the manifold. The tangent space of the product manifold is the direct sum of tangent spaces. Parameters ---------- vector : array-like, shape=[..., n_copies, *base_shape] Vector. base_point : array-like, shape=[..., n_copies, *base_shape] Point on the manifold. Returns ------- tangent_vec : array-like, shape=[..., n_copies, *base_shape] Tangent vector at base point. """ vector_, point_ = gs.broadcast_arrays(vector, base_point) point_ = gs.reshape(point_, (-1, *self.base_shape)) vector_ = gs.reshape(vector_, (-1, *self.base_shape)) each_tangent = self.base_manifold.to_tangent(vector_, point_) reshaped = gs.reshape(each_tangent, (-1, self.n_copies) + self.base_shape) return gs.squeeze(reshaped)
def square_root_velocity_inverse(self, srv, starting_point): """Retrieve a curve from sqrt velocity rep and starting point. Parameters ---------- srv : starting_point : Returns ------- curve : """ if not isinstance(self.ambient_metric, EuclideanMetric): raise AssertionError('The square root velocity inverse is only ' 'implemented for dicretized curves embedded ' 'in a Euclidean space.') if gs.ndim(srv) != gs.ndim(starting_point): starting_point = gs.transpose(gs.tile(starting_point, (1, 1, 1)), axes=(1, 0, 2)) srv_shape = srv.shape srv = gs.to_ndarray(srv, to_ndim=3) n_curves, n_sampling_points_minus_one, n_coords = srv.shape srv = gs.reshape(srv, (n_curves * n_sampling_points_minus_one, n_coords)) srv_norm = self.ambient_metric.norm(srv) delta_points = 1 / n_sampling_points_minus_one * srv_norm * srv delta_points = gs.reshape(delta_points, srv_shape) curve = gs.concatenate((starting_point, delta_points), -2) curve = gs.cumsum(curve, -2) return curve
def inner_product(self, tangent_vec_a, tangent_vec_b, base_curve): """ Inner product between two tangent vectors at a base curve. """ assert tangent_vec_a.shape == tangent_vec_b.shape assert tangent_vec_a.shape == base_curve.shape tangent_vec_a = gs.to_ndarray(tangent_vec_a, to_ndim=3) tangent_vec_b = gs.to_ndarray(tangent_vec_b, to_ndim=3) base_curve = gs.to_ndarray(base_curve, to_ndim=3) n_curves, n_sampling_points, n_coords = tangent_vec_a.shape new_dim = n_curves * n_sampling_points tangent_vec_a = gs.reshape(tangent_vec_a, (new_dim, n_coords)) tangent_vec_b = gs.reshape(tangent_vec_b, (new_dim, n_coords)) base_curve = gs.reshape(base_curve, (new_dim, n_coords)) inner_prod = self.embedding_metric.inner_product( tangent_vec_a, tangent_vec_b, base_curve) inner_prod = gs.reshape(inner_prod, (n_curves, n_sampling_points)) inner_prod = gs.sum(inner_prod, -1) n_sampling_points_float = gs.array(n_sampling_points) n_sampling_points_float = gs.cast(n_sampling_points_float, gs.float32) inner_prod = inner_prod / n_sampling_points_float inner_prod = gs.to_ndarray(inner_prod, to_ndim=1) inner_prod = gs.to_ndarray(inner_prod, to_ndim=2, axis=1) return inner_prod
def dist(self, landmarks_a, landmarks_b): """Compute geodesic distance between two landmark sets. Parameters ---------- landmarks_a landmarks_b Returns ------- dist """ if landmarks_a.shape != landmarks_b.shape: raise NotImplementedError n_landmark_sets, n_landmarks_per_set, n_coords = landmarks_a.shape landmarks_a = gs.reshape( landmarks_a, (n_landmark_sets * n_landmarks_per_set, n_coords)) landmarks_b = gs.reshape( landmarks_b, (n_landmark_sets * n_landmarks_per_set, n_coords)) dist = self.ambient_metric.dist(landmarks_a, landmarks_b) dist = gs.reshape(dist, (n_landmark_sets, n_landmarks_per_set)) n_landmarks_per_set_float = gs.array(n_landmarks_per_set) n_landmarks_per_set_float = gs.cast(n_landmarks_per_set_float, gs.float32) dist = gs.sqrt(gs.sum(dist**2, -1) / n_landmarks_per_set_float) dist = gs.to_ndarray(dist, to_ndim=1) dist = gs.to_ndarray(dist, to_ndim=2, axis=1) return dist
def jacobian_diffeomorphism(self, base_point): r"""Jacobian of the diffeomorphism at base point. Let :math:`f` be the diffeomorphism :math:`f: M \rightarrow N` of the manifold :math:`M` into the manifold `N`. Parameters ---------- base_point : array-like, shape=[..., *shape] Base point. Returns ------- mat : array-like, shape=[..., *i_shape, *shape] Inner-product matrix. """ rad = base_point.shape[:-len(self.shape)] base_point = gs.reshape(base_point, (-1, ) + self.shape) J = self.raw_jacobian_diffeomorphism(base_point) J = gs.moveaxis( gs.diagonal(J, axis1=0, axis2=len(self.embedding_metric.shape) + 1), -1, 0, ) J = gs.reshape(J, rad + self.embedding_metric.shape + self.shape) return J
def dist(self, landmarks_a, landmarks_b): """ Geodesic distance between two landmark sets. """ assert landmarks_a.shape == landmarks_b.shape landmarks_a = gs.to_ndarray(landmarks_a, to_ndim=3) landmarks_b = gs.to_ndarray(landmarks_b, to_ndim=3) n_landmark_sets, n_landmarks_per_set, n_coords = landmarks_a.shape landmarks_a = gs.reshape( landmarks_a, (n_landmark_sets * n_landmarks_per_set, n_coords)) landmarks_b = gs.reshape( landmarks_b, (n_landmark_sets * n_landmarks_per_set, n_coords)) dist = self.ambient_metric.dist(landmarks_a, landmarks_b) dist = gs.reshape(dist, (n_landmark_sets, n_landmarks_per_set)) n_landmarks_per_set_float = gs.array(n_landmarks_per_set) n_landmarks_per_set_float = gs.cast( n_landmarks_per_set_float, gs.float32) dist = gs.sqrt(gs.sum(dist ** 2, -1) / n_landmarks_per_set_float) dist = gs.to_ndarray(dist, to_ndim=1) dist = gs.to_ndarray(dist, to_ndim=2, axis=1) return dist
def square_root_velocity(self, curve): """Compute the square root velocity representation of a curve. The velocity is computed using the log map. The case of several curves is handled through vectorization. In that case, an index selection procedure allows to get rid of the log between the end point of curve[k, :, :] and the starting point of curve[k + 1, :, :]. Parameters ---------- curve : Returns ------- srv : """ curve = gs.to_ndarray(curve, to_ndim=3) n_curves, n_sampling_points, n_coords = curve.shape srv_shape = (n_curves, n_sampling_points - 1, n_coords) curve = gs.reshape(curve, (n_curves * n_sampling_points, n_coords)) coef = gs.cast(gs.array(n_sampling_points - 1), gs.float32) velocity = coef * self.ambient_metric.log(point=curve[1:, :], base_point=curve[:-1, :]) velocity_norm = self.ambient_metric.norm(velocity, curve[:-1, :]) srv = velocity / gs.sqrt(velocity_norm) index = gs.arange(n_curves * n_sampling_points - 1) mask = ~gs.equal((index + 1) % n_sampling_points, 0) index_select = gs.gather(index, gs.squeeze(gs.where(mask))) srv = gs.reshape(gs.gather(srv, index_select), srv_shape) return srv
def exp(self, tangent_vec, base_landmarks): """Compute Riemannian exponential of tan vector wrt base landmark set. Parameters ---------- tangent_vec base_landmarks Returns ------- exp """ tangent_vec = gs.to_ndarray(tangent_vec, to_ndim=3) base_landmarks = gs.to_ndarray(base_landmarks, to_ndim=3) n_landmark_sets, n_landmarks_per_set, n_coords = base_landmarks.shape n_tangent_vecs = tangent_vec.shape[0] new_dim = n_landmark_sets * n_landmarks_per_set new_base_landmarks = gs.reshape(base_landmarks, (new_dim, n_coords)) new_tangent_vec = gs.reshape(tangent_vec, (new_dim, n_coords)) exp = self.ambient_metric.exp(new_tangent_vec, new_base_landmarks) exp = gs.reshape(exp, (n_tangent_vecs, n_landmarks_per_set, n_coords)) exp = gs.squeeze(exp) return exp
def log(self, point, base_point, **kwargs): """Compute the Riemannian logarithm of a point. Parameters ---------- point : array-like, shape=[..., k_landmarks, m_ambient] Point on the pre-shape space. base_point : array-like, shape=[..., k_landmarks, m_ambient] Point on the pre-shape space. Returns ------- log : array-like, shape=[..., k_landmarks, m_ambient] Tangent vector at the base point equal to the Riemannian logarithm of point at the base point. """ flat_bp = gs.reshape(base_point, (-1, self.sphere_metric.dim + 1)) flat_pt = gs.reshape(point, (-1, self.sphere_metric.dim + 1)) flat_log = self.sphere_metric.log(flat_pt, flat_bp) try: log = gs.reshape(flat_log, base_point.shape) except (RuntimeError, check_tf_error(ValueError, "InvalidArgumentError")): log = gs.reshape(flat_log, point.shape) return log
def log(self, landmarks, base_landmarks): """Compute Riemannian log of a set of landmarks wrt base landmark set. Parameters ---------- landmarks base_landmarks Returns ------- log """ assert landmarks.shape == base_landmarks.shape landmarks = gs.to_ndarray(landmarks, to_ndim=3) base_landmarks = gs.to_ndarray(base_landmarks, to_ndim=3) n_landmark_sets, n_landmarks_per_set, n_coords = landmarks.shape landmarks = gs.reshape( landmarks, (n_landmark_sets * n_landmarks_per_set, n_coords)) base_landmarks = gs.reshape( base_landmarks, (n_landmark_sets * n_landmarks_per_set, n_coords)) log = self.ambient_metric.log(landmarks, base_landmarks) log = gs.reshape(log, (n_landmark_sets, n_landmarks_per_set, n_coords)) log = gs.squeeze(log) return log
def is_tangent(self, vector, base_point, atol=gs.atol): """Check whether the vector is tangent at base_point. The tangent space of the product manifold is the direct sum of tangent spaces. Parameters ---------- vector : array-like, shape=[..., n_copies, *base_shape] Vector. base_point : array-like, shape=[..., n_copies, *base_shape] Point on the manifold. atol : float Absolute tolerance. Optional, default: backend atol. Returns ------- is_tangent : bool Boolean denoting if vector is a tangent vector at the base point. """ vector_, point_ = gs.broadcast_arrays(vector, base_point) point_ = gs.reshape(point_, (-1, *self.base_shape)) vector_ = gs.reshape(vector_, (-1, *self.base_shape)) each_tangent = self.base_manifold.is_tangent(vector_, point_) reshaped = gs.reshape(each_tangent, (-1, self.n_copies)) return gs.all(reshaped, axis=1)
def parallel_transport(self, tangent_vec_a, tangent_vec_b, base_point): """Compute the Riemannian parallel transport of a tangent vector. Parameters ---------- tangent_vec_a : array-like, shape=[..., k_landmarks, m_ambient] Tangent vector at a base point. tangent_vec_b : array-like, shape=[..., k_landmarks, m_ambient] Tangent vector at a base point. base_point : array-like, shape=[..., k_landmarks, m_ambient] Point on the pre-shape space. Returns ------- transported : array-like, shape=[..., k_landmarks, m_ambient] Point on the pre-shape space equal to the Riemannian exponential of tangent_vec at the base point. """ max_shape = (tangent_vec_a.shape if tangent_vec_a.ndim == 3 else tangent_vec_b.shape) flat_bp = gs.reshape(base_point, (-1, self.sphere_metric.dim + 1)) flat_tan_a = gs.reshape(tangent_vec_a, (-1, self.sphere_metric.dim + 1)) flat_tan_b = gs.reshape(tangent_vec_b, (-1, self.sphere_metric.dim + 1)) flat_transport = self.sphere_metric.parallel_transport( flat_tan_a, flat_tan_b, flat_bp) return gs.reshape(flat_transport, max_shape)
def _fit_extrinsic(self, X, y, weights=None, compute_training_score=False): """Estimate the parameters using the extrinsic gradient descent. Estimate the intercept and the coefficient defining the geodesic regression model, using the extrinsic gradient. Parameters ---------- X : {array-like, sparse matrix}, shape=[...,}] Training input samples. y : array-like, shape=[..., {dim, [n,n]}] Training target values. weights : array-like, shape=[...,] Weights associated to the points. Optional, default: None. compute_training_score : bool Whether to compute R^2. Optional, default: False. Returns ------- self : object Returns self. """ shape = ( y.shape[-1:] if self.space.default_point_type == "vector" else y.shape[-2:] ) intercept_init, coef_init = self.initialize_parameters(y) intercept_hat = self.space.projection(intercept_init) coef_hat = self.space.to_tangent(coef_init, intercept_hat) initial_guess = gs.vstack([gs.flatten(intercept_hat), gs.flatten(coef_hat)]) objective_with_grad = gs.autodiff.value_and_grad( lambda param: self._loss(X, y, param, shape, weights), to_numpy=True ) res = minimize( objective_with_grad, initial_guess, method="CG", jac=True, options={"disp": self.verbose, "maxiter": self.max_iter}, tol=self.tol, ) intercept_hat, coef_hat = gs.split(gs.array(res.x), 2) intercept_hat = gs.reshape(intercept_hat, shape) intercept_hat = gs.cast(intercept_hat, dtype=y.dtype) coef_hat = gs.reshape(coef_hat, shape) coef_hat = gs.cast(coef_hat, dtype=y.dtype) self.intercept_ = self.space.projection(intercept_hat) self.coef_ = self.space.to_tangent(coef_hat, self.intercept_) if compute_training_score: variance = gs.sum(self.metric.squared_dist(y, self.intercept_)) self.training_score_ = 1 - 2 * res.fun / variance return self
def square_root_velocity(self, curve): """Compute the square root velocity representation of a curve. The velocity is computed using the log map. In the case of several curves, an index selection procedure allows to get rid of the log between the end point of curve[k, :, :] and the starting point of curve[k + 1, :, :]. Parameters ---------- curve : array-like, shape=[..., n_sampling_points, ambient_dim] Discrete curve. Returns ------- srv : array-like, shape=[..., n_sampling_points - 1, ambient_dim] Square-root velocity representation of a discrete curve. """ curve = gs.to_ndarray(curve, to_ndim=3) n_curves, n_sampling_points, n_coords = curve.shape srv_shape = (n_curves, n_sampling_points - 1, n_coords) curve = gs.reshape(curve, (n_curves * n_sampling_points, n_coords)) coef = gs.cast(gs.array(n_sampling_points - 1), gs.float32) velocity = coef * self.ambient_metric.log(point=curve[1:, :], base_point=curve[:-1, :]) velocity_norm = self.ambient_metric.norm(velocity, curve[:-1, :]) srv = gs.einsum( '...i,...->...i', velocity, 1. / gs.sqrt(velocity_norm)) index = gs.arange(n_curves * n_sampling_points - 1) mask = ~((index + 1) % n_sampling_points == 0) srv = gs.reshape(srv[mask], srv_shape) return srv
def log(self, landmarks, base_landmarks): """Compute Riemannian log of a set of landmarks wrt base landmark set. Parameters ---------- landmarks base_landmarks Returns ------- log """ if landmarks.shape != base_landmarks.shape: raise NotImplementedError n_landmark_sets, n_landmarks_per_set, n_coords = landmarks.shape landmarks = gs.reshape( landmarks, (n_landmark_sets * n_landmarks_per_set, n_coords)) base_landmarks = gs.reshape( base_landmarks, (n_landmark_sets * n_landmarks_per_set, n_coords)) log = self.ambient_metric.log(landmarks, base_landmarks) log = gs.reshape(log, (n_landmark_sets, n_landmarks_per_set, n_coords)) log = gs.squeeze(log) return log
def inner_product(self, tangent_vec_a, tangent_vec_b, base_landmarks): """ Inner product between two tangent vectors at a base landmark set. """ assert tangent_vec_a.shape == tangent_vec_b.shape assert tangent_vec_a.shape == base_landmarks.shape tangent_vec_a = gs.to_ndarray(tangent_vec_a, to_ndim=3) tangent_vec_b = gs.to_ndarray(tangent_vec_b, to_ndim=3) base_landmarks = gs.to_ndarray(base_landmarks, to_ndim=3) n_landmark_sets, n_landmarks_per_set, n_coords = tangent_vec_a.shape new_dim = n_landmark_sets * n_landmarks_per_set tangent_vec_a = gs.reshape(tangent_vec_a, (new_dim, n_coords)) tangent_vec_b = gs.reshape(tangent_vec_b, (new_dim, n_coords)) base_landmarks = gs.reshape(base_landmarks, (new_dim, n_coords)) inner_prod = self.ambient_metric.inner_product( tangent_vec_a, tangent_vec_b, base_landmarks) inner_prod = gs.reshape( inner_prod, (n_landmark_sets, n_landmarks_per_set)) inner_prod = gs.sum(inner_prod, -1) n_landmarks_per_set_float = gs.array(n_landmarks_per_set) n_landmarks_per_set_float = gs.cast( n_landmarks_per_set_float, gs.float32) inner_prod = inner_prod / n_landmarks_per_set_float inner_prod = gs.to_ndarray(inner_prod, to_ndim=1) inner_prod = gs.to_ndarray(inner_prod, to_ndim=2, axis=1) return inner_prod
def sample_y(self, X, n_samples=1, random_state=0): """Draw samples from Wrapped Gaussian process and evaluate at X. A fitted Wrapped Gaussian process can be use to sample values through the following steps: - Use the stored Gaussian process regression on the dataset to sample tangent values - Compute the base-points using the prior - Flatten (and repeat if needed) both the base-points and the tangent samples to benefit from vectorized computation. - Map the tangent samples on the manifold via the metric's exp with the flattened and repeated base-points yielded by the prior Parameters ---------- X : array-like of shape (n_samples_X, n_features) or list of object Query points where the WGP is evaluated. n_samples : int, default=1 Number of samples drawn from the Wrapped Gaussian process per query point. random_state : int, RandomState instance or None, default=0 Determines random number generation to randomly draw samples. Pass an int for reproducible results across multiple function calls. Returns ------- y_samples : ndarray of shape (n_samples_X, n_samples), or \ (n_samples_X, n_targets, n_samples) Values of n_samples samples drawn from wrapped Gaussian process and evaluated at query points. """ tangent_samples = self._euclidean_gpr.sample_y(X, n_samples, random_state) tangent_samples = gs.cast(tangent_samples, dtype=X.dtype) # flatten the samples tangent_samples = gs.reshape(gs.transpose(tangent_samples, [0, 2, 1]), (-1, *self.y_train_shape_)) # generate the base_points base_points = self.prior(X) # repeat the base points in order to match the tangent samples base_points = gs.repeat(gs.expand_dims(base_points, 2), n_samples, axis=2) # flatten the base_points base_points = gs.reshape(gs.transpose(base_points, [0, 2, 1]), (-1, *self.y_train_shape_)) # get the flattened samples y_samples = self.metric.exp(tangent_samples, base_point=base_points) y_samples = gs.transpose( gs.reshape(y_samples, (X.shape[0], n_samples, *self.y_train_shape_)), [0, 2, 1], ) return y_samples
def dist_broadcast(self, point_a, point_b): """Compute the geodesic distance between points. If n_samples_a == n_samples_b then dist is the element-wise distance result of a point in points_a with the point from points_b of the same index. If n_samples_a not equal to n_samples_b then dist is the result of applying geodesic distance for each point from points_a to all points from points_b. Parameters ---------- point_a : array-like, shape=[n_samples_a, dim] Set of points in hyperbolic space. point_b : array-like, shape=[n_samples_b, dim] Second set of points in hyperbolic space. Returns ------- dist : array-like, shape=[n_samples_a, dim] or [n_samples_a, n_samples_b, dim] Geodesic distance between the two points. """ if point_a.shape[-1] != point_b.shape[-1]: raise ValueError('Manifold dimensions not equal') if point_a.shape[0] != point_b.shape[0]: point_a_broadcast, point_b_broadcast = gs.broadcast_arrays( point_a[:, None], point_b[None, ...]) point_a_flatten = gs.reshape(point_a_broadcast, (-1, point_a_broadcast.shape[-1])) point_b_flatten = gs.reshape(point_b_broadcast, (-1, point_b_broadcast.shape[-1])) point_a_norm = gs.clip(gs.sum(point_a_flatten**2, -1), 0., 1 - EPSILON) point_b_norm = gs.clip(gs.sum(point_b_flatten**2, -1), 0., 1 - EPSILON) square_diff = (point_a_flatten - point_b_flatten)**2 diff_norm = gs.sum(square_diff, -1) norm_function = 1 + 2 * \ diff_norm / ((1 - point_a_norm) * (1 - point_b_norm)) dist = gs.log(norm_function + gs.sqrt(norm_function**2 - 1)) dist *= self.scale dist = gs.reshape(dist, (point_a.shape[0], point_b.shape[0])) dist = gs.squeeze(dist) elif point_a.shape == point_b.shape: dist = self.dist(point_a, point_b) return dist
def test_loss_minimization_extrinsic_se2(self): gr = GeodesicRegression( self.se2, metric=self.metric_se2, center_X=False, method="extrinsic", max_iter=50, init_step_size=0.1, verbose=True, ) def loss_of_param(param): return gr._loss(self.X_se2, self.y_se2, param, self.shape_se2) objective_with_grad = gs.autodiff.value_and_grad(loss_of_param, to_numpy=True) res = minimize( objective_with_grad, gs.flatten(self.param_se2_guess), method="CG", jac=True, options={ "disp": True, "maxiter": 50 }, ) self.assertAllClose(gs.array(res.x).shape, (18, )) self.assertAllClose(res.fun, 0.0, atol=1e-6) # Cast required because minimization happens in scipy in float64 param_hat = gs.cast(gs.array(res.x), self.param_se2_true.dtype) intercept_hat, coef_hat = gs.split(param_hat, 2) intercept_hat = gs.reshape(intercept_hat, self.shape_se2) coef_hat = gs.reshape(coef_hat, self.shape_se2) intercept_hat = self.se2.projection(intercept_hat) coef_hat = self.se2.to_tangent(coef_hat, intercept_hat) self.assertAllClose(intercept_hat, self.intercept_se2_true, atol=1e-4) tangent_vec_of_transport = self.se2.metric.log( self.intercept_se2_true, base_point=intercept_hat) transported_coef_hat = self.se2.metric.parallel_transport( tangent_vec=coef_hat, base_point=intercept_hat, direction=tangent_vec_of_transport, ) self.assertAllClose(transported_coef_hat, self.coef_se2_true, atol=0.6)
def gmm_pdf( data, means, variances, norm_func, metric, variances_range, norm_func_var): """Return the separate probability density function of GMM. The probability density function is computed for each component of the GMM separately (i.e., mixture coefficients are not taken into account). Parameters ---------- data : array-like, shape=[n_samples, dim] Points at which the GMM probability density is computed. means : array-like, shape=[n_gaussians, dim] Means of each component of the GMM. variances : array-like, shape=[n_gaussians,] Variances of each component of the GMM. norm_func : function Normalisation factor function. metric : function Distance function associated with the used metric. Returns ------- pdf : array-like, shape=[n_samples, n_gaussians,] Probability density function computed at each data sample and for each component of the GMM. """ data_length, _, _ = data.shape + (means.shape[0],) variances_expanded = gs.expand_dims(variances, 0) variances_expanded = gs.repeat(variances_expanded, data_length, 0) variances_flatten = variances_expanded.flatten() distances = -(metric.dist_broadcast(data, means) ** 2) distances = gs.reshape(distances, (data.shape[0] * variances.shape[0])) num = gs.exp( distances / (2 * variances_flatten ** 2)) den = norm_func(variances, variances_range, norm_func_var) den = gs.expand_dims(den, 0) den = gs.repeat(den, data_length, axis=0).flatten() pdf = num / den pdf = gs.reshape( pdf, (data.shape[0], means.shape[0])) return pdf
def objective(velocity): """Define the objective function.""" velocity = gs.array(velocity) velocity = gs.cast(velocity, dtype=base_point.dtype) velocity = gs.reshape(velocity, max_shape) delta = self.exp(velocity, base_point, n_steps, step) - point return gs.sum(delta**2)
def setup_method(self): gs.random.seed(1234) self.n_samples = 20 # Set up for hypersphere self.dim_sphere = 2 self.shape_sphere = (self.dim_sphere + 1, ) self.sphere = Hypersphere(dim=self.dim_sphere) self.intercept_sphere_true = gs.array([0.0, -1.0, 0.0]) self.coef_sphere_true = gs.array([1.0, 0.0, 0.5]) # set up the prior self.prior = lambda x: self.sphere.metric.exp( x * self.coef_sphere_true, base_point=self.intercept_sphere_true, ) self.kernel = ConstantKernel(1.0, (1e-3, 1e3)) * RBF(10.0, (1e-2, 1e2)) # generate data X = gs.linspace(0.0, 1.5 * gs.pi, self.n_samples) self.X_sphere = gs.reshape((X - gs.mean(X)), (-1, 1)) # generate the geodesic y = self.prior(self.X_sphere) # Then add orthogonal sinusoidal oscillations o = (1.0 / 20.0) * gs.array([-0.5, 0.0, 1.0]) o = self.sphere.to_tangent(o, base_point=y) s = self.X_sphere * gs.sin(5.0 * gs.pi * self.X_sphere) self.y_sphere = self.sphere.metric.exp(s * o, base_point=y)
def test_svd(self): gs_point = gs.reshape(gs.arange(12), (4, 3)) gs_point = gs.cast(gs_point, gs.float64) np_point = _np.arange(12).reshape(4, 3) reconstruction = gs.array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.], [0., 0., 0.]]) u, s, v = _np.linalg.svd(np_point) u_r, s_r, v_r = gs.linalg.svd(gs_point) s_r_reconstructed = gs.einsum('kl,l->kl', reconstruction, s_r) gs_a_approx = gs.matmul(gs.matmul(u_r, s_r_reconstructed), v_r) s_reconstructed = _np.einsum('kl,l->kl', reconstruction, s) np_a_approx = _np.dot(u, _np.dot(s_reconstructed, v)) self.assertAllClose(gs_a_approx, np_a_approx) full_matrices = False u, s, v = _np.linalg.svd(np_point, full_matrices=full_matrices) u_r, s_r, v_r = gs.linalg.svd(gs_point, full_matrices) reconstruction = gs.eye(3) s_r_reconstructed = gs.einsum('kl,l->kl', reconstruction, s_r) gs_a_approx = gs.matmul(gs.matmul(u_r, s_r_reconstructed), v_r) s_reconstructed = _np.einsum('kl,l->kl', reconstruction, s) np_a_approx = _np.dot(u, _np.dot(s_reconstructed, v)) self.assertAllClose(gs_a_approx, np_a_approx) compute_uv = False s = _np.linalg.svd(np_point, compute_uv=compute_uv) s_r = gs.linalg.svd(gs_point, compute_uv=compute_uv) self.assertAllClose(s, s_r)
def test_is_diagonal(self): base_point = gs.array([ [1., 2., 3.], [0., 0., 0.], [3., 1., 1.]]) result = self.space.is_diagonal(base_point) expected = False self.assertAllClose(result, expected) diagonal = gs.eye(3) result = self.space.is_diagonal(diagonal) self.assertTrue(result) base_point = gs.stack([base_point, diagonal]) result = self.space.is_diagonal(base_point) expected = gs.array([False, True]) self.assertAllClose(result, expected) base_point = gs.stack([diagonal] * 2) result = self.space.is_diagonal(base_point) self.assertTrue(gs.all(result)) base_point = gs.reshape(gs.arange(6), (2, 3)) result = self.space.is_diagonal(base_point) self.assertTrue(~result)
def test_basis_and_matrix_representation(self): n_samples = 2 expected = gs.random.rand(n_samples * self.dim) expected = gs.reshape(expected, (n_samples, self.dim)) mat = self.algebra.matrix_representation(expected) result = self.algebra.basis_representation(mat) self.assertAllClose(result, expected)
def transform(self, X, y=None): """Project X on the principal components. Parameters ---------- X : array-like, shape=[..., n_features] Data, where n_samples is the number of samples and n_features is the number of features. y : Ignored (Compliance with scikit-learn interface) Returns ------- X_new : array-like, shape=[..., n_components] Projected data. """ tangent_vecs = self.metric.log(X, base_point=self.base_point_fit) if self.point_type == 'matrix': if Matrices.is_symmetric(tangent_vecs).all(): X = SymmetricMatrices.to_vector(tangent_vecs) else: X = gs.reshape(tangent_vecs, (len(X), -1)) else: X = tangent_vecs X = X - self.mean_ X_transformed = gs.matmul(X, gs.transpose(self.components_)) return X_transformed
def inverse_transform(self, X): """Low-dimensional reconstruction of X. The reconstruction will match X_original whose transform would be X if `n_components=min(n_samples, n_features)`. Parameters ---------- X : array-like, shape=[..., n_components] New data, where n_samples is the number of samples and n_components is the number of components. Returns ------- X_original : array-like, shape=[..., n_features] Original data. """ scores = self.mean_ + gs.matmul(X, self.components_) if self.point_type == 'matrix': if Matrices.is_symmetric(self.base_point_fit).all(): scores = SymmetricMatrices( self.base_point_fit.shape[-1]).from_vector(scores) else: dim = self.base_point_fit.shape[-1] scores = gs.reshape(scores, (len(scores), dim, dim)) return self.metric.exp(scores, self.base_point_fit)
def vector_from_matrix(self, matrix): """ Conversion function from (_, m, n) to (_, mn). """ matrix = gs.to_ndarray(matrix, to_ndim=3) n_mats, m, n = matrix.shape return gs.reshape(matrix, (n_mats, m * n))
def matrix_from_vector(self, vec): """Convert point in vector point-type to matrix. Parameters ---------- vec : array-like, shape=[..., dimension] Vector. Returns ------- mat : array-like, shape=[..., n+1, n+1] Matrix. """ vec = self.regularize(vec) n_vecs, _ = vec.shape rot_vec = vec[:, :self.rotations.dim] trans_vec = vec[:, self.rotations.dim:] rot_mat = self.rotations.matrix_from_rotation_vector(rot_vec) trans_vec = gs.reshape(trans_vec, (n_vecs, self.n, 1)) mat = gs.concatenate((rot_mat, trans_vec), axis=2) last_lines = gs.array(gs.get_mask_i_float(self.n, self.n + 1)) last_lines = gs.to_ndarray(last_lines, to_ndim=2) last_lines = gs.to_ndarray(last_lines, to_ndim=3) mat = gs.concatenate((mat, last_lines), axis=1) return mat