def logistic_radial_kernel(distance, bandwidth=1.0): """Logistic radial kernel. Parameters ---------- distance : array-like Array of non-negative real values. bandwidth : float, optional (default=1.0) Positive scale parameter of the kernel. Returns ------- weight : array-like Array of non-negative real values of the same shape than parameter 'distance'. References ---------- https://en.wikipedia.org/wiki/Kernel_(statistics) """ distance = _check_distance(distance) bandwidth = _check_bandwidth(bandwidth) scaled_distance = distance / bandwidth weight = 1 / (gs.exp(scaled_distance) + 2 + gs.exp(-scaled_distance)) return weight
def orbit_data(self): point = gs.array([[gs.exp(4.0), 0.0], [0.0, gs.exp(2.0)]]) sqrt = gs.array([[gs.exp(2.0), 0.0], [0.0, gs.exp(1.0)]]) identity = GeneralLinear(2).identity time = gs.linspace(0.0, 1.0, 3) smoke_data = [ dict( n=2, point=point, base_point=identity, time=time, expected=gs.array([identity, sqrt, point]), ), dict( n=2, point=[point, point], base_point=identity, time=time, expected=[ gs.array([identity, sqrt, point]), gs.array([identity, sqrt, point]), ], ), ] return self.generate_tests(smoke_data)
def test_gaussian_radial_kernel(self): """Test the gaussian radial kernel.""" distance = gs.array([[1], [2]], dtype=float) bandwidth = 2 weight = gaussian_radial_kernel(distance=distance, bandwidth=bandwidth) result = weight expected = gs.array([[gs.exp(-1 / 8)], [gs.exp(-1 / 2)]], dtype=float) self.assertAllClose(expected, result, atol=gs.atol)
def norm_factor_gradient(self, variances): """Compute normalization factor and its gradient. Compute normalization factor given current variance and dimensionality. Parameters ---------- variances : array-like, shape=[n] Value of variance. Returns ------- norm_factor : array-like, shape=[n] Normalisation factor. norm_factor_gradient : array-like, shape=[n] Gradient of the normalization factor. """ variances = gs.transpose(gs.to_ndarray(variances, to_ndim=2)) dim_range = gs.arange(0, self.dim, 1.0) alpha = self._compute_alpha(dim_range) binomial_coefficient = gs.ones(self.dim) binomial_coefficient[1:] = (self.dim - 1 + 1 - dim_range[1:]) / dim_range[1:] binomial_coefficient = gs.cumprod(binomial_coefficient) beta = ((-gs.ones(self.dim)) ** dim_range) * binomial_coefficient sigma_repeated = gs.repeat(variances, self.dim, -1) prod_alpha_sigma = gs.einsum("ij,j->ij", sigma_repeated, alpha) term_2 = gs.exp((prod_alpha_sigma) ** 2) * (1 + gs.erf(prod_alpha_sigma)) term_1 = gs.sqrt(gs.pi / 2.0) * (1.0 / (2 ** (self.dim - 1))) term_2 = gs.einsum("ij,j->ij", term_2, beta) norm_factor = term_1 * variances * gs.sum(term_2, axis=-1, keepdims=True) grad_term_1 = 1 / variances grad_term_21 = 1 / gs.sum(term_2, axis=-1, keepdims=True) grad_term_211 = ( gs.exp((prod_alpha_sigma) ** 2) * (1 + gs.erf(prod_alpha_sigma)) * gs.einsum("ij,j->ij", sigma_repeated, alpha**2) * 2 ) grad_term_212 = gs.repeat( gs.expand_dims((2 / gs.sqrt(gs.pi)) * alpha, axis=0), variances.shape[0], axis=0, ) grad_term_22 = grad_term_211 + grad_term_212 grad_term_22 = gs.einsum("ij, j->ij", grad_term_22, beta) grad_term_22 = gs.sum(grad_term_22, axis=-1, keepdims=True) norm_factor_gradient = grad_term_1 + (grad_term_21 * grad_term_22) return gs.squeeze(norm_factor), gs.squeeze(norm_factor_gradient)
def test_laplacian_radial_kernel(self): """Test the Laplacian radial kernel.""" distance = gs.array([[1], [2]], dtype=float) bandwidth = 2 weight = laplacian_radial_kernel(distance=distance, bandwidth=bandwidth) result = weight expected = gs.array([[gs.exp(-1 / 2)], [gs.exp(-1.0)]], dtype=float) self.assertAllClose(expected, result, atol=TOLERANCE)
def test_logistic_radial_kernel(self): """Test the logistic radial kernel.""" distance = gs.array([[1], [2]], dtype=float) bandwidth = 2 weight = logistic_radial_kernel(distance=distance, bandwidth=bandwidth) result = weight expected = gs.array([[1 / (gs.exp(1 / 2) + 2 + gs.exp(-1 / 2))], [1 / (gs.exp(1.0) + 2 + gs.exp(-1.0))]]) self.assertAllClose(expected, result, atol=TOLERANCE)
def test_orbit(self): point = gs.array([[gs.exp(4.), 0.], [0., gs.exp(2.)]]) sqrt = gs.array([[gs.exp(2.), 0.], [0., gs.exp(1.)]]) idty = GeneralLinear(2).identity path = GeneralLinear(2).orbit(point) time = gs.linspace(0., 1., 3) result = path(time) expected = gs.array([idty, sqrt, point]) self.assertAllClose(result, expected)
def test_orbit_vectorization(self): point = gs.array([[gs.exp(4.), 0.], [0., gs.exp(2.)]]) sqrt = gs.array([[gs.exp(2.), 0.], [0., gs.exp(1.)]]) identity = GeneralLinear(2).identity path = GeneralLinear(2).orbit(gs.stack([point] * 2), identity) time = gs.linspace(0., 1., 3) result = path(time) expected = gs.array([identity, sqrt, point]) expected = gs.stack([expected] * 2) self.assertAllClose(result, expected)
def weighted_gmm_pdf(mixture_coefficients, mesh_data, means, variances, metric): """Return the probability density function of a GMM. Parameters ---------- mixture_coefficients : array-like, shape=[n_gaussians,] Coefficients of the Gaussian mixture model. mesh_data : array-like, shape=[n_precision, 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. metric : function Distance function associated with the used metric. Returns ------- weighted_pdf : array-like, shape=[n_precision, n_gaussians,] Probability density function computed for each point of the mesh data, for each component of the GMM. """ distance_to_mean = metric.dist_broadcast(mesh_data, means) variances_units = gs.expand_dims(variances, 0) variances_units = gs.repeat( variances_units, distance_to_mean.shape[0], axis=0) distribution_normal = gs.exp( -(distance_to_mean ** 2) / (2 * variances_units ** 2)) zeta_sigma = PI_2_3 * variances zeta_sigma = zeta_sigma * gs.exp( (variances ** 2 / 2) * gs.erf(variances / gs.sqrt(2))) result_num = gs.expand_dims(mixture_coefficients, 0) result_num = gs.repeat( result_num, len(distribution_normal), axis=0) result_num = result_num * distribution_normal result_denum = gs.expand_dims(zeta_sigma, 0) result_denum = gs.repeat( result_denum, len(distribution_normal), axis=0) weighted_pdf = result_num / result_denum return weighted_pdf
def test_sigmoid_radial_kernel(self): """Test the sigmoid radial kernel.""" distance = gs.array([[1], [2]], dtype=float) bandwidth = 2 weight = sigmoid_radial_kernel(distance=distance, bandwidth=bandwidth) result = weight expected = gs.array( [ [1 / (gs.exp(1 / 2) + gs.exp(-1 / 2))], [1 / (gs.exp(1.0) + gs.exp(-1.0))], ], dtype=float, ) self.assertAllClose(expected, result, atol=gs.atol)
def normalization_factor(self, variances): """Return normalization factor. Parameters ---------- variances : array-like, shape=[n,] Array of equally distant values of the variance precision time. Returns ------- norm_func : array-like, shape=[n,] Normalisation factor for all given variances. """ binomial_coefficient = None n_samples = variances.shape[0] expand_variances = gs.expand_dims(variances, axis=0) expand_variances = gs.repeat(expand_variances, self.dim, axis=0) if binomial_coefficient is None: dim_range = gs.arange(self.dim) dim_range[0] = 1 n_fact = dim_range.prod() k_fact = gs.concatenate([ gs.expand_dims(dim_range[:i].prod(), 0) for i in range(1, dim_range.shape[0] + 1) ], 0) nmk_fact = gs.flip(k_fact, 0) binomial_coefficient = n_fact / (k_fact * nmk_fact) binomial_coefficient = gs.expand_dims(binomial_coefficient, -1) binomial_coefficient = gs.repeat(binomial_coefficient, n_samples, axis=1) range_ = gs.expand_dims(gs.arange(self.dim), -1) range_ = gs.repeat(range_, n_samples, axis=1) ones_ = gs.expand_dims(gs.ones(self.dim), -1) ones_ = gs.repeat(ones_, n_samples, axis=1) alternate_neg = (-ones_)**(range_) erf_arg = (( (self.dim - 1) - 2 * range_) * expand_variances) / gs.sqrt(2) exp_arg = ((((self.dim - 1) - 2 * range_) * expand_variances) / gs.sqrt(2))**2 norm_func_1 = (1 + gs.erf(erf_arg)) * gs.exp(exp_arg) norm_func_2 = binomial_coefficient * norm_func_1 norm_func_3 = alternate_neg * norm_func_2 norm_func = NORMALIZATION_FACTOR_CST * variances * \ norm_func_3.sum(0) * (1 / (2 ** (self.dim - 1))) return norm_func
def _exp(tangent_vec, base_point): circle_center = (base_point[0] + base_point[1] * tangent_vec[1] / tangent_vec[0]) circle_radius = gs.sqrt((circle_center - base_point[0])**2 + base_point[1]**2) moebius_d = 1 moebius_c = 1 / (2 * circle_radius) moebius_b = circle_center - circle_radius moebius_a = (circle_center + circle_radius) * moebius_c point_complex = base_point[0] + 1j * base_point[1] tangent_vec_complex = tangent_vec[0] + 1j * tangent_vec[1] point_moebius = (1j * (moebius_d * point_complex - moebius_b) / (moebius_c * point_complex - moebius_a)) tangent_vec_moebius = ( -1j * tangent_vec_complex * (1j * moebius_c * point_moebius + moebius_d)**2) end_point_moebius = point_moebius * gs.exp( tangent_vec_moebius / point_moebius) end_point_complex = ( moebius_a * 1j * end_point_moebius + moebius_b) / (moebius_c * 1j * end_point_moebius + moebius_d) end_point_expected = gs.hstack( [np.real(end_point_complex), np.imag(end_point_complex)]) return end_point_expected
def exp(self, tangent_vec, base_point, **kwargs): """Compute the Cholesky exponential map. Compute the Riemannian exponential at point base_point of tangent vector tangent_vec wrt the Cholesky metric. This gives a lower triangular matrix with positive elements. Parameters ---------- tangent_vec : array-like, shape=[..., n, n] Tangent vector at base point. base_point : array-like, shape=[..., n, n] Base point. Returns ------- exp : array-like, shape=[..., n, n] Riemannian exponential. """ sl_base_point = Matrices.to_strictly_lower_triangular(base_point) sl_tangent_vec = Matrices.to_strictly_lower_triangular(tangent_vec) diag_base_point = Matrices.diagonal(base_point) diag_tangent_vec = Matrices.diagonal(tangent_vec) diag_product_expm = gs.exp(gs.divide(diag_tangent_vec, diag_base_point)) sl_exp = sl_base_point + sl_tangent_vec diag_exp = gs.vec_to_diag(diag_base_point * diag_product_expm) exp = sl_exp + diag_exp return exp
def laplacian_radial_kernel(distance, bandwidth=1.0): """Laplacian radial kernel. Parameters ---------- distance : array-like Array of non-negative real values. bandwidth : float, optional (default=1.0) Positive scale parameter of the kernel. Returns ------- weight : array-like Array of non-negative real values of the same shape than parameter 'distance'. Returns ------- http://crsouza.com/2010/03/17/ kernel-functions-for-machine-learning-applications/ https://data-flair.training/blogs/svm-kernel-functions/ """ distance = _check_distance(distance) bandwidth = _check_bandwidth(bandwidth) scaled_distance = distance / bandwidth weight = gs.exp(-scaled_distance) return weight
def bump_radial_kernel(distance, bandwidth=1.0): """Bump radial kernel. Parameters ---------- distance : array-like Array of non-negative real values. bandwidth : float, optional (default=1.0) Positive scale parameter of the kernel. Returns ------- weight : array-like Array of non-negative real values of the same shape than parameter 'distance'. References ---------- https://en.wikipedia.org/wiki/Radial_basis_function """ distance = _check_distance(distance) bandwidth = _check_bandwidth(bandwidth) scaled_distance = distance / bandwidth weight = gs.where( scaled_distance < 1, gs.exp(-1 / (1 - scaled_distance**2)), gs.zeros(distance.shape, dtype=float), ) return weight
def test_exp(self): point = gs.array([1., 1.]) tangent_vec = gs.array([2., 1.]) end_point = self.metric.exp(tangent_vec, point) circle_center = point[0] + point[1] * tangent_vec[1] / tangent_vec[0] circle_radius = gs.sqrt((circle_center - point[0])**2 + point[1]**2) moebius_d = 1 moebius_c = 1 / (2 * circle_radius) moebius_b = circle_center - circle_radius moebius_a = (circle_center + circle_radius) * moebius_c point_complex = point[0] + 1j * point[1] tangent_vec_complex = tangent_vec[0] + 1j * tangent_vec[1] point_moebius = 1j * (moebius_d * point_complex - moebius_b)\ / (moebius_c * point_complex - moebius_a) tangent_vec_moebius = -1j * tangent_vec_complex * ( 1j * moebius_c * point_moebius + moebius_d)**2 end_point_moebius = point_moebius * gs.exp( tangent_vec_moebius / point_moebius) end_point_complex = (moebius_a * 1j * end_point_moebius + moebius_b)\ / (moebius_c * 1j * end_point_moebius + moebius_d) end_point_expected = gs.hstack( [np.real(end_point_complex), np.imag(end_point_complex)]) self.assertAllClose(end_point, end_point_expected)
def gaussian_radial_kernel(distance, bandwidth=1.0): """Gaussian radial kernel. Parameters ---------- distance : array-like Array of non-negative real values. bandwidth : float, optional (default=1.0) Positive scale parameter of the kernel. Returns ------- weight : array-like Array of non-negative real values of the same shape than parameter 'distance'. References ---------- https://en.wikipedia.org/wiki/Kernel_(statistics) https://en.wikipedia.org/wiki/Radial_basis_function """ distance = _check_distance(distance) bandwidth = _check_bandwidth(bandwidth) scaled_distance = distance / bandwidth weight = gs.exp(-(scaled_distance**2) / 2) return weight
def threshold(random_v): """Compute the acceptance threshold.""" squared_norm = gs.sum(random_v ** 2, axis=-1) sinc = utils.taylor_exp_even_func( squared_norm, utils.sinc_close_0) ** (dim - 1) threshold_val = sinc * gs.exp(squared_norm * (dim - 1) / 2 / gs.pi) return threshold_val, squared_norm ** .5
def test_exp_vectorization(self): point = gs.array([[1.0, 1.0], [1.0, 1.0]]) tangent_vec = gs.array([[2.0, 1.0], [2.0, 1.0]]) result = self.metric.exp(tangent_vec, point) point = point[0] tangent_vec = tangent_vec[0] circle_center = point[0] + point[1] * tangent_vec[1] / tangent_vec[0] circle_radius = gs.sqrt((circle_center - point[0])**2 + point[1]**2) moebius_d = 1 moebius_c = 1 / (2 * circle_radius) moebius_b = circle_center - circle_radius moebius_a = (circle_center + circle_radius) * moebius_c point_complex = point[0] + 1j * point[1] tangent_vec_complex = tangent_vec[0] + 1j * tangent_vec[1] point_moebius = (1j * (moebius_d * point_complex - moebius_b) / (moebius_c * point_complex - moebius_a)) tangent_vec_moebius = (-1j * tangent_vec_complex * (1j * moebius_c * point_moebius + moebius_d)**2) end_point_moebius = point_moebius * gs.exp( tangent_vec_moebius / point_moebius) end_point_complex = (moebius_a * 1j * end_point_moebius + moebius_b) / (moebius_c * 1j * end_point_moebius + moebius_d) end_point_expected = gs.hstack( [np.real(end_point_complex), np.imag(end_point_complex)]) expected = gs.stack([end_point_expected, end_point_expected]) self.assertAllClose(result, expected)
def random_von_mises_fisher(self, kappa=10, n_samples=1): """Sample in the 2-sphere with the von Mises distribution. Sample in the 2-sphere with the von Mises distribution centered in the north pole. Parameters ---------- kappa : int, optional n_samples : int, optional Returns ------- point : array-like """ if self.dimension != 2: raise NotImplementedError( 'Sampling from the von Mises Fisher distribution' 'is only implemented in dimension 2.') angle = 2. * gs.pi * gs.random.rand(n_samples) angle = gs.to_ndarray(angle, to_ndim=2, axis=1) unit_vector = gs.hstack((gs.cos(angle), gs.sin(angle))) scalar = gs.random.rand(n_samples) coord_z = 1. + 1. / kappa * gs.log(scalar + (1. - scalar) * gs.exp(gs.array(-2. * kappa))) coord_z = gs.to_ndarray(coord_z, to_ndim=2, axis=1) coord_xy = gs.sqrt(1. - coord_z**2) * unit_vector point = gs.hstack((coord_xy, coord_z)) return point
def summand(k): exp_arg = -((dim - 1 - 2 * k)**2) / 2 / variances erf_arg_2 = (gs.pi * variances - (dim - 1 - 2 * k) * 1j) / gs.sqrt(2 * variances) sign = (-1.0)**k comb_2 = gs.comb(k, dim - 1) return sign * comb_2 * gs.exp(exp_arg) * gs.real(gs.erf(erf_arg_2))
def aux_differential_power(power, tangent_vec, base_point): """Compute the differential of the matrix power. Auxiliary function to the functions differential_power and inverse_differential_power. Parameters ---------- power : float Power function to differentiate. tangent_vec : array_like, shape=[..., n, n] Tangent vector at base point. base_point : array_like, shape=[..., n, n] Base point. Returns ------- eigvectors : array-like, shape=[..., n, n] transp_eigvectors : array-like, shape=[..., n, n] numerator : array-like, shape=[..., n, n] denominator : array-like, shape=[..., n, n] temp_result : array-like, shape=[..., n, n] """ eigvalues, eigvectors = gs.linalg.eigh(base_point) if power == 0: powered_eigvalues = gs.log(eigvalues) elif power == math.inf: powered_eigvalues = gs.exp(eigvalues) else: powered_eigvalues = eigvalues ** power denominator = eigvalues[..., :, None] - eigvalues[..., None, :] numerator = powered_eigvalues[..., :, None] - powered_eigvalues[..., None, :] if power == 0: numerator = gs.where(denominator == 0, gs.ones_like(numerator), numerator) denominator = gs.where( denominator == 0, eigvalues[..., :, None], denominator ) elif power == math.inf: numerator = gs.where( denominator == 0, powered_eigvalues[..., :, None], numerator ) denominator = gs.where( denominator == 0, gs.ones_like(numerator), denominator ) else: numerator = gs.where( denominator == 0, power * powered_eigvalues[..., :, None], numerator ) denominator = gs.where( denominator == 0, eigvalues[..., :, None], denominator ) transp_eigvectors = Matrices.transpose(eigvectors) temp_result = Matrices.mul(transp_eigvectors, tangent_vec, eigvectors) return (eigvectors, transp_eigvectors, numerator, denominator, temp_result)
def test_inverse_differential_exp(self): base_point = gs.array([[1., 0., 0.], [0., 1., 0.], [0., 0., -1.]]) x = gs.exp(1) y = gs.sinh(1) tangent_vec = gs.array([[x, x, y], [x, x, y], [y, y, 1 / x]]) result = self.space.inverse_differential_exp(tangent_vec, base_point) expected = gs.array([[[1., 1., 1.], [1., 1., 1.], [1., 1., 1.]]]) self.assertAllClose(result, expected)
def test_bump_radial_kernel(self): """Test the bump radial kernel.""" distance = gs.array([[1 / 2], [2]], dtype=float) bandwidth = 1 weight = bump_radial_kernel(distance=distance, bandwidth=bandwidth) result = weight expected = gs.array([[gs.exp(-1 / (3 / 4))], [0]], dtype=float) self.assertAllClose(expected, result, atol=gs.atol)
def gaussian(x, mu, sig): a = (x - mu)**2 / (2 * (sig**2)) b = 1 / (sig * (gs.sqrt(2 * gs.pi))) f = b * gs.exp(-a) l2_norm = gs.sqrt(gs.trapz(f**2, x)) f_sinf = f / l2_norm return gs.array([f_sinf])
def test_inverse_differential_exp(self): """Test of inverse_differential_exp method.""" base_point = gs.array([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, -1.0]]) x = gs.exp(1.0) y = gs.sinh(1.0) tangent_vec = gs.array([[x, x, y], [x, x, y], [y, y, 1.0 / x]]) result = self.space.inverse_differential_exp(tangent_vec, base_point) expected = gs.array([[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]]) self.assertAllClose(result, expected)
def summand(k): exp_arg = -((dim - 1 - 2 * k)**2) / 2 / variances erf_arg_1 = (dim - 1 - 2 * k) * 1j / gs.sqrt(2 * variances) erf_arg_2 = (gs.pi * variances - (dim - 1 - 2 * k) * 1j) / gs.sqrt(2 * variances) sign = (-1.0)**k comb = gs.comb(dim - 1, k) erf_terms = gs.imag(gs.erf(erf_arg_2) + gs.erf(erf_arg_1)) return sign * comb * gs.exp(exp_arg) * erf_terms
def test_differential_exp(self): """Test of differential_exp method.""" base_point = gs.array([[1., 0., 0.], [0., 1., 0.], [0., 0., -1.]]) tangent_vec = gs.array([[1., 1., 1.], [1., 1., 1.], [1., 1., 1.]]) result = self.space.differential_exp(tangent_vec, base_point) x = gs.exp(1.) y = gs.sinh(1.) expected = gs.array([[x, x, y], [x, x, y], [y, y, 1 / x]]) self.assertAllClose(result, expected)
def grad_log_sigmoid(vector): """Gradient of log sigmoid function. Parameters ---------- vector : array-like, shape=[n_samples, dim] Returns ------- gradient : array-like, shape=[n_samples, dim] """ return 1 / (1 + gs.exp(vector))
def log_test_data(self): smoke_data = [ dict( n=2, point=[[EULER, 0.0], [2.0, EULER**3]], base_point=[[EULER**3, 0.0], [4.0, EULER**4]], expected=[[-2.0 * EULER**3, 0.0], [-2.0, -1 * EULER**4]], ), dict( n=2, point=[ [[gs.exp(-2.0), 0.0], [0.0, gs.exp(2.0)]], [[gs.exp(-3.0), 0.0], [2.0, gs.exp(3.0)]], ], base_point=[[[1.0, 0.0], [-1.0, 1.0]], [[1.0, 0.0], [0.0, 1.0]]], expected=[[[-2.0, 0.0], [1.0, 2.0]], [[-3.0, 0.0], [2.0, 3.0]]], ), ] return self.generate_tests(smoke_data)