From ed66516c2001022e467da0c8b4f229015813a1dc Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Thu, 13 Jun 2019 17:51:31 -0400 Subject: added sklearn_tda preprocessing functions --- src/cython/sktda/preprocessing.py | 274 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 274 insertions(+) create mode 100644 src/cython/sktda/preprocessing.py (limited to 'src') diff --git a/src/cython/sktda/preprocessing.py b/src/cython/sktda/preprocessing.py new file mode 100644 index 00000000..2f4a5fe5 --- /dev/null +++ b/src/cython/sktda/preprocessing.py @@ -0,0 +1,274 @@ +""" +@author: Mathieu Carriere +All rights reserved +""" + +import numpy as np +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.preprocessing import StandardScaler + +############################################# +# Preprocessing ############################# +############################################# + +class BirthPersistenceTransform(BaseEstimator, TransformerMixin): + """ + This is a class for the affine transformation (x,y) -> (x,y-x) to be applied on persistence diagrams. It is a particular scaler for persistence diagram that can be given as input for the DiagramPreprocessor class. + """ + def __init__(self): + """ + Constructor for BirthPersistenceTransform class. + """ + return None + + def fit(self, X, y=None): + """ + Fit the BirthPersistenceTransform class on a list of persistence diagrams (this function actually does nothing but is useful when BirthPersistenceTransform is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + Apply the BirthPersistenceTransform function on the persistence diagrams. + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + + Returns: + Xfit (list of n x 2 numpy arrays): transformed persistence diagrams. + """ + Xfit = np.matmul(X, np.array([[1., -1.],[0., 1.]])) + return Xfit + + +class DiagramPreprocessor(BaseEstimator, TransformerMixin): + """ + This is a class for preprocessing persistence diagrams with a given list of scalers, such as those included in scikit-learn. + """ + def __init__(self, use=False, scalers=[]): + """ + Constructor for the DiagramPreprocessor class. + + Attributes: + use (bool): whether to use the class or not (default False). + scalers (list of classes): list of scalers to be fit on the persistence diagrams (default []). Each element of the list is a tuple with two elements: the first one is a list of coordinates, and the second one is a scaler (i.e. a class with fit() and transform() methods) that is going to be applied to these coordinates. Common scalers can be found in the scikit-learn library (such as MinMaxScaler for instance). + """ + self.scalers = scalers + self.use = use + + def fit(self, X, y=None): + """ + Fit the DiagramPreprocessor class on a list of persistence diagrams: persistence diagrams are concatenated in a big numpy array, and scalers are fit (by calling their fit() method) on their corresponding coordinates in this big array. + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if self.use: + if len(X) == 1: + P = X[0] + else: + P = np.concatenate(X,0) + for (indices, scaler) in self.scalers: + scaler.fit(P[:,indices]) + return self + + def transform(self, X): + """ + Apply the DiagramPreprocessor function on the persistence diagrams. The fitted scalers are applied (by calling their transform() method) to their corresponding coordinates in each persistence diagram individually. + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + + Returns: + Xfit (list of n x 2 or n x 1 numpy arrays): transformed persistence diagrams. + """ + Xfit = [np.copy(d) for d in X] + if self.use: + for i in range(len(Xfit)): + if Xfit[i].shape[0] > 0: + for (indices, scaler) in self.scalers: + Xfit[i][:,indices] = scaler.transform(Xfit[i][:,indices]) + return Xfit + +class Padding(BaseEstimator, TransformerMixin): + """ + This is a class for padding a list of persistence diagrams with dummy points, so that all persistence diagrams end up with the same number of points. + """ + def __init__(self, use=False): + """ + Constructor for the Padding class. + + Attributes: + use (bool): whether to use the class or not (default False). + """ + self.use = use + + def fit(self, X, y=None): + """ + Fit the Padding class on a list of persistence diagrams (this function actually does nothing but is useful when Padding is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + Add dummy points to each persistence diagram so that they all have the same cardinality. All points are given an additional coordinate indicating if the point was added after padding (0) or already present before (1). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + + Returns: + Xfit (list of n x 3 or n x 2 numpy arrays): padded persistence diagrams. + """ + if self.use: + Xfit, num_diag = [], len(X) + max_card = max([len(diag) for diag in X]) + for diag in X: + [num_pts, dim] = diag.shape + diag_pad = np.zeros([max_card, dim+1]) + diag_pad[:num_pts,:dim] = diag + diag_pad[:num_pts, dim] = np.ones(num_pts) + Xfit.append(diag_pad) + else: + Xfit = X + return Xfit + +class ProminentPoints(BaseEstimator, TransformerMixin): + """ + This is a class for removing points that are close or far from the diagonal in persistence diagrams. + """ + def __init__(self, use=False, num_pts=10, threshold=-1, location="upper", point_type="finite"): + """ + Constructor for the ProminentPoints class. + + Attributes: + use (bool): whether to use the class or not (default False). + location (string): either "upper" or "lower" (default "upper"). Whether to keep the points that are far away ("upper") or close ("lower") to the diagonal. + num_pts (int): cardinality threshold (default 10). If location == "upper", keep the top **num_pts** points that are the farthest away from the diagonal. If location == "lower", keep the top **num_pts** points that are the closest to the diagonal. + threshold (double): distance-to-diagonal threshold (default -1). If location == "upper", keep the points that are at least at a distance **threshold** from the diagonal. If location == "lower", keep the points that are at most at a distance **threshold** from the diagonal. + point_type (string): either "finite" if persistence diagrams are n x 2 numpy arrays, or "essential" if persistence diagrams are n x 1 numpy arrays (default "finite"). If "finite", points are ordered and thresholded by distance-to-diagonal. If "essential", points are ordered and thresholded by first coordinate. + """ + self.num_pts = num_pts + self.threshold = threshold + self.use = use + self.location = location + self.point_type = point_type + + def fit(self, X, y=None): + """ + Fit the ProminentPoints class on a list of persistence diagrams (this function actually does nothing but is useful when ProminentPoints is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + If location == "upper", first select the top **num_pts** points that are the farthest away from the diagonal, then select and return from these points the ones that are at least at distance **threshold** from the diagonal for each persistence diagram individually. If location == "lower", first select the top **num_pts** points that are the closest to the diagonal, then select and return from these points the ones that are at most at distance **threshold** from the diagonal for each persistence diagram individually. If point_type == "essential", do the same with first coordinate instead of distance-to-diagonal. + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + + Returns: + Xfit (list of n x 2 or n x 1 numpy arrays): thresholded persistence diagrams. + """ + if self.use: + Xfit, num_diag = [], len(X) + for i in range(num_diag): + diag = X[i] + if self.point_type == "finite": + if diag.shape[0] > 0: + pers = np.abs(np.matmul(diag[:,:2], [-1., 1.])) + idx_thresh = pers >= self.threshold + thresh_diag, thresh_pers = diag[idx_thresh.flatten()], pers[idx_thresh.flatten()] + sort_index = np.flip(np.argsort(thresh_pers, axis=None), 0) + if self.location == "upper": + new_diag = thresh_diag[sort_index[:min(self.num_pts, thresh_diag.shape[0])],:] + if self.location == "lower": + new_diag = np.concatenate( [ thresh_diag[sort_index[min(self.num_pts, thresh_diag.shape[0]):],:], diag[~idx_thresh.flatten()] ], axis=0) + else: + new_diag = diag + + else: + if diag.shape[0] > 0: + birth = diag[:,:1] + idx_thresh = birth >= self.threshold + thresh_diag, thresh_birth = diag[idx_thresh.flatten()], birth[idx_thresh.flatten()] + if self.location == "upper": + new_diag = thresh_diag[:min(self.num_pts, thresh_diag.shape[0]),:] + if self.location == "lower": + new_diag = np.concatenate( [ thresh_diag[min(self.num_pts, thresh_diag.shape[0]):,:], diag[~idx_thresh.flatten()] ], axis=0) + else: + new_diag = diag + + Xfit.append(new_diag) + else: + Xfit = X + return Xfit + +class DiagramSelector(BaseEstimator, TransformerMixin): + """ + This is a class for extracting finite or essential points in persistence diagrams. + """ + def __init__(self, use=False, limit=np.inf, point_type="finite"): + """ + Constructor for the DiagramSelector class. + + Attributes: + use (bool): whether to use the class or not (default False). + limit (double): second coordinate value that is the criterion for being an essential point (default numpy.inf). + point_type (string): either "finite" or "essential". The type of the points that are going to be extracted. + """ + self.use, self.limit, self.point_type = use, limit, point_type + + def fit(self, X, y=None): + """ + Fit the DiagramSelector class on a list of persistence diagrams (this function actually does nothing but is useful when DiagramSelector is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + Extract and return the finite or essential points of each persistence diagram individually. + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + + Returns: + Xfit (list of n x 2 or n x 1 numpy arrays): extracted persistence diagrams. + """ + if self.use: + Xfit, num_diag = [], len(X) + if self.point_type == "finite": + for i in range(num_diag): + diag = X[i] + if diag.shape[0] != 0: + idx_fin = diag[:,1] != self.limit + Xfit.append(diag[idx_fin,:]) + else: + Xfit.append(diag) + if self.point_type == "essential": + for i in range(num_diag): + diag = X[i] + if diag.shape[0] != 0: + idx_ess = diag[:,1] == self.limit + Xfit.append(np.delete(diag,1,1)[idx_ess,:]) + else: + Xfit.append(np.delete(diag,1,1)) + else: + Xfit = X + return Xfit -- cgit v1.2.3 From de65ea32902e31bc1f951d32eeb035ae95c624db Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Thu, 13 Jun 2019 17:51:45 -0400 Subject: added sklearn_tda vectorizations --- src/cython/sktda/vector_methods.py | 404 +++++++++++++++++++++++++++++++++++++ 1 file changed, 404 insertions(+) create mode 100644 src/cython/sktda/vector_methods.py (limited to 'src') diff --git a/src/cython/sktda/vector_methods.py b/src/cython/sktda/vector_methods.py new file mode 100644 index 00000000..4dd147e7 --- /dev/null +++ b/src/cython/sktda/vector_methods.py @@ -0,0 +1,404 @@ +""" +@author: Mathieu Carriere +All rights reserved +""" + +import numpy as np +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.preprocessing import MinMaxScaler +from sklearn.neighbors import DistanceMetric + +from preprocessing import DiagramPreprocessor + +############################################# +# Finite Vectorization methods ############## +############################################# + +class PersistenceImage(BaseEstimator, TransformerMixin): + """ + This is a class for computing persistence images from a list of persistence diagrams. A persistence image is a 2D function computed from a persistence diagram by convolving the diagram points with a weighted Gaussian kernel. The plane is then discretized into an image with pixels, which is flattened and returned as a vector. See http://jmlr.org/papers/v18/16-337.html for more details. + """ + def __init__(self, bandwidth=1., weight=lambda x: 1, resolution=[20,20], im_range=[np.nan, np.nan, np.nan, np.nan]): + """ + Constructor for the PersistenceImage class. + + Attributes: + bandwidth (double): bandwidth of the Gaussian kernel (default 1.). + weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie lists or numpy arrays of the form [p_x,p_y]. + resolution ([int,int]): size (in pixels) of the persistence image (default [20,20]). + im_range ([double,double,double,double]): minimum and maximum of each axis of the persistence image, of the form [x_min, x_max, y_min, y_max] (default [numpy.nan, numpy.nan, numpy.nan, numpyp.nan]). If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + """ + self.bandwidth, self.weight = bandwidth, weight + self.resolution, self.im_range = resolution, im_range + + def fit(self, X, y=None): + """ + Fit the PersistenceImage class on a list of persistence diagrams: if any of the values in **im_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.im_range)).any(): + pre = DiagramPreprocessor(use=True, scalers=[([0,1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = pre.scalers[0][1].data_min_, pre.scalers[0][1].data_max_ + self.im_range = np.where(np.isnan(np.array(self.im_range)), np.array([mx, Mx, my, My]), np.array(self.im_range)) + return self + + def transform(self, X): + """ + Compute the persistence image for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (number of pixels = **resolution[0]** x **resolution[1]**)): output persistence images. + """ + num_diag, Xfit = len(X), [] + for i in range(num_diag): + + diagram, num_pts_in_diag = X[i], X[i].shape[0] + + w = np.ones(num_pts_in_diag) + for j in range(num_pts_in_diag): + w[j] = self.weight(diagram[j,:]) + + x_values, y_values = np.linspace(self.im_range[0], self.im_range[1], self.resolution[0]), np.linspace(self.im_range[2], self.im_range[3], self.resolution[1]) + Xs, Ys = np.tile((diagram[:,0][:,np.newaxis,np.newaxis]-x_values[np.newaxis,np.newaxis,:]),[1,self.resolution[1],1]), np.tile(diagram[:,1][:,np.newaxis,np.newaxis]-y_values[np.newaxis,:,np.newaxis],[1,1,self.resolution[0]]) + image = np.tensordot(w, np.exp((-np.square(Xs)-np.square(Ys))/(2*np.square(self.bandwidth)))/(self.bandwidth*np.sqrt(2*np.pi)), 1) + + Xfit.append(image.flatten()[np.newaxis,:]) + Xfit = np.concatenate(Xfit,0) + + return Xfit + +class Landscape(BaseEstimator, TransformerMixin): + """ + This is a class for computing persistence landscapes from a list of persistence diagrams. A persistence landscape is a collection of 1D piecewise-linear functions computed from the rank function associated to the persistence diagram. These piecewise-linear functions are then sampled uniformly on a given range and the corresponding vectors of samples are concatenated and returned. See http://jmlr.org/papers/v16/bubenik15a.html for more details. + """ + def __init__(self, num_landscapes=5, resolution=100, ls_range=[np.nan, np.nan]): + """ + Constructor for the Landscape class. + + Attributes: + num_landscapes (int): number of piecewise-linear functions to output (default 5). + resolution (int): number of sample for all piecewise-linear functions (default 100). + ls_range ([double, double]): minimum and maximum of all piecewise-linear function domains, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + """ + self.num_landscapes, self.resolution, self.ls_range = num_landscapes, resolution, ls_range + + def fit(self, X, y=None): + """ + Fit the Landscape class on a list of persistence diagrams: if any of the values in **ls_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.ls_range)).any(): + pre = DiagramPreprocessor(use=True, scalers=[([0,1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = pre.scalers[0][1].data_min_, pre.scalers[0][1].data_max_ + self.ls_range = np.where(np.isnan(np.array(self.ls_range)), np.array([mx, My]), np.array(self.ls_range)) + return self + + def transform(self, X): + """ + Compute the persistence landscape for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (number of samples = **num_landscapes** x **resolution**)): output persistence landscapes. + """ + num_diag, Xfit = len(X), [] + x_values = np.linspace(self.ls_range[0], self.ls_range[1], self.resolution) + step_x = x_values[1] - x_values[0] + + for i in range(num_diag): + + diagram, num_pts_in_diag = X[i], X[i].shape[0] + + ls = np.zeros([self.num_landscapes, self.resolution]) + + events = [] + for j in range(self.resolution): + events.append([]) + + for j in range(num_pts_in_diag): + [px,py] = diagram[j,:] + min_idx = np.minimum(np.maximum(np.ceil((px - self.ls_range[0]) / step_x).astype(int), 0), self.resolution) + mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.ls_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.ls_range[0]) / step_x).astype(int), 0), self.resolution) + + if min_idx < self.resolution and max_idx > 0: + + landscape_value = self.ls_range[0] + min_idx * step_x - px + for k in range(min_idx, mid_idx): + events[k].append(landscape_value) + landscape_value += step_x + + landscape_value = py - self.ls_range[0] - mid_idx * step_x + for k in range(mid_idx, max_idx): + events[k].append(landscape_value) + landscape_value -= step_x + + for j in range(self.resolution): + events[j].sort(reverse=True) + for k in range( min(self.num_landscapes, len(events[j])) ): + ls[k,j] = events[j][k] + + Xfit.append(np.sqrt(2)*np.reshape(ls,[1,-1])) + Xfit = np.concatenate(Xfit,0) + + return Xfit + +class Silhouette(BaseEstimator, TransformerMixin): + """ + This is a class for computing persistence silhouettes from a list of persistence diagrams. A persistence silhouette is computed by taking a weighted average of the collection of 1D piecewise-linear functions given by the persistence landscapes, and then by uniformly sampling this average on a given range. Finally, the corresponding vector of samples is returned. See https://arxiv.org/abs/1312.0308 for more details. + """ + def __init__(self, weight=lambda x: 1, resolution=100, sh_range=[np.nan, np.nan]): + """ + Constructor for the Silhouette class. + + Attributes: + weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie on lists or numpy arrays of the form [p_x,p_y]. + resolution (int): number of samples for the weighted average (default 100). + sh_range ([double, double]): minimum and maximum for the weighted average domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + """ + self.weight, self.resolution, self.sh_range = weight, resolution, sh_range + + def fit(self, X, y=None): + """ + Fit the Silhouette class on a list of persistence diagrams: if any of the values in **sh_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.sh_range)).any(): + pre = DiagramPreprocessor(use=True, scalers=[([0,1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = pre.scalers[0][1].data_min_, pre.scalers[0][1].data_max_ + self.sh_range = np.where(np.isnan(np.array(self.sh_range)), np.array([mx, My]), np.array(self.sh_range)) + return self + + def transform(self, X): + """ + Compute the persistence silhouette for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (**resolution**): output persistence silhouettes. + """ + num_diag, Xfit = len(X), [] + x_values = np.linspace(self.sh_range[0], self.sh_range[1], self.resolution) + step_x = x_values[1] - x_values[0] + + for i in range(num_diag): + + diagram, num_pts_in_diag = X[i], X[i].shape[0] + + sh, weights = np.zeros(self.resolution), np.zeros(num_pts_in_diag) + for j in range(num_pts_in_diag): + weights[j] = self.weight(diagram[j,:]) + total_weight = np.sum(weights) + + for j in range(num_pts_in_diag): + + [px,py] = diagram[j,:] + weight = weights[j] / total_weight + min_idx = np.minimum(np.maximum(np.ceil((px - self.sh_range[0]) / step_x).astype(int), 0), self.resolution) + mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sh_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.sh_range[0]) / step_x).astype(int), 0), self.resolution) + + if min_idx < self.resolution and max_idx > 0: + + silhouette_value = self.sh_range[0] + min_idx * step_x - px + for k in range(min_idx, mid_idx): + sh[k] += weight * silhouette_value + silhouette_value += step_x + + silhouette_value = py - self.sh_range[0] - mid_idx * step_x + for k in range(mid_idx, max_idx): + sh[k] += weight * silhouette_value + silhouette_value -= step_x + + Xfit.append(np.reshape(np.sqrt(2) * sh, [1,-1])) + Xfit = np.concatenate(Xfit, 0) + + return Xfit + +class BettiCurve(BaseEstimator, TransformerMixin): + """ + This is a class for computing Betti curves from a list of persistence diagrams. A Betti curve is a 1D piecewise-constant function obtained from the rank function. It is sampled uniformly on a given range and the vector of samples is returned. See https://www.researchgate.net/publication/316604237_Time_Series_Classification_via_Topological_Data_Analysis for more details. + """ + def __init__(self, resolution=100, bc_range=[np.nan, np.nan]): + """ + Constructor for the BettiCurve class. + + Attributes: + resolution (int): number of sample for the piecewise-constant function (default 100). + bc_range ([double, double]): minimum and maximum of the piecewise-constant function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + """ + self.resolution, self.bc_range = resolution, bc_range + + def fit(self, X, y=None): + """ + Fit the BettiCurve class on a list of persistence diagrams: if any of the values in **bc_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.bc_range)).any(): + pre = DiagramPreprocessor(use=True, scalers=[([0,1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = pre.scalers[0][1].data_min_, pre.scalers[0][1].data_max_ + self.bc_range = np.where(np.isnan(np.array(self.bc_range)), np.array([mx, My]), np.array(self.bc_range)) + return self + + def transform(self, X): + """ + Compute the Betti curve for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (**resolution**): output Betti curves. + """ + num_diag, Xfit = len(X), [] + x_values = np.linspace(self.bc_range[0], self.bc_range[1], self.resolution) + step_x = x_values[1] - x_values[0] + + for i in range(num_diag): + + diagram, num_pts_in_diag = X[i], X[i].shape[0] + + bc = np.zeros(self.resolution) + for j in range(num_pts_in_diag): + [px,py] = diagram[j,:] + min_idx = np.minimum(np.maximum(np.ceil((px - self.bc_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.bc_range[0]) / step_x).astype(int), 0), self.resolution) + for k in range(min_idx, max_idx): + bc[k] += 1 + + Xfit.append(np.reshape(bc,[1,-1])) + Xfit = np.concatenate(Xfit, 0) + + return Xfit + +class TopologicalVector(BaseEstimator, TransformerMixin): + """ + This is a class for computing topological vectors from a list of persistence diagrams. The topological vector associated to a persistence diagram is the sorted vector of a slight modification of the pairwise distances between the persistence diagram points. See https://diglib.eg.org/handle/10.1111/cgf12692 for more details. + """ + def __init__(self, threshold=10): + """ + Constructor for the TopologicalVector class. + + Attributes: + threshold (int): number of distances to keep (default 10). This is the dimension of the topological vector. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding topological vector as threshold. + """ + self.threshold = threshold + + def fit(self, X, y=None): + """ + Fit the TopologicalVector class on a list of persistence diagrams (this function actually does nothing but is useful when TopologicalVector is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + Compute the topological vector for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (**threshold**): output topological vectors. + """ + if self.threshold == -1: + thresh = np.array([X[i].shape[0] for i in range(len(X))]).max() + else: + thresh = self.threshold + + num_diag = len(X) + Xfit = np.zeros([num_diag, thresh]) + + for i in range(num_diag): + + diagram, num_pts_in_diag = X[i], X[i].shape[0] + pers = 0.5 * np.matmul(diagram, np.array([[-1.0],[1.0]])) + min_pers = np.minimum(pers,np.transpose(pers)) + distances = DistanceMetric.get_metric("chebyshev").pairwise(diagram) + vect = np.flip(np.sort(np.triu(np.minimum(distances, min_pers)), axis=None), 0) + dim = min(len(vect), thresh) + Xfit[i, :dim] = vect[:dim] + + return Xfit + +class ComplexPolynomial(BaseEstimator, TransformerMixin): + """ + This is a class for computing complex polynomials from a list of persistence diagrams. The persistence diagram points are seen as the roots of some complex polynomial, whose coefficients are returned in a complex vector. See https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27 for more details. + """ + def __init__(self, F="R", threshold=10): + """ + Constructor for the ComplexPolynomial class. + + Attributes: + F (char): either "R", "S" or "T" (default "R"). Type of complex polynomial that is going to be computed (explained in https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27). + threshold (int): number of coefficients (default 10). This is the dimension of the complex vector of coefficients. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding complex vector of coefficients as threshold. + """ + self.threshold, self.F = threshold, F + + def fit(self, X, y=None): + """ + Fit the ComplexPolynomial class on a list of persistence diagrams (this function actually does nothing but is useful when ComplexPolynomial is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + Compute the complex vector of coefficients for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (**threshold**): output complex vectors of coefficients. + """ + if self.threshold == -1: + thresh = np.array([X[i].shape[0] for i in range(len(X))]).max() + else: + thresh = self.threshold + + Xfit = np.zeros([len(X), thresh]) + 1j * np.zeros([len(X), thresh]) + for d in range(len(X)): + D, N = X[d], X[d].shape[0] + if self.F == "R": + roots = D[:,0] + 1j * D[:,1] + elif self.F == "S": + alpha = np.linalg.norm(D, axis=1) + alpha = np.where(alpha==0, np.ones(N), alpha) + roots = np.multiply( np.multiply( (D[:,0]+1j*D[:,1]), (D[:,1]-D[:,0]) ), 1./(np.sqrt(2)*alpha) ) + elif self.F == "T": + alpha = np.linalg.norm(D, axis=1) + roots = np.multiply( (D[:,1]-D[:,0])/2, np.cos(alpha) - np.sin(alpha) + 1j * (np.cos(alpha) + np.sin(alpha)) ) + coeff = [0] * (N+1) + coeff[N] = 1 + for i in range(1, N+1): + for j in range(N-i-1, N): + coeff[j] += ((-1) * roots[i-1] * coeff[j+1]) + coeff = np.array(coeff[::-1])[1:] + Xfit[d, :min(thresh, coeff.shape[0])] = coeff[:min(thresh, coeff.shape[0])] + return Xfit -- cgit v1.2.3 From 6b5bc4da619d96129d31764f477afeda49670ccf Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Thu, 13 Jun 2019 17:51:59 -0400 Subject: added sklearn_tda kernels --- src/cython/sktda/kernel_methods.py | 202 +++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 src/cython/sktda/kernel_methods.py (limited to 'src') diff --git a/src/cython/sktda/kernel_methods.py b/src/cython/sktda/kernel_methods.py new file mode 100644 index 00000000..57bfafd7 --- /dev/null +++ b/src/cython/sktda/kernel_methods.py @@ -0,0 +1,202 @@ +""" +@author: Mathieu Carriere +All rights reserved +""" + +import numpy as np +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.metrics import pairwise_distances +from metrics import SlicedWassersteinDistance, PersistenceFisherDistance + +############################################# +# Kernel methods ############################ +############################################# + +class SlicedWassersteinKernel(BaseEstimator, TransformerMixin): + """ + This is a class for computing the sliced Wasserstein kernel matrix from a list of persistence diagrams. The sliced Wasserstein kernel is computed by exponentiating the corresponding sliced Wasserstein distance with a Gaussian kernel. See http://proceedings.mlr.press/v70/carriere17a.html for more details. + """ + def __init__(self, num_directions=10, bandwidth=1.0): + """ + Constructor for the SlicedWassersteinDistance class. + + Attributes: + bandwidth (double): bandwidth of the Gaussian kernel applied to the sliced Wasserstein distance (default 1.). + num_directions (int): number of lines to sample uniformly from [-pi,pi] in order to approximate and speed up the kernel computation (default 10). If -1, the exact kernel is computed. + """ + self.bandwidth = bandwidth + self.sw_ = SlicedWassersteinDistance(num_directions=num_directions) + + def fit(self, X, y=None): + """ + Fit the SlicedWassersteinKernel class on a list of persistence diagrams: an instance of the SlicedWassersteinDistance class is fitted on the diagrams and then stored. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.sw_.fit(X, y) + return self + + def transform(self, X): + """ + Compute all sliced Wasserstein kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise sliced Wasserstein kernel values. + """ + return np.exp(-self.sw_.transform(X)/self.bandwidth) + +class PersistenceWeightedGaussianKernel(BaseEstimator, TransformerMixin): + """ + This is a class for computing the persistence weighted Gaussian kernel matrix from a list of persistence diagrams. The persistence weighted Gaussian kernel is computed by convolving the persistence diagram points with weighted Gaussian kernels. See http://proceedings.mlr.press/v48/kusano16.html for more details. + """ + def __init__(self, bandwidth=1., weight=lambda x: 1, kernel_approx=None): + """ + Constructor for the PersistenceWeightedGaussianKernel class. + + Attributes: + bandwidth (double): bandwidth of the Gaussian kernel with which persistence diagrams will be convolved (default 1.) + weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie lists or numpy arrays of the form [p_x,p_y]. + kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). + """ + self.bandwidth, self.weight = bandwidth, weight + self.kernel_approx = kernel_approx + + def fit(self, X, y=None): + """ + Fit the PersistenceWeightedGaussianKernel class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams** and the kernel approximation class (if not None) is applied on them. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = list(X) + self.ws_ = [ np.array([self.weight(self.diagrams_[i][j,:]) for j in range(self.diagrams_[i].shape[0])]) for i in range(len(self.diagrams_)) ] + if self.kernel_approx is not None: + self.approx_ = np.concatenate([np.sum(np.multiply(self.ws_[i][:,np.newaxis], self.kernel_approx.transform(self.diagrams_[i])), axis=0)[np.newaxis,:] for i in range(len(self.diagrams_))]) + return self + + def transform(self, X): + """ + Compute all sliced Wasserstein kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence weighted Gaussian kernel values. + """ + Xp = list(X) + Xfit = np.zeros((len(Xp), len(self.diagrams_))) + if len(self.diagrams_) == len(Xp) and np.all([np.array_equal(self.diagrams_[i], Xp[i]) for i in range(len(Xp))]): + if self.kernel_approx is not None: + Xfit = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.matmul(self.approx_, self.approx_.T) + else: + for i in range(len(self.diagrams_)): + for j in range(i+1, len(self.diagrams_)): + W = np.matmul(self.ws_[i][:,np.newaxis], self.ws_[j][np.newaxis,:]) + E = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.exp(-np.square(pairwise_distances(self.diagrams_[i], self.diagrams_[j]))/(2*np.square(self.bandwidth))) + Xfit[i,j] = np.sum(np.multiply(W, E)) + Xfit[j,i] = X[i,j] + else: + ws = [ np.array([self.weight(Xp[i][j,:]) for j in range(Xp[i].shape[0])]) for i in range(len(Xp)) ] + if self.kernel_approx is not None: + approx = np.concatenate([np.sum(np.multiply(ws[i][:,np.newaxis], self.kernel_approx.transform(Xp[i])), axis=0)[np.newaxis,:] for i in range(len(Xp))]) + Xfit = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.matmul(approx, self.approx_.T) + else: + for i in range(len(Xp)): + for j in range(len(self.diagrams_)): + W = np.matmul(ws[i][:,np.newaxis], self.ws_[j][np.newaxis,:]) + E = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.exp(-np.square(pairwise_distances(Xp[i], self.diagrams_[j]))/(2*np.square(self.bandwidth))) + Xfit[i,j] = np.sum(np.multiply(W, E)) + + return Xfit + +class PersistenceScaleSpaceKernel(BaseEstimator, TransformerMixin): + """ + This is a class for computing the persistence scale space kernel matrix from a list of persistence diagrams. The persistence scale space kernel is computed by adding the symmetric to the diagonal of each point in each persistence diagram, and then convolving the points with a Gaussian kernel. See https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Reininghaus_A_Stable_Multi-Scale_2015_CVPR_paper.pdf for more details. + """ + def __init__(self, bandwidth=1., kernel_approx=None): + """ + Constructor for the PersistenceScaleSpaceKernel class. + + Attributes: + bandwidth (double): bandwidth of the Gaussian kernel with which persistence diagrams will be convolved (default 1.) + kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). + """ + self.pwg_ = PersistenceWeightedGaussianKernel(bandwidth=bandwidth, weight=lambda x: 1 if x[1] >= x[0] else -1, kernel_approx=kernel_approx) + + def fit(self, X, y=None): + """ + Fit the PersistenceScaleSpaceKernel class on a list of persistence diagrams: symmetric to the diagonal of all points are computed and an instance of the PersistenceWeightedGaussianKernel class is fitted on the diagrams and then stored. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = list(X) + for i in range(len(self.diagrams_)): + op_D = np.matmul(self.diagrams_[i], np.array([[0.,1.], [1.,0.]])) + self.diagrams_[i] = np.concatenate([self.diagrams_[i], op_D], axis=0) + self.pwg_.fit(X) + return self + + def transform(self, X): + """ + Compute all persistence scale space kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence scale space kernel values. + """ + Xp = list(X) + for i in range(len(Xp)): + op_X = np.matmul(Xp[i], np.array([[0.,1.], [1.,0.]])) + Xp[i] = np.concatenate([Xp[i], op_X], axis=0) + return self.pwg_.transform(Xp) + +class PersistenceFisherKernel(BaseEstimator, TransformerMixin): + """ + This is a class for computing the persistence Fisher kernel matrix from a list of persistence diagrams. The persistence Fisher kernel is computed by exponentiating the corresponding persistence Fisher distance with a Gaussian kernel. See papers.nips.cc/paper/8205-persistence-fisher-kernel-a-riemannian-manifold-kernel-for-persistence-diagrams for more details. + """ + def __init__(self, bandwidth_fisher=1., bandwidth=1., kernel_approx=None): + """ + Constructor for the PersistenceFisherKernel class. + + Attributes: + bandwidth (double): bandwidth of the Gaussian kernel applied to the persistence Fisher distance (default 1.). + bandwidth_fisher (double): bandwidth of the Gaussian kernel used to turn persistence diagrams into probability distributions by PersistenceFisherDistance class (default 1.). + kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). + """ + self.bandwidth = bandwidth + self.pf_ = PersistenceFisherDistance(bandwidth=bandwidth_fisher, kernel_approx=kernel_approx) + + def fit(self, X, y=None): + """ + Fit the PersistenceFisherKernel class on a list of persistence diagrams: an instance of the PersistenceFisherDistance class is fitted on the diagrams and then stored. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.pf_.fit(X, y) + return self + + def transform(self, X): + """ + Compute all persistence Fisher kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence Fisher kernel values. + """ + return np.exp(-self.pf_.transform(X)/self.bandwidth) + -- cgit v1.2.3 From a27340146f7b82d7e64c6e9236141354232b56e1 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Thu, 13 Jun 2019 17:52:11 -0400 Subject: added sklearn_tda metrics --- src/cython/sktda/metrics.py | 226 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 src/cython/sktda/metrics.py (limited to 'src') diff --git a/src/cython/sktda/metrics.py b/src/cython/sktda/metrics.py new file mode 100644 index 00000000..05141e8b --- /dev/null +++ b/src/cython/sktda/metrics.py @@ -0,0 +1,226 @@ +""" +@author: Mathieu Carriere +All rights reserved +""" + +import sys +import numpy as np +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.metrics import pairwise_distances +try: + from gudhi import bottleneck_distance + USE_GUDHI = True +except ImportError: + USE_GUDHI = False + print("Gudhi not found: BottleneckDistance will return null matrix, and exact SlicedWassersteinDistance not available") + +############################################# +# Metrics ################################### +############################################# + +class SlicedWassersteinDistance(BaseEstimator, TransformerMixin): + """ + This is a class for computing the sliced Wasserstein distance matrix from a list of persistence diagrams. The Sliced Wasserstein distance is computed by projecting the persistence diagrams onto lines, comparing the projections with the 1-norm, and finally integrating over all possible lines. See http://proceedings.mlr.press/v70/carriere17a.html for more details. + """ + def __init__(self, num_directions=10): + """ + Constructor for the SlicedWassersteinDistance class. + + Attributes: + num_directions (int): number of lines to sample uniformly from [-pi,pi] in order to approximate and speed up the distance computation (default 10). + """ + self.num_directions = num_directions + thetas = np.linspace(-np.pi/2, np.pi/2, num=self.num_directions+1)[np.newaxis,:-1] + self.lines_ = np.concatenate([np.cos(thetas), np.sin(thetas)], axis=0) + + def fit(self, X, y=None): + """ + Fit the SlicedWassersteinDistance class on a list of persistence diagrams: persistence diagrams are projected onto the different lines. The diagrams themselves and their projections are then stored in a numpy array called **diagrams**. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = X + self.approx_ = [np.matmul(X[i], self.lines_) for i in range(len(X))] + diag_proj = (1./2) * np.ones((2,2)) + self.approx_diag_ = [np.matmul(np.matmul(X[i], diag_proj), self.lines_) for i in range(len(X))] + return self + + def transform(self, X): + """ + Compute all sliced Wasserstein distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise sliced Wasserstein distances. + """ + Xfit = np.zeros((len(X), len(self.approx_))) + if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): + for i in range(len(self.approx_)): + for j in range(i+1, len(self.approx_)): + A = np.sort(np.concatenate([self.approx_[i], self.approx_diag_[j]], axis=0), axis=0) + B = np.sort(np.concatenate([self.approx_[j], self.approx_diag_[i]], axis=0), axis=0) + L1 = np.sum(np.abs(A-B), axis=0) + Xfit[i,j] = np.mean(L1) + Xfit[j,i] = Xfit[i,j] + else: + diag_proj = (1./2) * np.ones((2,2)) + approx = [np.matmul(X[i], self.lines_) for i in range(len(X))] + approx_diag = [np.matmul(np.matmul(X[i], diag_proj), self.lines_) for i in range(len(X))] + for i in range(len(approx)): + for j in range(len(self.approx_)): + A = np.sort(np.concatenate([approx[i], self.approx_diag_[j]], axis=0), axis=0) + B = np.sort(np.concatenate([self.approx_[j], approx_diag[i]], axis=0), axis=0) + L1 = np.sum(np.abs(A-B), axis=0) + Xfit[i,j] = np.mean(L1) + + return Xfit + +class BottleneckDistance(BaseEstimator, TransformerMixin): + """ + This is a class for computing the bottleneck distance matrix from a list of persistence diagrams. + """ + def __init__(self, epsilon=1e-3): + """ + Constructor for the BottleneckDistance class. + + Attributes: + epsilon (double): approximation quality (default 1e-4). + """ + self.epsilon = epsilon + + def fit(self, X, y=None): + """ + Fit the BottleneckDistance class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams**. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = X + return self + + def transform(self, X): + """ + Compute all bottleneck distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise bottleneck distances. + """ + num_diag1 = len(X) + + if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): + matrix = np.zeros((num_diag1, num_diag1)) + + if USE_GUDHI: + for i in range(num_diag1): + #sys.stdout.write( str(i*1.0 / num_diag1) + "\r") + for j in range(i+1, num_diag1): + matrix[i,j] = bottleneck_distance(X[i], X[j], self.epsilon) + matrix[j,i] = matrix[i,j] + else: + print("Gudhi required---returning null matrix") + + else: + num_diag2 = len(self.diagrams_) + matrix = np.zeros((num_diag1, num_diag2)) + + if USE_GUDHI: + for i in range(num_diag1): + #sys.stdout.write( str(i*1.0 / num_diag1) + "\r") + for j in range(num_diag2): + matrix[i,j] = bottleneck_distance(X[i], self.diagrams_[j], self.epsilon) + else: + print("Gudhi required---returning null matrix") + + Xfit = matrix + + return Xfit + +class PersistenceFisherDistance(BaseEstimator, TransformerMixin): + """ + This is a class for computing the persistence Fisher distance matrix from a list of persistence diagrams. The persistence Fisher distance is obtained by computing the original Fisher distance between the probability distributions associated to the persistence diagrams given by convolving them with a Gaussian kernel. See papers.nips.cc/paper/8205-persistence-fisher-kernel-a-riemannian-manifold-kernel-for-persistence-diagrams for more details. + """ + def __init__(self, bandwidth=1., kernel_approx=None): + """ + Constructor for the PersistenceFisherDistance class. + + Attributes: + bandwidth (double): bandwidth of the Gaussian kernel used to turn persistence diagrams into probability distributions (default 1.). + kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). + """ + self.bandwidth, self.kernel_approx = bandwidth, kernel_approx + + def fit(self, X, y=None): + """ + Fit the PersistenceFisherDistance class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams** and the kernel approximation class (if not None) is applied on them. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = X + projection = (1./2) * np.ones((2,2)) + self.diagonal_projections_ = [np.matmul(X[i], projection) for i in range(len(X))] + if self.kernel_approx is not None: + self.approx_ = [self.kernel_approx.transform(X[i]) for i in range(len(X))] + self.approx_diagonal_ = [self.kernel_approx.transform(self.diagonal_projections_[i]) for i in range(len(X))] + return self + + def transform(self, X): + """ + Compute all persistence Fisher distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence Fisher distances. + """ + Xfit = np.zeros((len(X), len(self.diagrams_))) + if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): + for i in range(len(self.diagrams_)): + for j in range(i+1, len(self.diagrams_)): + if self.kernel_approx is not None: + Z = np.concatenate([self.approx_[i], self.approx_diagonal_[i], self.approx_[j], self.approx_diagonal_[j]], axis=0) + U, V = np.sum(np.concatenate([self.approx_[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], self.approx_diagonal_[i]], axis=0), axis=0) + vectori, vectorj = np.matmul(Z, U.T), np.matmul(Z, V.T) + vectori, vectorj = vectori/np.sum(vectori), vectorj/np.sum(vectorj) + Xfit[i,j] = np.arccos(np.dot(np.sqrt(vectori), np.sqrt(vectorj))) + Xfit[j,i] = Xfit[i,j] + else: + Z = np.concatenate([self.diagrams_[i], self.diagonal_projections_[i], self.diagrams_[j], self.diagonal_projections_[j]], axis=0) + U, V = np.concatenate([self.diagrams_[i], self.diagonal_projections_[j]], axis=0), np.concatenate([self.diagrams_[j], self.diagonal_projections_[i]], axis=0) + vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) + vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) + vectori, vectorj = vectori/np.sum(vectori), vectorj/np.sum(vectorj) + Xfit[i,j] = np.arccos(np.dot(np.sqrt(vectori), np.sqrt(vectorj))) + Xfit[j,i] = Xfit[i,j] + else: + projection = (1./2) * np.ones((2,2)) + diagonal_projections = [np.matmul(X[i], projection) for i in range(len(X))] + if self.kernel_approx is not None: + approx = [self.kernel_approx.transform(X[i]) for i in range(len(X))] + approx_diagonal = [self.kernel_approx.transform(diagonal_projections[i]) for i in range(len(X))] + for i in range(len(X)): + for j in range(len(self.diagrams_)): + if self.kernel_approx is not None: + Z = np.concatenate([approx[i], approx_diagonal[i], self.approx_[j], self.approx_diagonal_[j]], axis=0) + U, V = np.sum(np.concatenate([approx[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], approx_diagonal[i]], axis=0), axis=0) + vectori, vectorj = np.matmul(Z, U.T), np.matmul(Z, V.T) + vectori, vectorj = vectori/np.sum(vectori), vectorj/np.sum(vectorj) + Xfit[i,j] = np.arccos(np.dot(np.sqrt(vectori), np.sqrt(vectorj))) + else: + Z = np.concatenate([X[i], diagonal_projections[i], self.diagrams_[j], self.diagonal_projections_[j]], axis=0) + U, V = np.concatenate([X[i], self.diagonal_projections_[j]], axis=0), np.concatenate([self.diagrams_[j], diagonal_projections[i]], axis=0) + vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) + vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) + vectori, vectorj = vectori/np.sum(vectori), vectorj/np.sum(vectorj) + Xfit[i,j] = np.arccos(np.dot(np.sqrt(vectori), np.sqrt(vectorj))) + return Xfit -- cgit v1.2.3 From b0218cdb108b444b0403cfd3a782158a6772fc30 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Thu, 13 Jun 2019 17:52:25 -0400 Subject: added sklearn_tda clustering --- src/cython/sktda/clustering.py | 269 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 src/cython/sktda/clustering.py (limited to 'src') diff --git a/src/cython/sktda/clustering.py b/src/cython/sktda/clustering.py new file mode 100644 index 00000000..d3dd531b --- /dev/null +++ b/src/cython/sktda/clustering.py @@ -0,0 +1,269 @@ +""" +@author: Mathieu Carriere +All rights reserved +""" + +import numpy as np +import itertools + +from metrics import BottleneckDistance +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.cluster import DBSCAN, AgglomerativeClustering +from sklearn.metrics import pairwise_distances +from sklearn.neighbors import radius_neighbors_graph, kneighbors_graph +from scipy.spatial.distance import directed_hausdorff +from scipy.sparse import csgraph + +try: + import gudhi as gd + USE_GUDHI = True + +except ImportError: + USE_GUDHI = False + print("Gudhi not found: MapperComplex not available") + +############################################# +# Clustering ################################ +############################################# + +class MapperComplex(BaseEstimator, TransformerMixin): + """ + This is a class for computing Mapper simplicial complexes on point clouds or distance matrices. + """ + def __init__(self, filters, filter_bnds, colors, resolutions, gains, inp="point cloud", clustering=DBSCAN(), mask=0): + """ + Constructor for the MapperComplex class. + + Attributes: + inp (string): either "point cloud" or "distance matrix". Specifies the type of input data. + filters (numpy array of shape (num_points) x (num_filters)): filters (sometimes called lenses) used to compute the Mapper. Each column of the numpy array defines a scalar function defined on the input points. + filter_bnds (numpy array of shape (num_filters) x 2): limits of each filter, of the form [[f_1^min, f_1^max], ..., [f_n^min, f_n^max]]. If one of the values is numpy.nan, it can be computed from the points with the fit() method. + colors (numpy array of shape (num_points) x (num_colors)): functions used to color the nodes of the output Mapper simplicial complex. More specifically, coloring is done by computing the means of these functions on the subpopulations corresponding to each node. It can be the same as filters. + resolutions (numpy array of shape num_filters containing integers): resolution of each filter, ie number of intervals required to cover each filter image. + gains (numpy array of shape num_filters containing doubles in [0,1]): gain of each filter, ie overlap percentage of the intervals covering each filter image. + clustering (class): clustering class (default sklearn.cluster.DBSCAN()). Common clustering classes can be found in the scikit-learn library (such as AgglomerativeClustering for instance). + mask (int): threshold on the size of the Mapper nodes (default 0). Any node associated to a subpopulation with less than **mask** points will be removed. + + mapper_ (gudhi SimplexTree): Mapper simplicial complex computed after calling the fit() method + node_info_ (dictionary): various information associated to the nodes of the Mapper. + """ + self.filters, self.filter_bnds, self.resolutions, self.gains, self.colors, self.clustering = filters, filter_bnds, resolutions, gains, colors, clustering + self.input, self.mask = inp, mask + + def get_optimal_parameters_for_agglomerative_clustering(self, X, beta=0., C=10., N=100): + """ + Compute optimal scale and resolutions for a point cloud or a distance matrix. + + Parameters: + X (numpy array of shape (num_points) x (num_coordinates) if point cloud and (num_points) x (num_points) if distance matrix): input point cloud or distance matrix. + beta (double): exponent parameter (default 0.). See http://www.jmlr.org/papers/volume19/17-291/17-291.pdf for details. + C (double): constant parameter (default 10.). See http://www.jmlr.org/papers/volume19/17-291/17-291.pdf for details. + N (int): subsampling iterations (default 100). See http://www.jmlr.org/papers/volume19/17-291/17-291.pdf for details. + + Returns: + delta (double): optimal scale that can be used with agglomerative clustering. + resolutions (numpy array of shape (num_filters): optimal resolutions associated to each filter. + """ + num_pts, num_filt, delta = X.shape[0], self.filters.shape[1], 0 + m = int( num_pts / np.exp((1+beta) * np.log(np.log(num_pts)/np.log(C))) ) + for _ in range(N): + subpop = np.random.choice(num_pts, size=m, replace=False) + if self.input == "point cloud": + d, _, _ = directed_hausdorff(X, X[subpop,:]) + if self.input == "distance matrix": + d = np.max(np.min(X[:,subpop], axis=1), axis=0) + delta += d/N + + pairwise = pairwise_distances(X, metric="euclidean") if self.input == "point cloud" else X + pairs = np.argwhere(pairwise <= delta) + num_pairs = pairs.shape[0] + res = [] + for f in range(num_filt): + F = self.filters[:,f] + minf, maxf = np.min(F), np.max(F) + resf = 0 + for p in range(num_pairs): + resf = max(resf, abs(F[pairs[p,0]] - F[pairs[p,1]])) + res.append(int((maxf-minf)/resf)) + + return delta, np.array(res) + + + def fit(self, X, y=None): + """ + Fit the MapperComplex class on a point cloud or a distance matrix: compute the Mapper and store it in a simplex tree called mapper_ + + Parameters: + X (numpy array of shape (num_points) x (num_coordinates) if point cloud and (num_points) x (num_points) if distance matrix): input point cloud or distance matrix. + y (n x 1 array): point labels (unused). + """ + num_pts, num_filters, num_colors = self.filters.shape[0], self.filters.shape[1], self.colors.shape[1] + + # If some resolutions are not specified, automatically compute them + if np.any(np.isnan(self.resolutions)): + delta, resolutions = self.get_optimal_parameters_for_agglomerative_clustering(X=X, beta=0., C=10, N=100) + #self.clustering = NNClustering(radius=delta, inp=self.input) + if self.input == "point cloud": + self.clustering = AgglomerativeClustering(n_clusters=None, linkage="single", distance_threshold=delta, affinity="euclidean") + else: + self.clustering = AgglomerativeClustering(n_clusters=None, linkage="single", distance_threshold=delta, affinity="precomputed") + self.resolutions = np.where(np.isnan(self.resolutions), resolutions, self.resolutions) + + # If some filter limits are unspecified, automatically compute them + self.filter_bnds = np.where(np.isnan(self.filter_bnds), np.hstack([np.min(self.filters, axis=0)[:,np.newaxis], np.max(self.filters, axis=0)[:,np.newaxis]]), self.filter_bnds) + + # Initialize attributes + self.mapper_, self.node_info_ = gd.SimplexTree(), {} + + # Compute which points fall in which patch or patch intersections + interval_inds, intersec_inds = np.empty(self.filters.shape), np.empty(self.filters.shape) + for i in range(num_filters): + f, r, g = self.filters[:,i], self.resolutions[i], self.gains[i] + min_f, max_f = self.filter_bnds[i,0], np.nextafter(self.filter_bnds[i,1], np.inf) + interval_endpoints, l = np.linspace(min_f, max_f, num=r+1, retstep=True) + intersec_endpoints = [] + for j in range(1, len(interval_endpoints)-1): + intersec_endpoints.append(interval_endpoints[j] - g*l / (2 - 2*g)) + intersec_endpoints.append(interval_endpoints[j] + g*l / (2 - 2*g)) + interval_inds[:,i] = np.digitize(f, interval_endpoints) + intersec_inds[:,i] = 0.5 * (np.digitize(f, intersec_endpoints) + 1) + + # Build the binned_data map that takes a patch or a patch intersection and outputs the indices of the points contained in it + binned_data = {} + for i in range(num_pts): + list_preimage = [] + for j in range(num_filters): + a, b = interval_inds[i,j], intersec_inds[i,j] + list_preimage.append([a]) + if b == a: + list_preimage[j].append(a+1) + if b == a-1: + list_preimage[j].append(a-1) + list_preimage = list(itertools.product(*list_preimage)) + for pre_idx in list_preimage: + try: + binned_data[pre_idx].append(i) + except KeyError: + binned_data[pre_idx] = [i] + + # Initialize the cover map, that takes a point and outputs the clusters to which it belongs + cover, clus_base = [[] for _ in range(num_pts)], 0 + + # For each patch + for preimage in binned_data: + + # Apply clustering on the corresponding subpopulation + idxs = np.array(binned_data[preimage]) + if len(idxs) > 1: + clusters = self.clustering.fit_predict(X[idxs,:]) if self.input == "point cloud" else self.clustering.fit_predict(X[idxs,:][:,idxs]) + elif len(idxs) == 1: + clusters = np.array([0]) + else: + continue + + # Collect various information on each cluster + num_clus_pre = np.max(clusters) + 1 + for clus_i in range(num_clus_pre): + node_name = clus_base + clus_i + subpopulation = idxs[clusters == clus_i] + if len(subpopulation) >= self.mask: + self.node_info_[node_name] = {} + self.node_info_[node_name]["indices"] = subpopulation + self.node_info_[node_name]["size"] = len(subpopulation) + self.node_info_[node_name]["colors"] = np.mean(self.colors[subpopulation,:], axis=0) + self.node_info_[node_name]["patch"] = preimage + + # Update the cover map + for pt in range(clusters.shape[0]): + node_name = clus_base + clusters[pt] + if clusters[pt] != -1 and self.node_info_[node_name]["size"] >= self.mask: + cover[idxs[pt]].append(node_name) + + clus_base += np.max(clusters) + 1 + + # Insert the simplices of the Mapper complex + for i in range(num_pts): + self.mapper_.insert(cover[i], filtration=-3) + self.mapper_.initialize_filtration() + + return self + + def compute_persistence_diagrams(self): + """ + Compute the extended persistence diagrams of the Mapper simplicial complex associated to each color function. + + Returns: + list_dgm (list of gudhi persistence diagrams): output extended persistence diagrams. There is one per color function. + """ + num_cols, list_dgm = self.colors.shape[1], [] + + # Compute an extended persistence diagram for each color + for c in range(num_cols): + + # Retrieve all color values + col_vals = {node_name: self.node_info_[node_name]["colors"][c] for node_name in self.node_info_.keys()} + + # Create a new simplicial complex by coning the Mapper with an extra point with name -2 + st = gd.SimplexTree() + list_simplices, list_vertices = self.mapper_.get_skeleton(1), self.mapper_.get_skeleton(0) + for (simplex, f) in list_simplices: + st.insert(simplex + [-2], filtration=-3) + + # Assign ascending filtration values on the original simplices and descending filtration values on the coned simplices + min_val, max_val = min(col_vals), max(col_vals) + for (vertex, f) in list_vertices: + if st.find(vertex): + st.assign_filtration(vertex, filtration = -2 + (col_vals[vertex[0]]-min_val)/(max_val-min_val)) + st.assign_filtration(vertex + [-2], filtration = 2 - (col_vals[vertex[0]]-min_val)/(max_val-min_val)) + + # Compute persistence + st.make_filtration_non_decreasing() + dgm = st.persistence() + + # Output extended persistence diagrams + for point in range(len(dgm)): + b,d = dgm[point][1][0], dgm[point][1][1] + b,d = min_val+(2-abs(b))*(max_val-min_val), min_val+(2-abs(d))*(max_val-min_val) + dgm[point] = tuple([dgm[point][0], tuple([b,d])]) + list_dgm.append(dgm) + + return list_dgm + + def compute_distribution(self, X, N=100): + """ + Compute a bootstrap distribution of bottleneck distances. More specifically, subsample the input point cloud or distance matrix, compute the Mapper with the same parameters on this subsample, and compare its extended persistence diagrams with the original ones. + + Parameters: + X (numpy array of shape (num_points) x (num_coordinates) if point cloud and (num_points) x (num_points) if distance matrix): input point cloud or distance matrix. + N (int): bootstrap iterations (default 100). + + Returns: + distribution: list of bottleneck distance values. + """ + num_pts, distribution = len(X), [] + dgm = self.compute_persistence_diagrams() + + for bootstrap_id in range(N): + + print(str(bootstrap_id) + "th iteration") + + # Randomly select points + idxs = np.random.choice(num_pts, size=num_pts, replace=True) + Xboot = X[idxs,:] if self.input == "point cloud" else X[idxs,:][:,idxs] + f_boot, c_boot = self.filters[idxs,:], self.colors[idxs,:] + Mboot = self.__class__(filters=f_boot, filter_bnds=self.filter_bnds, colors=c_boot, resolutions=self.resolutions, gains=self.gains, inp=self.input, clustering=self.clustering).fit(Xboot) + + # Compute the corresponding persistence diagrams + dgm_boot = Mboot.compute_persistence_diagrams() + + # Compute the bottleneck distances between them and keep the maximum + df = 0. + for i in range(len(dgm)): + npts, npts_boot = len(dgm[i]), len(dgm_boot[i]) + D1 = np.array([[dgm[i][pt][1][0], dgm[i][pt][1][1]] for pt in range(npts) if dgm[i][pt][0] <= 1]) + D2 = np.array([[dgm_boot[i][pt][1][0], dgm_boot[i][pt][1][1]] for pt in range(npts_boot) if dgm_boot[i][pt][0] <= 1]) + bottle = BottleneckDistance().fit([D1]) + df = max(df, float(np.squeeze(bottle.transform([D2])))) + distribution.append(df) + + return np.sort(distribution) -- cgit v1.2.3 From 7157d123ffd425ec104e0c468d24d48d7ce2fcd0 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Thu, 13 Jun 2019 17:52:38 -0400 Subject: added sklearn_tda examples --- src/cython/example/ex_clustering.py | 64 +++++++++++++++++++++ src/cython/example/ex_diagrams.py | 107 ++++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 src/cython/example/ex_clustering.py create mode 100644 src/cython/example/ex_diagrams.py (limited to 'src') diff --git a/src/cython/example/ex_clustering.py b/src/cython/example/ex_clustering.py new file mode 100644 index 00000000..bee82414 --- /dev/null +++ b/src/cython/example/ex_clustering.py @@ -0,0 +1,64 @@ +import numpy as np +from sklearn.metrics import pairwise_distances +import os +import gudhi as gd +import sys +sys.path.append("../sktda/") +from clustering import * + +X = np.loadtxt("human") + +print("Mapper computation with point cloud") +mapper = MapperComplex(inp="point cloud", + filters=X[:,[2,0]], + filter_bnds=np.array([[np.nan,np.nan],[np.nan,np.nan]]), + resolutions=np.array([np.nan,np.nan]), gains=np.array([0.33,0.33]), + colors=X[:,2:3], + ).fit(X) + +f = open("mapper_pc", "w") +f.write("%s\n%s\n%s\n%f %f\n%d %d\n" % ("human", "coord2-0", "coord2", 10, 0.33, len(mapper.mapper_.get_skeleton(0)), len([edge for (edge,f) in mapper.mapper_.get_skeleton(1) if len(edge)==2]))) +for (vertex,_) in mapper.mapper_.get_skeleton(0): + f.write(str(vertex[0]) + " " + str(mapper.node_info_[vertex[0]]["colors"][0]) + " " + str(mapper.node_info_[vertex[0]]["size"]) + "\n") +for (edge,_) in mapper.mapper_.get_skeleton(1): + if len(edge) == 2: + f.write(str(edge[0]) + " " + str(edge[1]) + "\n") +f.close() + +os.system("python3 ~/Git/gudhi-devel/src/Nerve_GIC/utilities/KeplerMapperVisuFromTxtFile.py -f ~/Git/gudhi-devel/src/cython/example/mapper_pc") +os.system("rm ~/Git/gudhi-devel/src/cython/example/mapper_pc") + +dgms = mapper.compute_persistence_diagrams() +plot = gd.plot_persistence_diagram(dgms[0]) +plot.show() + +distrib = mapper.compute_distribution(X, N=10) +print("Distance threshold associated to confidence 90 percent is " + str(distrib[int(np.floor(0.9 * len(distrib)))])) + +print("Mapper computation with pairwise distances only") +X = pairwise_distances(X) +mapper = MapperComplex(inp="distance matrix", + filters=X[:,[2,0]], + filter_bnds=np.array([[np.nan,np.nan],[np.nan,np.nan]]), + resolutions=np.array([np.nan,np.nan]), gains=np.array([0.33,0.33]), + colors=np.max(X, axis=1)[:,np.newaxis], + ).fit(X) + +f = open("mapper_dm", "w") +f.write("%s\n%s\n%s\n%f %f\n%d %d\n" % ("human", "coord2-0", "coord2", 10, 0.33, len(mapper.mapper_.get_skeleton(0)), len([edge for (edge,f) in mapper.mapper_.get_skeleton(1) if len(edge)==2]))) +for (vertex,_) in mapper.mapper_.get_skeleton(0): + f.write(str(vertex[0]) + " " + str(mapper.node_info_[vertex[0]]["colors"][0]) + " " + str(mapper.node_info_[vertex[0]]["size"]) + "\n") +for (edge,_) in mapper.mapper_.get_skeleton(1): + if len(edge) == 2: + f.write(str(edge[0]) + " " + str(edge[1]) + "\n") +f.close() + +os.system("python3 ~/Git/gudhi-devel/src/Nerve_GIC/utilities/KeplerMapperVisuFromTxtFile.py -f ~/Git/gudhi-devel/src/cython/example/mapper_dm") +os.system("rm ~/Git/gudhi-devel/src/cython/example/mapper_dm") + +dgms = mapper.compute_persistence_diagrams() +plot = gd.plot_persistence_diagram(dgms[0]) +plot.show() + +distrib = mapper.compute_distribution(X, N=10) +print("Distance threshold associated to confidence 90 percent is " + str(distrib[int(np.floor(0.9 * len(distrib)))])) diff --git a/src/cython/example/ex_diagrams.py b/src/cython/example/ex_diagrams.py new file mode 100644 index 00000000..aee20245 --- /dev/null +++ b/src/cython/example/ex_diagrams.py @@ -0,0 +1,107 @@ +import matplotlib.pyplot as plt +import numpy as np +from sklearn.kernel_approximation import RBFSampler + +import sys +sys.path.append("../sktda/") +from preprocessing import * +from metrics import * +from vector_methods import * +from kernel_methods import * + +D = np.array([[0.,4.],[1.,2.],[3.,8.],[6.,8.]]) +plt.scatter(D[:,0],D[:,1]) +plt.plot([0.,10.],[0.,10.]) +plt.show() + +diags = [D] + +LS = Landscape(resolution = 1000) +L = LS.fit_transform(diags) +plt.plot(L[0][:1000]) +plt.plot(L[0][1000:2000]) +plt.plot(L[0][2000:3000]) +plt.show() + +def pow(n): + return lambda x: np.power(x[1]-x[0],n) + +SH = Silhouette(resolution=1000, weight=pow(2)) +sh = SH.fit_transform(diags) +plt.plot(sh[0]) +plt.show() + +BC = BettiCurve(resolution=1000) +bc = BC.fit_transform(diags) +plt.plot(bc[0]) +plt.show() + +CP = ComplexPolynomial(threshold=-1, F="T") +cp = CP.fit_transform(diags) +print("Complex polynomial is " + str(cp[0,:])) + +TV = TopologicalVector(threshold=-1) +tv = TV.fit_transform(diags) +print("Topological vector is " + str(tv[0,:])) + +diagsT = DiagramPreprocessor(use=True, scalers=[([0,1], BirthPersistenceTransform())]).fit_transform(diags) +PI = PersistenceImage(bandwidth=1., weight=lambda x: x[1], im_range=[0,10,0,10], resolution=[100,100]) +pi = PI.fit_transform(diagsT) +plt.imshow(np.flip(np.reshape(pi[0], [100,100]), 0)) +plt.show() + +plt.scatter(D[:,0],D[:,1]) +D = np.array([[1.,5.],[3.,6.],[2.,7.]]) +plt.scatter(D[:,0],D[:,1]) +plt.plot([0.,10.],[0.,10.]) +plt.show() + +diags2 = [D] + +def arctan(C,p): + return lambda x: C*np.arctan(np.power(x[1], p)) + +PWG = PersistenceWeightedGaussianKernel(bandwidth=1., kernel_approx=None, weight=arctan(1.,1.)) +X = PWG.fit(diags) +Y = PWG.transform(diags2) +print("PWG kernel is " + str(Y[0][0])) + +PWG = PersistenceWeightedGaussianKernel(kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2])), weight=arctan(1.,1.)) +X = PWG.fit(diags) +Y = PWG.transform(diags2) +print("Approximate PWG kernel is " + str(Y[0][0])) + +PSS = PersistenceScaleSpaceKernel(bandwidth=1.) +X = PSS.fit(diags) +Y = PSS.transform(diags2) +print("PSS kernel is " + str(Y[0][0])) + +PSS = PersistenceScaleSpaceKernel(kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2]))) +X = PSS.fit(diags) +Y = PSS.transform(diags2) +print("Approximate PSS kernel is " + str(Y[0][0])) + +sW = SlicedWassersteinDistance(num_directions=100) +X = sW.fit(diags) +Y = sW.transform(diags2) +print("SW distance is " + str(Y[0][0])) + +SW = SlicedWassersteinKernel(num_directions=100, bandwidth=1.) +X = SW.fit(diags) +Y = SW.transform(diags2) +print("SW kernel is " + str(Y[0][0])) + +W = BottleneckDistance(epsilon=.001) +X = W.fit(diags) +Y = W.transform(diags2) +print("Bottleneck distance is " + str(Y[0][0])) + +PF = PersistenceFisherKernel(bandwidth_fisher=1., bandwidth=1.) +X = PF.fit(diags) +Y = PF.transform(diags2) +print("PF kernel is " + str(Y[0][0])) + +PF = PersistenceFisherKernel(bandwidth_fisher=1., bandwidth=1., kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2]))) +X = PF.fit(diags) +Y = PF.transform(diags2) +print("Approximate PF kernel is " + str(Y[0][0])) -- cgit v1.2.3 From 3cf724a855cef4f47e5cdbb2a0ff2b6b093c6b87 Mon Sep 17 00:00:00 2001 From: mathieu Date: Fri, 14 Jun 2019 10:31:19 -0400 Subject: added human file and updated path in ex_clustering.py --- data/points/human | 4706 +++++++++++++++++++++++++++++++++++ src/cython/example/ex_clustering.py | 2 +- 2 files changed, 4707 insertions(+), 1 deletion(-) create mode 100644 data/points/human (limited to 'src') diff --git a/data/points/human b/data/points/human new file mode 100644 index 00000000..feaa0dd1 --- /dev/null +++ b/data/points/human @@ -0,0 +1,4706 @@ +0.144529 -0.014328 0.381773 +-0.15378 -0.045707 0.361045 +-0.153917 -0.057275 0.371308 +-0.172725 0.007 0.492798 +-0.119664 0.061296 0.365349 +0.128166 -0.068572 0.432762 +0.089198 0.014048 -0.447694 +0.075569 0.021145 -0.417112 + 0.098403 0.03188 -0.416332 +-0.135518 -0.031058 -0.793657 +-0.155207 -0.043962 -0.867084 +-0.155561 -0.039537 -0.782486 +0.047747 0.043681 0.604458 +0.01706 0.058681 0.583267 +0.153119 -0.065476 -0.465974 +0.134237 -0.06509 -0.429525 +0.138009 -0.083138 -0.463111 +0.061936 0.021354 -0.365004 +-0.327282 0.105737 -0.088733 +-0.333168 0.111481 -0.099092 +0.02915 0.038215 0.809093 +0.000855 0.039818 0.813241 +0.188052 0.020848 -0.942916 +0.200327 0.058375 -0.947736 +0.170213 0.052306 -0.931828 +-0.051119 -0.110776 0.111316 +-0.024531 -0.111686 0.078082 +-0.021008 -0.089762 0.10657 +0.158999 0.015347 -0.916402 +0.127084 0.053037 -0.930089 +0.110535 0.0176 -0.450151 +0.12031 0.025078 -0.419837 +0.125486 0.007107 -0.444987 +0.087186 -0.068787 -0.428022 +0.068096 -0.048487 -0.423994 +0.074575 -0.071291 -0.460151 +-0.129151 -0.031845 0.547791 +-0.092828 -0.001702 0.544926 +-0.146897 -0.021057 0.536567 +-0.040908 -0.080471 0.144988 +-0.070348 -0.092228 0.142231 +0.135184 -0.05792 -0.366185 +0.150502 -0.030301 -0.366514 +0.154969 -0.053619 -0.297939 +-0.046419 0.016608 0.551426 +-0.140395 -0.04129 0.102368 +-0.15361 -0.023507 0.066896 +-0.143445 -0.07334 0.071094 +-0.150698 -0.083179 0.012934 +-0.164552 -0.040183 0.016637 +-0.172922 -0.056311 -0.054118 +0.142078 0.114992 0.400106 +0.136581 0.121451 0.422073 +0.118363 -0.052599 -0.969187 +0.110299 -0.090045 -0.973743 +0.106227 -0.067834 -0.951593 +-0.075572 -0.055159 0.227875 +-0.048656 -0.08199 0.316154 +-0.088566 -0.065864 0.297905 +-0.175443 0.052256 -0.931828 +-0.142719 0.087605 -0.938697 +-0.186188 0.080344 -0.942939 +-0.281944 -0.02183 0.181925 +-0.282953 -0.017408 0.129994 +-0.25389 -0.034093 0.168434 +-0.092047 -0.041549 0.557254 +-0.085291 -0.062035 0.558917 +-0.302715 0.009876 0.086968 +-0.282908 -0.00097 0.077127 +-0.117228 0.023298 0.342619 +-0.177404 -0.012747 -0.051392 +-0.272247 -0.020141 0.233158 +-0.254475 -0.039985 0.216094 +-0.017864 0.070352 0.178973 +-0.003695 0.067376 0.163481 +0.030308 0.031315 0.603728 +0.02661 0.115199 0.744608 +0.033244 0.11394 0.710348 +0.012279 0.123046 0.708533 +-0.05487 -0.030769 0.660855 +-0.04444 -0.039595 0.603866 +0.027038 -0.082087 0.737178 +0.023403 -0.083864 0.708068 +0.044858 -0.07186 0.710449 +-0.141696 0.005473 0.082272 +-0.322537 0.044383 -0.093982 +0.137584 0.087655 -0.938697 +0.110404 0.089056 -0.947375 +0.201593 0.059307 -0.975093 +0.19232 0.094332 -0.975768 +0.209811 0.077354 -0.962319 +0.008617 -0.099163 0.047708 +0.005395 -0.079622 0.076357 +0.106371 0.041851 -0.959546 +0.116565 0.024136 -0.939902 +0.10789 0.05867 -0.947567 +0.117802 0.064238 0.011831 +0.099654 0.053985 0.060307 +0.12682 0.046382 0.064253 +0.14171 0.005468 0.082276 +0.162647 0.005991 0.019937 +0.146819 0.043902 0.018713 +-0.086735 -0.081196 0.550877 +-0.142522 -0.064292 0.541934 +-0.101551 -0.092019 0.536874 +-0.147816 -0.090727 -0.053942 +0.089669 -0.075237 -0.296805 +0.110958 -0.094262 -0.219465 +0.067718 -0.086145 -0.213647 +0.035525 -0.10073 0.457763 +0.000152 -0.097146 0.473706 +0.000252 -0.080555 0.318839 +0.16693 0.014297 -0.292232 +0.165909 0.033768 -0.213443 +0.177974 -0.006912 -0.216479 +0.122756 0.049674 -0.332788 +0.11496 0.064838 -0.287502 +0.148695 0.045817 -0.290571 +0.055746 -0.030683 0.660961 +0.033546 -0.066936 0.676952 +0.017439 -0.069226 0.625575 +0.039048 0.038642 -0.121677 +0.035134 0.012056 -0.055409 +0.054628 0.051615 -0.046746 +-0.219382 0.038108 0.176265 +-0.218586 0.03396 0.225697 +-0.200001 0.012939 0.213512 +0.134152 -0.008037 -0.466849 +-0.111507 0.041802 -0.959546 +-0.105326 0.074882 -0.964246 +-0.113025 0.05862 -0.947567 +-0.054482 -0.034932 0.574847 +-0.090128 -0.021415 0.553574 +-0.116313 -0.048454 0.13681 +-0.09038 -0.053234 0.172229 +-0.105186 -0.014928 0.166671 +-0.126942 0.046414 0.064231 +-0.116088 0.034063 0.10285 +0.078883 0.038902 -0.375952 +0.08771 0.046925 -0.353531 +0.166615 -0.02867 -0.506862 +0.15046 -0.019487 -0.47215 +0.156742 -0.040331 -0.46813 +0.049615 0.064657 0.606895 +-0.093081 0.017267 0.197077 +-0.107564 -0.001066 0.279921 +-0.087365 0.043211 0.25934 +-0.229315 0.033998 0.125778 +-0.248089 0.054546 0.138866 +0.106254 -0.066109 -0.3667 +0.125371 -0.075616 -0.30084 +-0.035641 -0.100747 0.457937 +-0.095086 -0.103368 0.460305 +0.018598 0.108161 0.632002 +0.008009 0.110019 0.630723 +-0.145172 -0.126207 -0.962029 +-0.147995 -0.115103 -0.901861 +-0.165635 -0.112794 -0.963402 +0.181052 0.080394 -0.942939 +-0.037409 0.016634 -0.022456 +-0.054935 0.051562 -0.047076 +-0.051074 0.0426 0.012895 +0.024454 -0.043608 -0.124411 +0.030596 -0.06583 -0.057455 +0.026133 -0.020196 -0.061267 +0.004091 -0.07642 0.016114 +-0.143275 0.11508 -0.950869 +-0.10902 0.06955 0.488685 +-0.119427 0.028724 0.512181 +-0.077684 0.050975 0.507111 +0.03316 -0.09173 0.54611 +-0.126953 0.020019 -0.968705 +-0.153991 -0.027525 -0.970453 +-0.162328 0.037423 -0.975575 +0.019284 0.112865 0.690206 +-0.017888 0.004075 0.813135 +-0.036857 0.015351 0.806263 +-0.019684 -0.022438 0.806286 +-0.118067 0.060321 -0.975334 +-0.11084 -0.089559 0.388201 +-0.136103 -0.098275 0.477748 +-0.068592 0.049409 0.668449 +-0.354522 0.111955 -0.119529 +0.133555 0.0107 -0.91375 +0.152919 -0.021289 -0.903386 +-0.047277 0.088388 0.477505 +0.028943 0.100045 0.614915 +-0.178297 -0.027228 -0.973164 +-0.195107 0.015542 -0.975093 +-0.009711 -0.053847 -0.021292 +-0.020989 -0.095217 -0.015669 +-0.206728 0.059258 -0.975093 +-0.160538 0.080674 -0.978019 +-0.028046 0.017736 0.594311 +-0.099769 0.054021 0.060283 +0.292938 0.024667 0.005698 +0.281872 0.042386 0.010061 +0.293815 0.043479 -0.024205 +0.253755 -0.042591 0.24146 +0.236374 -0.050375 0.26034 +0.2334 -0.049604 0.232122 +-0.041908 -0.013527 0.798038 +-0.169026 -0.017468 -0.554275 +-0.180719 -0.036864 -0.610601 +-0.177641 -0.046876 -0.552508 +0.142302 -0.064331 0.542099 +0.091978 -0.041556 0.557259 +0.085357 -0.061971 0.558866 +0.163846 -0.07001 -0.134881 +0.147918 -0.090765 -0.053915 +0.128677 -0.096438 -0.134035 +0.033866 0.08705 0.595856 +-0.061852 0.08285 0.67803 +-0.064405 0.0745 0.705228 +-0.057315 0.087943 0.688282 +0.026825 0.058063 0.011471 +0.00959 0.046503 -0.022489 +7.5e-05 0.067016 0.012148 +0.037289 0.016594 -0.022429 +0.027248 0.032898 -0.019611 +0.051064 0.042566 0.012814 +0.172917 -0.056346 -0.054093 +0.177533 -0.01278 -0.051369 +0.165297 -0.040174 0.016676 +-0.049827 -0.001275 0.628239 +-0.046082 -0.007732 0.588234 +0.027282 0.017653 0.594293 +0.218467 0.03392 0.225725 +0.218303 0.022488 0.260748 +0.246836 0.034226 0.236039 +0.180761 -0.032705 -0.133588 +-0.061259 -0.067857 -0.496632 +-0.054909 -0.056275 -0.544951 +-0.05886 -0.036687 -0.498712 +-0.096128 0.07647 0.350771 +-0.125425 -0.075572 -0.300885 +-0.089749 -0.075321 -0.29683 +-0.111609 -0.094226 -0.21961 +-0.059916 -0.025146 -0.5475 +-0.070274 -0.011925 -0.501404 +-0.014861 0.085999 0.475193 +0.011944 0.087545 0.471872 +-0.013265 0.101124 0.440331 +-0.111865 -0.105085 -0.952919 +-0.111362 -0.067884 -0.951593 +-0.108817 -0.085906 -0.928478 +0.129068 -0.032432 0.547796 +0.092747 -0.004496 0.54493 +-0.154642 0.003294 -0.352704 +-0.142743 0.028986 -0.337671 +-0.069935 -0.024902 -0.463284 +-0.315481 0.074492 -0.075996 +-0.325874 0.058528 -0.097514 +-0.332121 0.079166 -0.097184 +0.065756 0.113544 0.355893 +0.099174 0.128438 0.362935 +0.091993 0.102855 0.353431 +0.150875 -0.083168 0.013016 +-0.108399 -0.048104 0.306063 +-0.047271 0.099741 0.356288 +-0.066219 0.088165 0.321493 +-0.035225 -0.092767 0.543027 +-0.009859 0.104972 0.60128 +-0.043422 -0.067033 0.234382 +-0.197455 0.094282 -0.975768 +-0.104462 -0.111554 -0.498426 +-0.078262 -0.094503 -0.497102 +-0.092157 -0.088636 -0.463068 +-0.146801 -0.077454 -0.220163 +0.086804 -0.081061 0.550911 +0.134521 0.11988 0.378405 +0.118328 0.120392 0.364735 +0.115393 0.14537 0.390518 +-0.054843 -0.015197 0.563407 +-0.05504 0.025 0.792306 +-0.070388 0.032156 0.769815 +-0.063682 -0.006236 0.777538 +-0.000626 0.114562 0.672841 +0.253835 -0.01734 0.119652 +0.223827 -0.021655 0.161215 +0.231867 0.004314 0.117834 +-0.114756 0.089415 -0.973165 +-0.127269 0.107695 -0.974322 +0.096751 0.074035 -0.05055 +0.078066 0.059374 0.009685 +0.000726 0.11622 0.656618 +0.165173 0.032563 -0.050176 +0.000689 -0.080625 0.101576 +-0.005268 -0.079602 0.076344 +0.183917 -0.053008 0.530657 +0.17545 -0.080203 0.523558 +0.141167 -0.093629 0.517571 +-0.051253 0.096483 0.711906 +-0.022715 -0.04717 0.792329 +-0.047913 -0.041404 0.777538 +0.052291 0.096624 0.712047 +-0.026424 -0.020287 -0.061717 +-0.035397 0.011988 -0.055788 +-0.024475 -0.015906 -0.03417 +-0.062564 0.085644 0.653961 +0.006432 -0.067984 0.669362 +-0.091524 -0.120719 -0.548864 +-0.352035 0.051345 -0.126219 +-0.078178 0.050942 0.154885 +-0.07227 0.047244 0.203935 +-0.048281 0.062448 0.195881 +0.155341 0.052205 -0.127805 +0.177243 0.012794 -0.130554 +0.072937 0.07156 -0.124618 +0.118217 0.146923 0.421907 +0.020198 0.076819 0.079276 +0.139978 0.062527 -0.211926 +-0.089177 -0.111241 0.111846 +-0.09518 -0.081905 0.14393 +-0.074495 -0.071296 -0.460179 +-0.042869 0.071572 0.14139 +0.00073 -0.05162 -0.010499 +-0.004172 -0.07642 0.016012 +-0.131034 -0.082145 0.442399 +-0.100523 -0.027685 0.22648 +-0.015731 -0.07176 0.615875 +0.119466 0.075689 -0.126799 +-0.132503 -0.106466 -0.501087 +-0.122962 -0.130064 -0.552684 +-0.074738 0.044114 0.706666 +-0.066077 0.071903 0.732644 +-0.062916 0.068521 0.757684 +-0.048496 0.098136 0.740433 +-0.033166 0.000252 -0.280952 +-0.048184 0.017041 -0.328096 +-0.048173 0.0348 -0.27966 +-0.025707 -0.068824 0.768329 +-0.166577 0.1103 -0.974177 +-0.124674 -0.051241 -0.914051 +-0.112361 -0.082599 -0.886027 +-0.08053 -0.007338 -0.458902 +-0.068099 -0.00941 -0.416012 +-0.214946 0.077303 -0.962319 +-0.205462 0.058324 -0.947736 +0.138907 0.065218 -0.050517 +0.199882 0.012901 0.213538 +0.200105 0.01313 0.252288 +0.055229 0.054786 -0.204024 +-0.024533 0.091239 0.330841 +-0.172277 -0.081982 -0.551109 +-0.167673 -0.057628 -0.50493 +-0.058987 -0.06452 -0.289445 +-0.068156 -0.086411 -0.213966 +0.254357 -0.040025 0.216122 +-0.067674 0.042176 -0.330429 +-0.076952 0.060866 -0.28229 +0.013977 0.113422 0.640177 +-6.9e-05 0.086527 0.3376 +0.019179 0.079744 0.281051 +-0.015722 0.079249 0.274606 +0.115072 -0.070048 -0.42498 +0.077551 -0.065157 -0.362486 +0.033381 0.053515 0.505504 +0.054667 0.033265 0.526511 +0.077572 0.050943 0.507134 +0.086553 0.046191 0.265442 +0.059227 0.073277 0.277063 +0.088572 0.057052 0.304907 +0.057158 -0.046385 -0.353944 +-0.056639 0.061106 0.779722 +-0.043816 0.051377 0.797042 +-0.031526 0.076752 0.792306 +0.171482 -0.044588 -0.21885 +-0.143382 0.00154 -0.554016 +-0.164175 -0.011574 -0.613396 +0.045344 -0.080279 -0.128946 +-0.043257 0.090499 0.76982 +-0.078577 0.001976 0.744204 +-0.07502 -0.017208 0.703979 +-0.068733 -0.033112 0.745947 +-0.009464 0.029546 0.541617 +-0.050437 -0.062276 0.744451 +-0.078293 0.038525 0.740162 +0.024463 0.091212 0.330858 +-0.112377 0.005125 -0.552117 +-0.131688 -0.001292 -0.614531 +-0.183022 -0.043595 -0.957618 +-0.196997 -0.004429 -0.960993 +0.240756 0.018538 0.268716 +0.095305 0.074055 -0.207363 +0.056798 0.020951 0.627455 +0.062692 0.009616 0.652266 +0.049787 -0.001444 0.628412 +0.146665 -0.077493 -0.22013 +-0.037163 -0.033757 -0.284242 +-0.038849 -0.058215 -0.209167 +-0.065469 -0.047111 -0.461306 +0.047178 0.099707 0.356312 +0.308841 0.028975 0.094589 +0.301247 0.005445 0.09387 +0.30888 0.029103 0.019447 +0.017844 0.071064 0.186113 +-0.155734 -0.088774 -0.50245 +-0.152652 -0.115873 -0.552632 +0.011229 0.02562 -0.035193 +-5.3e-05 0.023896 -0.036713 +-0.088766 0.057083 0.305007 +-0.120964 0.004341 -0.951472 +-0.115539 0.089006 -0.947375 +0.292644 0.072606 -0.014141 +-0.123809 0.004777 -0.499938 +0.000277 -0.080877 0.691549 +-0.034692 0.087566 0.596768 +0.103311 0.149984 0.391528 +0.078795 0.134336 0.365284 +0.135882 -0.098348 0.47797 +0.094456 -0.103522 0.46077 +-0.134073 -0.008009 -0.466876 +-0.142742 -0.028419 -0.439643 +-0.106323 -0.066074 -0.366736 +-0.080573 -0.002643 -0.550185 +-0.099477 -0.002733 -0.614967 +-0.329349 0.034688 -0.054928 +-0.342692 0.069348 -0.059398 +-0.347817 0.042787 -0.102173 +0.000763 0.044977 0.590746 +1.7e-05 0.025278 0.592935 +-0.029687 0.031244 0.60365 +-0.053206 0.088406 0.622436 +0.059948 -0.063445 -0.289438 +-0.127417 -0.115851 -0.974611 +-0.121738 -0.118767 -0.954292 +-0.151626 -0.006646 -0.507398 +-0.154614 -0.053562 -0.298047 +-0.141938 -0.093481 0.516984 +-0.165744 -0.028673 -0.506889 +-0.133184 0.029585 -0.373943 +-0.146871 0.043935 0.01869 +-0.161897 0.006001 0.019931 +0.039669 -0.057032 -0.208807 +-0.028046 0.038112 0.808937 +-4.6e-05 -0.020914 -0.03562 +-0.071917 0.145224 0.383713 +-0.078914 0.134376 0.365257 +-0.103429 0.150023 0.391501 +-0.016033 0.0586 0.583186 +-0.150342 -0.019487 -0.472176 +-0.058188 -0.098083 -0.059091 +-0.030917 -0.065889 -0.057781 +-0.0438 -0.102119 -0.030128 +-0.099293 0.128477 0.362907 +-0.092113 0.102894 0.353403 +-0.118447 0.120432 0.364707 +-0.0642 0.142063 0.402239 +0.044609 -0.039585 0.604051 +-0.168519 -0.098024 -0.977937 +-0.179056 -0.08227 -0.963161 +-0.122882 -0.110201 -0.896682 +-0.156663 -0.040336 -0.468156 +0.107748 -0.105176 -0.056081 +0.1285 -0.039664 0.355589 +-0.158194 -0.021338 -0.903386 +-0.164797 0.015297 -0.916402 +-0.176744 -0.016583 -0.931828 +0.033356 0.125884 0.406133 +0.010034 0.099979 0.392826 +0.01712 0.102705 0.44066 +0.024443 -0.015939 -0.034147 +0.009626 -0.053851 -0.021289 +0.042447 0.121595 0.37503 +-0.347781 0.05466 -0.105101 +-0.115513 0.145409 0.39049 +0.111316 -0.089478 0.388386 +-0.128517 -0.00972 0.345704 +0.080848 0.011758 0.692945 +-0.067863 -0.031032 -0.431189 +-0.118337 0.146961 0.421881 +0.045869 -0.069312 0.571091 +-0.126084 0.090514 0.363964 +-0.356352 0.06532 -0.102173 +-0.281744 0.042127 0.010238 +-0.292665 0.041989 -0.023182 +-0.2916 0.071244 -0.013206 +-0.016178 0.060644 0.806268 +-0.34744 0.048022 -0.151125 +-0.114149 0.105438 -0.967742 +-0.162121 -0.095588 0.45288 +-0.114846 -0.090481 -0.461001 +-0.087198 -0.068891 -0.428048 +0.042983 -0.102146 -0.030109 +0.023895 -0.066464 -0.037126 +0.045982 -0.007651 0.589677 +-0.13249 -0.021343 -0.963114 +-0.12112 -0.031941 -0.947976 +-0.123497 -0.052648 -0.969187 +-0.173337 -0.062024 -0.975816 +-0.212735 0.034612 -0.964126 +-0.13793 -0.083142 -0.463138 +-0.032206 0.113799 0.710208 +-0.018056 0.112723 0.690066 +-0.153041 -0.06548 -0.466002 +-0.084204 -0.118301 -0.016723 +-0.107774 -0.105145 -0.056224 +-0.357731 0.118728 -0.141076 +-0.012173 0.100156 0.388899 +-0.042563 0.12163 0.375005 +-0.033484 0.125919 0.406108 +0.00895 0.029531 0.541612 +0.034096 0.103917 0.67485 +0.316842 0.076237 -0.077194 +0.323899 0.046128 -0.095179 +0.025035 -0.017397 -0.204731 +0.022904 -0.002982 -0.119922 +-0.017702 0.108056 0.631897 +-0.090769 0.050144 0.086125 +-0.071768 0.049796 0.05205 +0.058343 0.098285 0.662255 +0.062548 0.085885 0.653903 +0.047412 0.103597 0.643011 +-0.356011 0.072244 -0.110379 +-0.094694 0.016232 0.53498 +0.031138 0.0202 -0.202722 +0.023257 0.108698 0.644556 +0.298563 0.049907 0.106045 +0.309127 0.055705 0.032301 +-0.127245 0.125928 0.44046 +-0.107979 0.133071 0.444265 +0.002793 0.120311 0.643807 +-0.139345 0.01065 -0.91375 +0.191354 -0.010656 0.245307 +0.000195 -0.089014 0.717793 +-0.022347 -0.083906 0.707302 +-0.033776 0.053541 0.505486 +0.0076 0.108722 0.620553 +0.009016 0.106372 0.608066 +0.05338 0.099889 0.672242 +0.247319 0.050332 0.189783 +0.277263 0.037855 0.19703 +0.066826 0.030362 0.643759 +0.068832 0.018953 0.660837 +-0.05508 0.054469 -0.2044 +-0.095081 0.07404 -0.207566 +-0.121146 -0.113881 0.007467 +-0.007983 -0.067736 0.665032 +-0.139878 0.062566 -0.211961 +-0.148273 0.045873 -0.29067 +-0.165897 0.033789 -0.213473 +-0.041772 0.121934 0.43798 +0.109205 -0.048064 0.306801 +-0.094235 0.003354 -0.498127 +0.277358 0.054616 0.14883 +0.247971 0.054507 0.138894 +0.15507 0.003229 -0.352581 +0.143146 0.028923 -0.337551 +0.349178 0.044531 -0.10337 +-0.134886 -0.057869 -0.366286 +0.301726 0.003593 0.142683 +0.299445 0.033456 0.150221 +0.291556 0.007949 0.193786 +-0.36146 0.095938 -0.122006 +-0.292738 0.024324 0.005932 +0.215633 0.016904 0.307366 +0.333478 0.080898 -0.098369 +-0.354136 0.088244 -0.126082 +-0.024054 -0.018703 -0.205089 +-0.030282 0.019018 -0.203081 +-0.023349 -0.003622 -0.121357 +-0.009637 0.122749 0.70621 +0.162028 -0.090011 -0.885108 +0.165776 -0.102924 -0.790685 +0.143895 -0.115061 -0.901856 +0.051724 0.088579 0.622321 +0.061724 0.063033 0.633357 +0.06924 0.049621 0.668534 +0.35772 0.067034 -0.103388 +0.214633 -0.048801 0.252022 +0.135277 -0.031016 -0.794097 +0.151255 -0.043483 -0.866807 +0.130025 -0.042014 -0.864241 +-0.027974 0.006868 -0.029505 +-0.024899 -0.044248 -0.125845 +0.142675 -0.028427 -0.439606 +0.146143 -0.047721 -0.430244 +-0.344331 0.038782 -0.132348 +-0.033038 0.103786 0.67472 +-0.125521 0.007147 -0.445015 +-0.110456 0.017639 -0.450178 +-0.177355 0.012827 -0.130577 +-0.177958 -0.006894 -0.216507 +0.020331 -0.09522 -0.015547 +-0.047532 0.103303 0.641395 +-0.028376 0.100234 0.614667 +-0.134303 -0.065085 -0.42956 +-0.354421 0.12716 -0.151317 +-0.348795 0.121213 -0.142322 +0.092382 0.046406 0.119475 +0.116004 0.034072 0.102858 +0.090667 0.050145 0.086144 +-0.13464 0.11992 0.378378 +-0.145812 0.082925 0.387467 +-0.142197 0.115031 0.400078 +-0.073683 0.142378 0.425002 +-0.11906 0.10213 0.463337 +-0.137325 0.059317 0.473022 +-0.216529 -0.057972 0.41774 +-0.196262 -0.081978 0.459561 +-0.211776 -0.046564 0.468374 +0.179611 -0.005892 0.290372 +0.072121 0.033447 0.670515 +-0.114173 0.06488 -0.287538 +0.086455 -0.101061 0.513489 +-0.148602 0.042639 0.439711 +-0.122607 0.04972 -0.332836 +-0.13875 0.086783 0.448968 +-0.166519 0.014354 -0.292347 +0.089044 -0.065899 0.298075 +0.100498 -0.027722 0.226627 +0.048519 -0.014071 -0.352485 +0.038536 -0.03231 -0.284242 +0.000144 -0.076816 0.142161 +-0.067966 -0.048732 -0.424016 +0.046995 0.088353 0.477529 +0.088208 0.107273 0.467101 +0.061381 0.118752 0.456477 +0.193747 0.011959 0.298081 +0.034539 0.001698 -0.280952 +0.048886 0.018356 -0.328229 +-0.000763 0.058573 0.503145 +-0.064384 0.02103 -0.364348 +-0.079524 0.038923 -0.375843 +-0.088095 0.046844 -0.35346 +0.279857 0.058875 0.019882 +0.256561 0.051897 0.087347 +0.293923 0.07157 0.020631 +-0.025409 0.115069 0.74448 +0.001008 0.120894 0.74601 +0.024428 -0.118227 0.018318 +0.045667 -0.121959 -0.007542 +0.252255 0.033409 0.076695 +-0.158045 -0.017034 0.358421 +-0.15663 -0.037984 0.35548 +-0.148633 0.077503 0.419924 +-0.180872 -0.032671 -0.13361 +-0.17169 -0.094525 0.495191 +-0.196785 -0.069355 0.503 +-0.136701 0.121491 0.422046 +-0.088328 0.107312 0.467073 +-0.356358 0.050663 -0.148806 +-0.129249 -0.096403 -0.134181 +-0.163998 -0.069974 -0.134907 +-0.150151 -0.030244 -0.366624 +0.062655 -0.048958 0.705925 +0.081642 -0.002642 -0.550158 +0.095049 0.003325 -0.498101 +0.113446 0.005123 -0.552089 +-0.17147 -0.044567 -0.218881 +-0.356494 0.095415 -0.106381 +-0.081569 -0.101578 -0.133396 +0.359248 0.088392 -0.100782 +-0.046231 -0.080746 -0.130322 +0.041642 0.121894 0.438006 +0.129402 -0.009686 0.345732 +0.201901 -0.019149 0.207794 +0.209533 0.008252 0.16446 +-0.329022 0.034296 -0.095499 +-0.065874 0.113583 0.355867 +0.075559 -0.055198 0.227902 +0.061849 -0.069654 0.179403 +0.089743 -0.053273 0.172255 +-0.115191 -0.070046 -0.425006 +-0.115434 -0.090095 -0.973743 +0.278928 0.059992 0.097192 +0.113573 -0.062643 -0.871804 +0.101579 -0.052474 -0.79698 +0.124623 0.004748 -0.499911 +0.161939 -0.11913 -0.696457 +0.136289 -0.130094 -0.703771 +0.149847 -0.119757 -0.783024 +0.225358 -0.039917 0.205755 +0.071144 -0.011928 -0.501376 +0.080559 -0.007372 -0.458874 +0.344051 0.071092 -0.060589 +-0.357919 0.086664 -0.099675 +-0.061593 -0.049099 0.705784 +-0.032553 -0.067034 0.676746 +-0.077423 -0.065374 -0.362509 +0.105019 -0.014965 0.166697 +0.212039 -0.041769 0.226301 +0.113461 -0.126095 -0.709147 +0.12885 -0.123528 -0.796839 +-0.047846 -0.015163 -0.352407 +0.119073 0.044154 -0.348785 +0.116024 -0.048486 0.136832 +0.345781 0.117308 -0.104241 +0.351121 0.109018 -0.094476 +0.052426 -0.056817 0.574477 +0.095396 -0.072379 -0.798043 +-0.328432 0.095191 -0.091348 +-0.061499 0.118792 0.456451 +-0.056158 -0.047468 -0.353951 +-0.069627 0.003538 -0.420243 +-0.094278 0.054827 -0.332642 +0.31826 0.03996 -0.016181 +-0.043757 -0.071989 0.710178 +0.105996 0.001263 0.27446 +0.076423 0.042651 0.208272 +-0.116755 -0.036153 -0.785482 +-0.134088 -0.042806 -0.864056 +0.253771 -0.034132 0.168461 +0.155978 -0.039528 -0.782461 +0.169688 -0.057425 -0.788607 +-0.009594 0.046517 -0.022378 +0.152497 -0.006648 -0.507371 +-0.342275 0.113004 -0.121218 +-0.328432 0.10443 -0.05057 +-0.333615 0.114459 -0.087733 +-0.349759 0.107273 -0.09328 +-0.087764 0.135617 0.442196 +0.162192 -0.06653 -0.871875 +0.174311 -0.078585 -0.788353 +-0.089218 0.014086 -0.447721 +0.197414 -0.033855 0.245837 +0.094128 -0.081944 0.143956 +-0.169532 -0.022453 -0.295043 +0.01708 0.100498 0.598854 +0.093526 0.014904 0.191271 +0.282834 -0.017447 0.13002 +0.282789 -0.00101 0.077153 +0.108091 -0.083728 -0.886243 +0.114925 -0.090476 -0.460974 +0.229196 0.03396 0.125804 +-0.011346 0.025647 -0.035213 +0.062826 0.08297 0.678076 +-0.172061 -0.002328 0.335921 +-0.169846 -0.00047 0.366421 +0.09249 -0.103709 -0.710652 +0.112314 -0.116058 -0.788678 +0.281825 -0.02187 0.181952 +0.272129 -0.02018 0.233186 +0.269184 0.012064 0.241483 +0.262166 -0.016175 0.27233 +-0.143576 -0.133746 -0.615782 +-0.160869 -0.119135 -0.696484 +-0.135325 -0.130063 -0.703567 +0.171942 -0.002366 0.335949 +0.190821 0.010089 0.346412 +0.080132 -0.074061 -0.708891 +0.100808 -0.096899 -0.79974 +0.357678 0.048422 -0.139516 +-0.071777 0.023237 0.672706 +0.237792 -0.032614 0.366062 +0.228778 -0.055637 0.359697 +0.2494 -0.035982 0.31464 +0.219264 0.03807 0.176292 +-0.25479 -0.03794 0.26771 +-0.253873 -0.042551 0.241433 +-0.236493 -0.050336 0.260313 +-0.13773 -0.005931 -0.414142 +-0.134495 0.011459 -0.41487 +0.163194 -0.058909 -0.917034 +0.17152 -0.016533 -0.931828 +-0.169841 -0.110147 -0.611986 +-0.178531 -0.092624 -0.689969 +-0.153471 -0.080507 0.409463 +-0.176946 -0.072394 0.374346 +-0.193866 0.011998 0.298053 +-0.17973 -0.005852 0.290346 +-0.200223 0.013169 0.252261 +-0.120376 0.02512 -0.419874 +-0.183095 -0.070451 -0.608863 +-0.182665 -0.061429 -0.687606 +-0.193025 0.006998 0.426495 +-0.146159 -0.047714 -0.430289 +0.050021 0.062413 0.195904 +0.352977 0.0731 -0.151044 +0.138139 0.11513 -0.950869 +0.330384 0.03604 -0.096696 +0.339748 0.038028 -0.111719 +-0.008455 -0.099159 0.047585 +0.134852 -0.023383 -0.900373 +-0.19131 -0.081309 0.407231 +0.33174 0.043482 -0.109801 +0.332326 0.053947 -0.107568 +0.164425 -0.024219 0.330085 +0.157925 -0.017074 0.358447 +0.25467 -0.037979 0.267736 +0.04634 0.016295 0.551532 +0.071798 0.145186 0.383741 +-0.149476 -0.121491 -0.974756 +-0.144727 -0.085602 -0.97833 +-0.112139 -0.135913 -0.616767 +-0.113224 -0.125811 -0.707323 +0.06408 0.142024 0.402264 +0.094829 0.15407 0.402267 +0.235265 -0.053833 0.306932 +0.357062 0.077449 -0.136492 +0.116128 -0.036368 -0.78768 +0.357733 0.052408 -0.149996 +-0.112323 0.042538 -0.375643 +-0.014254 0.018502 0.815081 +-0.026157 -0.082194 0.736953 +0.000167 -0.084481 0.746748 +0.122329 -0.012356 -0.929538 +0.181078 0.111755 -0.961681 +0.348813 0.049767 -0.152316 +-0.098522 0.031918 -0.41636 +-0.097817 0.045961 -0.374305 +-0.17422 -0.079079 -0.788737 +0.169811 -0.096837 -0.930936 +0.066086 0.088146 0.321504 +-0.149399 -0.119766 -0.783049 +-0.186242 -0.062921 0.337147 +-0.16574 -0.102936 -0.790708 +0.000149 0.001448 0.814988 +-0.191474 -0.010617 0.245282 +0.057009 -0.098046 -0.058783 +0.345692 0.040527 -0.133543 +0.261673 0.008906 0.072566 +0.207601 0.034662 -0.964126 +0.069559 -0.002266 0.673485 +0.361242 0.06737 -0.13841 +-0.017244 -0.065856 0.221488 +0.127126 0.125889 0.440487 +-0.218422 0.022526 0.260721 +0.209082 -0.067097 0.348097 +0.176827 -0.072432 0.374373 +0.186123 -0.06296 0.337175 +0.161442 0.11035 -0.974177 +0.137745 0.119463 -0.96321 +0.237267 0.007735 0.314909 +-0.073333 0.00819 0.671301 +0.250165 -0.012465 0.317687 +0.255373 0.003608 0.276391 +0.079771 -0.101541 -0.132485 +0.133483 0.029525 -0.373842 +-0.2092 -0.067057 0.348069 +0.1605 -0.112745 -0.963402 +0.213158 -0.059199 0.297507 +0.191524 -0.05003 0.289964 +0.054443 -0.034838 0.575107 +0.054752 -0.015075 0.563712 +0.119634 -0.051103 -0.913871 +-0.17563 -0.080145 0.523413 +0.146787 -0.021377 0.536589 +0.097999 0.024274 0.525773 +0.094615 0.015938 0.534984 +-0.309169 0.052202 0.035263 +-0.308531 0.029294 0.103042 +-0.298682 0.049946 0.106018 +-0.297572 0.066955 -0.047369 +-0.299564 0.033494 0.150195 +-0.291675 0.007987 0.193759 +-0.277382 0.037895 0.197003 +0.231763 -0.00842 0.365842 +-0.301845 0.003632 0.142656 +-0.277477 0.054656 0.148803 +-0.247437 0.050371 0.189756 +-0.054748 0.033272 0.526506 +0.122437 -0.113899 0.007519 +-0.193282 0.020798 -0.942916 +0.073563 0.142338 0.425029 +0.066203 0.062522 0.091972 +0.000232 0.073015 0.137844 +-0.047791 0.043658 0.604435 +0.212468 0.008589 0.36307 +0.176166 0.106477 -0.95205 +0.178626 -0.029829 0.287186 +-0.079237 0.005393 0.695935 +-0.076123 0.021169 -0.417027 +-0.169275 -0.04711 0.331153 +0.216739 -0.026752 0.424907 +0.000277 -0.068158 0.778329 +0.191192 -0.081347 0.407258 +-0.098086 0.024286 0.525765 +0.350164 0.122984 -0.143516 +0.183996 0.007199 0.388155 +0.212216 -0.004421 0.405766 +-0.184116 0.007238 0.38813 +0.10786 0.133032 0.444293 +-0.315013 0.1033 -0.056114 +0.343636 0.114749 -0.122414 +0.105901 0.15086 0.422464 +0.087645 0.135578 0.442222 +-0.279045 0.060032 0.097165 +0.018609 0.031918 0.534297 +0.109621 0.089465 -0.973165 +0.100191 0.074932 -0.964246 +-0.146831 -0.018057 -0.697226 +0.21641 -0.058012 0.417767 +0.065129 -0.080297 -0.613455 +0.067307 -0.090024 -0.544694 +0.055978 -0.05627 -0.544923 +-0.228896 -0.055598 0.359671 +0.153721 -0.115869 -0.552604 +0.133373 -0.106461 -0.501061 +0.12403 -0.130059 -0.552657 +0.192908 0.007033 0.426625 +0.15375 -0.002896 0.38574 +0.169726 -0.000509 0.366447 +-0.062061 0.009439 0.652188 +-0.056839 0.020863 0.627445 +-0.121258 -0.014342 -0.700516 +-0.214751 -0.048761 0.251994 +-0.212159 -0.041729 0.226274 +-0.197533 -0.033816 0.24581 +0.173921 -0.082221 -0.963161 +0.168202 -0.061975 -0.975816 +0.177887 -0.043544 -0.957618 +-0.095604 -0.024233 -0.703071 +-0.102468 -0.052468 -0.79493 +-0.198954 -0.008168 0.473288 +0.105332 -0.111549 -0.498398 +0.092592 -0.120714 -0.548836 +-0.213277 -0.05916 0.297479 +-0.191642 -0.049991 0.289936 +0.359854 0.065564 -0.149794 +0.14721 0.014138 0.493835 +0.175852 -0.005093 0.511355 +0.181789 -0.036859 -0.610575 +0.170095 -0.017463 -0.554248 +0.17871 -0.046872 -0.552482 +0.079132 -0.094498 -0.497075 +-0.031564 0.064185 0.586557 +-0.068814 -0.002386 0.673389 +0.144645 -0.133741 -0.615754 +-0.169618 -0.057919 -0.788991 +-0.164544 -0.02418 0.330059 +-0.178745 -0.029791 0.287159 +-0.353449 0.100297 -0.144988 +0.170909 -0.110143 -0.611958 +0.173346 -0.081978 -0.551082 +0.144451 0.001541 -0.553988 +0.184164 -0.070446 -0.608835 +0.058245 0.041798 0.617799 +-0.359902 0.069812 -0.126473 +-0.351593 0.071362 -0.14978 +-0.348792 0.075551 -0.122467 +0.052327 0.025048 0.537409 +0.156605 -0.088769 -0.502422 +0.113103 -0.135944 -0.616971 +0.061286 -0.046683 -0.614011 +0.060984 -0.025141 -0.547473 +0.059732 -0.036682 -0.498685 +0.072668 -0.018394 -0.615161 +-0.308402 0.029099 0.014168 +-0.320004 0.067021 -0.007001 +0.06213 -0.067852 -0.496605 +-0.112863 -0.115721 -0.78648 +0.122134 0.107746 -0.974322 +0.084451 -0.117277 -0.615599 +-0.09246 -0.103354 -0.708364 +-0.101836 -0.096328 -0.797447 +0.000687 0.081648 0.798072 +0.015952 0.101963 0.777649 +-0.014695 0.101868 0.777555 +0.017383 0.060739 0.806361 +0.189972 0.015591 -0.975093 +0.191862 -0.004379 -0.960993 +0.000267 -0.024418 0.808928 +-0.184152 -0.052972 0.530994 +0.180228 -0.027037 0.531658 +0.132653 -0.001324 -0.614735 +-0.293724 0.071226 0.020867 +0.164928 0.009787 0.470594 +0.165244 -0.011569 -0.613368 +0.33453 0.113225 -0.100287 +-0.317145 0.038512 -0.015189 +-0.235384 -0.053793 0.306905 +-0.165729 -0.069459 -0.874049 +0.168543 -0.057623 -0.504902 +0.169155 -0.04715 0.33118 +0.000295 -0.048076 0.797157 +0.100339 -0.002801 -0.615402 +-0.24952 -0.035943 0.314613 +-0.344146 0.066069 -0.129231 +-0.353663 0.060882 -0.126628 +0.321049 0.068382 -0.007936 +-0.233519 -0.049564 0.232094 +-0.176089 -0.005058 0.511692 +0.065548 -0.047106 -0.461278 +-0.237912 -0.032574 0.366035 +0.354767 0.102019 -0.146066 +-0.049903 0.064645 0.606883 +0.092275 -0.088632 -0.463041 +-0.215753 0.016943 0.30734 +-0.19094 0.010127 0.346385 +0.347086 0.098279 -0.123603 +-0.250285 -0.012426 0.317661 +-0.23188 -0.008467 0.365694 +0.119313 0.028689 0.512206 +-0.212585 0.008554 0.362939 +-0.212317 -0.0049 0.405009 +0.156511 -0.038022 0.355508 +-0.027271 0.106695 0.664112 +-0.237387 0.007774 0.314882 +0.100543 -0.091974 0.537372 +-0.061758 0.062859 0.633349 +-0.066238 -0.090029 -0.544722 +-0.0584 0.041725 0.617799 +-0.066718 0.02961 0.643738 +-0.081869 -0.044364 -0.703365 +-0.096247 -0.072048 -0.795993 +-0.171555 -0.034578 -0.692527 +0.153797 -0.057313 0.371336 +-0.080103 -0.073706 -0.706604 +-0.216857 -0.026786 0.424776 +-0.14007 -0.023534 -0.900373 +0.125964 0.090476 0.363992 +0.096007 0.076435 0.350796 +0.138416 0.051197 0.39081 +-0.052406 0.099858 0.672279 +-0.058922 0.096015 0.666199 +-0.082735 0.018206 0.705165 +0.088088 -0.111252 0.1119 +0.122322 -0.083917 0.107098 +0.094708 -0.129284 0.049443 +0.111728 -0.112324 0.082435 +0.06644 -0.127597 0.083167 +0.117103 0.023259 0.342647 +0.024221 -0.111692 0.078087 +0.049197 -0.110788 0.111325 +0.020854 -0.089789 0.106589 +-0.039595 0.038126 -0.123114 +0.196143 -0.082017 0.459588 +0.071653 0.049761 0.052075 +0.068917 -0.092263 0.142257 +0.153351 -0.080547 0.40949 +0.162002 -0.095627 0.452908 +0.17152 -0.09458 0.495315 +0.138631 0.086743 0.448996 +-0.262284 -0.016136 0.272303 +0.115985 -0.03189 -0.947976 +-0.240876 0.018577 0.268688 +-0.021682 -0.068079 0.181314 +0.024394 -0.068695 0.1802 +0.103653 -0.086124 -0.928478 +0.118736 -0.11038 -0.897024 +0.023641 -0.047052 0.792444 +0.130924 -0.08206 0.442419 +0.163385 -0.097974 -0.977937 +0.065571 0.074633 0.705359 +0.075525 0.044237 0.706769 +0.155403 0.080724 -0.978019 +-0.200349 -0.031877 0.511405 +-0.180522 -0.027003 0.532176 +0.316374 0.105045 -0.057311 +0.298933 0.0687 -0.048566 +0.101867 0.027765 0.144359 +0.078465 0.050928 0.154908 +-0.255493 0.003647 0.276365 +0.044808 0.051491 0.797208 +0.056032 0.025116 0.792472 +0.057676 0.061247 0.779862 +0.032619 0.076882 0.792434 +0.044295 0.090641 0.76996 +0.153823 -0.023485 0.066935 +0.140312 -0.041279 0.102405 +0.143659 -0.073205 0.071343 +0.044913 -0.130195 0.048916 +0.328643 0.107482 -0.089929 +0.359103 0.120508 -0.142271 +-0.116711 -0.063894 -0.872701 +0.037623 0.015333 0.806564 +0.329793 0.106175 -0.051768 +0.013631 -0.065663 0.215941 +0.018712 0.004167 0.813279 +-0.202019 -0.01911 0.207767 +0.03988 -0.080503 0.145011 +0.11583 0.00439 -0.951472 +0.063954 0.068662 0.757824 +-0.088107 -0.10108 0.514606 +0.304269 0.044884 -0.059759 +0.071426 0.032298 0.769955 +0.33071 0.036433 -0.056124 +-0.059257 0.073308 0.277159 +0.067868 -0.030914 -0.431164 +0.051605 0.094388 0.699984 +0.308124 0.031521 -0.022304 +-0.223946 -0.021616 0.161188 +-0.209652 0.008291 0.164432 +0.11894 0.10209 0.463365 +0.179601 -0.092619 -0.689941 +-0.302907 0.043139 -0.058563 +-0.225476 -0.039879 0.205727 +0.067115 0.072045 0.732783 +0.049534 0.098277 0.740572 +-0.037865 0.106055 0.663632 +0.134121 -0.101168 0.050634 +0.049119 -0.08204 0.316253 +0.075891 -0.017079 0.704095 +0.145693 0.082887 0.387494 +0.148513 0.077463 0.419952 +-0.020141 0.076839 0.079383 +-0.073478 0.071222 -0.125881 +0.18373 -0.061424 -0.687579 +-0.119577 0.075674 -0.127002 +-0.256679 0.051936 0.08732 +-0.252373 0.033449 0.076668 +-0.08359 -0.11721 -0.615163 +-0.155435 0.052239 -0.127829 +0.140036 -0.126156 -0.962029 +0.112933 0.060372 -0.975334 +-0.060424 -0.046616 -0.613575 +-0.071807 -0.018327 -0.614725 +0.126995 -0.004658 0.124934 +0.121818 0.020068 -0.968705 +-0.132384 0.052987 -0.930089 +0.020565 -0.022346 0.80643 +0.172617 -0.034573 -0.6925 +0.075579 -0.022709 -0.438903 +0.067669 -0.009307 -0.416099 +0.14779 -0.018088 -0.697429 +0.127355 -0.021293 -0.963114 +0.081894 -0.04472 -0.705653 +0.095626 -0.024589 -0.705358 +0.121488 -0.014626 -0.70234 +-0.168278 -0.059321 -0.917305 +0.079233 0.03866 0.740287 +-0.261791 0.008945 0.072539 +-0.231986 0.004352 0.117807 +0.21154 -0.046599 0.468037 +0.198718 -0.008203 0.472951 +0.043258 0.071539 0.141414 +-0.127551 -0.012406 -0.929538 +-0.026188 0.058074 0.012186 +0.112256 0.042497 -0.375606 +0.134501 0.011415 -0.414821 +0.026596 -0.068706 0.768445 +0.097698 0.045923 -0.374278 +0.137736 -0.005975 -0.414093 +0.122283 -0.115801 -0.974611 +-0.064269 -0.08023 -0.613019 +0.19953 -0.031896 0.50925 +0.10673 -0.105034 -0.952919 +-0.174945 -0.096887 -0.930936 +-0.166068 -0.090536 -0.885475 +0.137206 0.059278 0.473049 +0.108901 0.069512 0.488713 +0.043453 -0.067073 0.234408 +0.148483 0.0426 0.439737 +-0.121795 0.024085 -0.939902 +0.070013 -0.024905 -0.463256 +0.094521 0.054911 -0.332617 +-0.044988 -0.069707 0.571411 +0.196549 -0.06939 0.502664 +-0.269302 0.012103 0.241456 +0.139593 -0.085551 -0.97833 +-0.1813 0.106426 -0.95205 +0.084087 -0.118334 -0.0167 +0.070692 -0.131623 0.017117 +-0.027246 0.032932 -0.019514 +0.068233 0.043141 -0.330533 +0.169942 -0.022509 -0.294929 +-0.066197 0.062519 0.092052 +-0.246955 0.034265 0.236012 +0.000944 0.079609 0.57354 +0.157193 0.037473 -0.975575 +-0.017007 0.074555 0.577188 +-0.092451 0.046276 0.119469 +-0.101966 0.027771 0.14434 +0.148856 -0.027476 -0.970453 +0.357818 0.09714 -0.107473 +-0.253954 -0.017301 0.119624 +-0.062212 -0.069614 0.179375 +-0.118028 0.064277 0.011804 +-0.165178 0.032598 -0.050201 +-0.078283 0.059413 0.009658 +-0.139111 0.065256 -0.050545 +-0.067102 -0.127608 0.083129 +-0.093764 -0.129297 0.049364 +-0.044257 -0.130188 0.048792 +-0.02388 -0.118223 0.017593 +-0.133511 -0.101327 0.05037 +-0.070802 -0.131593 0.016976 +-0.045983 -0.121947 -0.007695 +-0.12708 -0.004649 0.124927 +-0.122402 -0.083924 0.107013 +0.042901 -0.013411 0.798205 +-0.110434 -0.112461 0.082173 +-0.300537 0.030467 -0.022794 +0.064719 -0.006094 0.777678 +0.06977 -0.03297 0.746087 +0.079517 0.002111 0.744329 +-0.097479 0.07402 -0.050754 +0.04895 -0.041262 0.777678 +0.051476 -0.062134 0.744591 +0.077936 0.061074 -0.282267 +0.049416 0.035999 -0.279656 +0.085 0.027706 -0.418945 +0.093614 0.023107 -0.434949 +0.083504 0.018742 -0.434733 +0.10551 0.024301 -0.434957 +0.099179 0.017905 -0.450637 +0.104225 0.010943 -0.473395 +0.093515 0.007878 -0.466875 +0.085533 -0.001387 -0.479578 +0.086084 0.003223 -0.460441 +0.079785 0.00487 -0.444702 +0.075304 0.009084 -0.435316 +0.069238 0.013881 -0.415505 +0.064922 0.011603 -0.400495 +0.067382 0.022857 -0.391336 +0.071056 0.033647 -0.36988 +0.075886 0.030472 -0.393918 +0.086388 0.036786 -0.396518 +0.087717 0.044492 -0.374683 +0.096522 0.042056 -0.387442 +0.107434 0.038476 -0.397641 +0.11772 0.03446 -0.397719 +0.111318 0.030022 -0.419755 +0.115082 0.022058 -0.434555 +-0.154396 -0.04401 -0.821796 +-0.146033 -0.03405 -0.792788 +-0.14559 -0.038588 -0.836323 +-0.151405 -0.029697 -0.744407 +-0.138817 -0.024076 -0.746508 +-0.133452 -0.013771 -0.699098 +-0.128459 -0.022903 -0.747439 +-0.117281 -0.025556 -0.748756 +-0.126527 -0.031998 -0.793891 +-0.123915 -0.04171 -0.82303 +-0.136441 -0.037708 -0.836495 +-0.144431 -0.036866 -0.866704 +-0.140784 -0.036521 -0.889768 +-0.14861 -0.032492 -0.887839 +-0.148537 -0.019592 -0.89748 +-0.156102 -0.036015 -0.894347 +-0.163448 -0.037764 -0.906158 +-0.161411 -0.050346 -0.895794 +-0.16356 -0.066484 -0.898927 +-0.163188 -0.058661 -0.874199 +-0.167186 -0.064576 -0.838923 +-0.162506 -0.054127 -0.837091 +-0.16378 -0.04718 -0.789485 +-0.17189 -0.048281 -0.739308 +-0.162645 -0.037607 -0.742288 +-0.161111 -0.024278 -0.695175 +0.034314 0.049359 0.593313 +0.031715 0.064242 0.586614 +0.039882 0.05637 0.594047 +0.018033 0.074655 0.577287 +0.027023 0.077375 0.582822 +0.035109 0.075342 0.589601 +0.043328 0.076991 0.600573 +0.042798 0.064854 0.596433 +0.049759 0.052827 0.603861 +0.055703 0.052902 0.614078 +0.06172 0.031236 0.625212 +0.054571 0.031488 0.61617 +0.04518 0.02485 0.613831 +0.042287 0.036067 0.605219 +0.025777 0.044468 0.593641 +0.014607 0.039923 0.594763 +0.008979 0.05128 0.586359 +-0.007541 0.051232 0.586311 +0.00073 0.061307 0.579418 +-0.004866 0.069214 0.575659 +0.006351 0.069261 0.575706 +0.009533 0.077746 0.574115 +0.137823 -0.072293 -0.448672 +0.14702 -0.076721 -0.464719 +0.144444 -0.066039 -0.45078 +0.147212 -0.087648 -0.480882 +0.156045 -0.07636 -0.482139 +0.164822 -0.073968 -0.503427 +0.161374 -0.062994 -0.483552 +0.163648 -0.047787 -0.484884 +0.157219 -0.052742 -0.467207 +0.150737 -0.045026 -0.452725 +0.149314 -0.055426 -0.451855 +0.142063 -0.058432 -0.430772 +0.141529 -0.051298 -0.399741 +0.13082 -0.059998 -0.402149 +0.121509 -0.065181 -0.367875 +0.121133 -0.064061 -0.400636 +0.10807 -0.065477 -0.398856 +0.125755 -0.068748 -0.427151 +0.129082 -0.076721 -0.446488 +0.117464 -0.078584 -0.445528 +0.127782 -0.087858 -0.46154 +0.123989 -0.099707 -0.478668 +0.136715 -0.094357 -0.479887 +0.146434 -0.100096 -0.501818 +0.0751 -0.005894 -0.439339 +0.068813 0.003522 -0.420396 +0.060073 0.00326 -0.38662 +0.058085 -0.009248 -0.386583 +0.052855 0.004846 -0.358571 +0.045201 0.005832 -0.332608 +0.053793 0.018972 -0.343158 +0.056407 0.030966 -0.330399 +0.064471 0.033784 -0.344822 +0.07639 0.0447 -0.34469 +0.072056 0.038791 -0.352654 +0.079377 0.042965 -0.360043 +-0.322854 0.106248 -0.077181 +-0.313158 0.092123 -0.066962 +-0.320502 0.095611 -0.078823 +-0.333369 0.102499 -0.098747 +-0.346526 0.104262 -0.113642 +-0.337765 0.107296 -0.110064 +-0.338259 0.116265 -0.110842 +0.017746 0.040951 0.812066 +0.009216 0.030475 0.814915 +0.020663 0.028746 0.813476 +-0.00773 0.030427 0.814869 +0.000519 0.020385 0.816414 +-0.005597 0.011172 0.816161 +0.006185 0.011219 0.816208 +0.009217 -0.000749 0.81448 +0.015179 0.018604 0.815181 +0.027084 0.022222 0.812192 +0.028174 0.010026 0.810997 +0.035581 0.02961 0.807508 +0.044336 0.035043 0.802024 +0.037243 0.045818 0.804088 +0.03363 0.059166 0.801916 +0.025828 0.050636 0.807364 +0.009273 0.050657 0.810793 +0.000687 0.061143 0.808539 +-0.007787 0.05061 0.810747 +-0.024567 0.050517 0.807247 +-0.016261 0.040856 0.811973 +-0.019401 0.028627 0.813359 +0.18904 0.05325 -0.939661 +0.182748 0.035737 -0.933876 +0.195966 0.039643 -0.94641 +0.164345 0.034493 -0.924958 +0.174357 0.016942 -0.928091 +0.167372 -0.001747 -0.921102 +0.179159 0.00241 -0.937492 +0.183653 -0.011515 -0.948821 +0.192301 0.007064 -0.952195 +0.200541 0.015069 -0.963765 +0.200717 0.0255 -0.955328 +0.206609 0.045938 -0.955087 +0.212793 0.056229 -0.96256 +0.209337 0.06592 -0.955087 +0.201419 0.088055 -0.954943 +0.201847 0.076324 -0.949784 +0.193309 0.085722 -0.948339 +0.192192 0.06995 -0.943036 +0.174661 0.068452 -0.937492 +0.162798 0.083759 -0.940143 +0.150956 0.069695 -0.933635 +0.12904 0.072825 -0.934358 +0.146932 0.05325 -0.926886 +0.14579 0.034745 -0.918932 +-0.022539 -0.100064 0.091452 +-0.034476 -0.101114 0.108583 +-0.037401 -0.114645 0.094311 +-0.030398 -0.087436 0.126216 +-0.046951 -0.094812 0.127269 +-0.056899 -0.086745 0.145514 +-0.0631 -0.10197 0.128565 +-0.079022 -0.103607 0.1285 +-0.070143 -0.11564 0.112648 +-0.079179 -0.122321 0.098194 +-0.056918 -0.122012 0.097079 +-0.043539 -0.124662 0.081103 +-0.055252 -0.131201 0.065892 +-0.03184 -0.122629 0.064184 +-0.021241 -0.119156 0.047819 +-0.014697 -0.106949 0.062432 +-0.005273 -0.087201 0.062205 +-0.011532 -0.094401 0.076493 +-0.011787 -0.086838 0.089827 +-0.004127 -0.078303 0.089556 +-0.007918 -0.081657 0.10567 +0.000538 -0.082118 0.117488 +-0.014592 -0.081206 0.124929 +-0.022503 -0.076678 0.144759 +0.131016 0.030007 -0.921102 +0.143786 0.015279 -0.910738 +0.14456 -0.003872 -0.904471 +0.155358 -0.003662 -0.909773 +0.1614 -0.021081 -0.914594 +0.119759 0.090982 -0.939902 +0.114652 0.074994 -0.938938 +0.10756 0.073056 -0.946893 +0.113518 0.057939 -0.939902 +0.111983 0.043341 -0.944241 +0.119362 0.03847 -0.932912 +0.123592 0.019284 -0.927128 +0.122701 0.016798 -0.434688 +0.119816 0.015003 -0.450125 +0.123458 0.007119 -0.467249 +0.115291 0.010284 -0.473285 +0.110572 0.006901 -0.501334 +0.123614 0.037812 -0.375614 +0.126764 0.02868 -0.397726 +0.136835 0.018556 -0.394055 +0.127983 0.018983 -0.419896 +0.128589 0.010047 -0.432236 +0.13734 0.003116 -0.416256 +0.130069 -6.1e-05 -0.434212 +0.133967 -0.011808 -0.420916 +0.128438 -0.005142 -0.442843 +0.130463 -0.011981 -0.449293 +0.127148 -0.001314 -0.460421 +0.131875 0.000305 -0.482078 +0.07376 -0.060361 -0.446447 +0.081422 -0.070317 -0.446155 +0.074366 -0.059211 -0.42425 +0.083033 -0.080999 -0.460094 +0.092136 -0.079874 -0.450203 +0.101872 -0.09105 -0.460544 +0.104184 -0.079577 -0.445754 +0.101264 -0.071654 -0.425042 +0.095197 -0.066519 -0.397707 +0.081907 -0.063519 -0.3956 +0.065807 -0.059207 -0.358372 +0.070643 -0.057006 -0.394146 +0.063182 -0.044838 -0.390651 +0.049602 -0.032254 -0.350668 +0.058213 -0.033789 -0.389715 +0.060478 -0.025451 -0.402212 +0.064594 -0.038002 -0.423824 +0.068956 -0.050201 -0.446956 +0.067741 -0.059835 -0.460462 +0.063104 -0.056832 -0.477018 +0.067905 -0.071221 -0.476469 +0.068524 -0.082087 -0.496373 +0.075535 -0.082429 -0.476394 +0.086245 -0.092693 -0.476471 +-0.13985 -0.032976 0.544864 +-0.121856 -0.015987 0.544631 +-0.157599 -0.047413 0.542014 +-0.138462 -0.044574 0.546762 +-0.118631 -0.049816 0.550605 +-0.118026 -0.037195 0.550138 +-0.093064 -0.030237 0.555935 +-0.109946 -0.022646 0.549189 +-0.095244 -0.014451 0.549912 +-0.07089 -0.019631 0.558345 +-0.070176 -0.006256 0.552081 +-0.052827 -0.000471 0.559068 +-0.061982 0.00917 0.546538 +-0.052401 0.02565 0.53756 +-0.071502 0.020137 0.538825 +-0.097721 0.006941 0.540286 +-0.115382 0.000856 0.538372 +-0.119433 0.007059 0.530845 +-0.111344 0.022729 0.520377 +-0.132721 0.00573 0.523074 +-0.1349 0.020916 0.511005 +-0.151237 -0.002315 0.520796 +-0.162466 0.000964 0.509945 +-0.165064 -0.01331 0.526696 +-0.178461 -0.01867 0.527382 +-0.171972 -0.028404 0.534845 +-0.183187 -0.037055 0.533602 +-0.17008 -0.044257 0.538687 +-0.169844 -0.059164 0.536704 +-0.011435 -0.071785 0.162323 +-0.033151 -0.071885 0.162021 +-0.043355 -0.069196 0.184369 +-0.050995 -0.074575 0.161725 +-0.067752 -0.080186 0.156535 +-0.079645 -0.075422 0.161214 +-0.081894 -0.09013 0.145433 +-0.096184 -0.097408 0.127305 +0.155193 -0.042267 -0.328511 +0.144044 -0.056138 -0.331802 +0.146565 -0.046701 -0.362179 +0.14187 -0.067816 -0.300288 +0.129697 -0.065918 -0.33509 +0.112584 -0.069761 -0.334921 +0.145724 -0.04062 -0.397722 +0.14768 -0.037354 -0.429624 +0.14385 -0.031198 -0.410293 +0.13465 -0.019295 -0.431087 +0.139795 -0.022598 -0.40794 +0.144662 -0.015466 -0.390082 +0.147078 -0.005504 -0.387421 +0.156843 -0.015484 -0.353528 +0.16598 -0.010938 -0.323817 +0.162478 -0.027604 -0.325149 +0.165205 -0.038736 -0.295989 +0.17237 -0.033081 -0.260025 +0.16446 -0.050074 -0.260846 +0.161644 -0.06222 -0.2196 +0.152275 -0.065284 -0.261854 +0.137313 -0.078934 -0.26276 +-0.035914 0.029887 0.535065 +-0.035627 0.023916 0.550875 +-0.045107 -0.011677 0.572079 +-0.044876 0.004108 0.5716 +-0.044648 0.008032 0.590438 +-0.040072 0.016353 0.569512 +-0.037008 0.012413 0.596277 +-0.030016 0.020549 0.569024 +-0.022786 0.024164 0.555526 +-0.027801 0.026911 0.545729 +-0.027459 0.037248 0.523334 +-0.15181 -0.045272 0.070061 +-0.143513 -0.057344 0.089619 +-0.147568 -0.033066 0.086242 +-0.133135 -0.081212 0.091882 +-0.134142 -0.064362 0.105115 +-0.121507 -0.066958 0.122248 +-0.130141 -0.046087 0.118848 +-0.125061 -0.026671 0.131923 +-0.135215 -0.024117 0.114596 +-0.135435 -0.002784 0.106996 +-0.143273 -0.018113 0.097486 +-0.150184 -0.01141 0.074829 +-0.152458 0.008475 0.053013 +-0.157561 -0.009098 0.049444 +-0.166125 -0.015147 0.017548 +-0.15867 -0.030222 0.04676 +-0.154641 -0.056404 0.046788 +-0.159489 -0.064526 0.015852 +-0.149614 -0.077279 0.04327 +-0.14235 -0.093183 0.038038 +-0.138852 -0.091657 0.060299 +-0.123816 -0.106646 0.066798 +-0.129778 -0.095184 0.080177 +-0.118404 -0.100227 0.094179 +-0.170441 -0.04876 -0.016736 +-0.162724 -0.069586 -0.017523 +-0.163057 -0.075771 -0.054285 +-0.151345 -0.088426 -0.018273 +-0.136028 -0.10162 -0.018561 +-0.140846 -0.09927 0.00987 +-0.129984 -0.108136 0.032549 +-0.170585 -0.002107 -0.013763 +-0.173047 -0.025564 -0.015013 +-0.178563 -0.035749 -0.052958 +-0.181919 -0.023271 -0.091209 +-0.179103 -0.044763 -0.092663 +-0.17597 -0.052477 -0.134767 +-0.171315 -0.06396 -0.093206 +-0.1577 -0.080727 -0.092798 +0.131039 0.13736 0.411877 +0.140198 0.119042 0.411468 +0.135377 0.131816 0.400307 +0.144295 0.099758 0.424146 +0.146946 0.09759 0.408961 +0.148633 0.077584 0.402356 +0.146465 0.09759 0.394257 +0.141644 0.100482 0.37859 +0.139715 0.118559 0.38881 +0.130556 0.136395 0.392112 +0.125229 0.136629 0.384559 +0.123446 0.146097 0.401633 +0.113217 0.154187 0.409708 +0.122121 0.147965 0.414047 +0.127664 0.139047 0.421037 +0.123473 0.137444 0.430974 +0.132243 0.124585 0.432438 +0.132484 0.110605 0.445597 +0.139715 0.103616 0.435956 +0.1455 0.079994 0.435619 +0.105464 -0.079538 -0.965452 +0.108877 -0.059028 -0.964005 +0.113441 -0.073235 -0.973405 +0.112164 -0.048839 -0.948579 +0.114524 -0.041362 -0.959667 +0.118654 -0.025019 -0.956534 +0.124001 -0.034779 -0.963282 +0.13177 -0.046317 -0.96979 +0.14364 -0.059065 -0.97437 +0.126855 -0.067107 -0.975575 +0.120626 -0.088659 -0.978708 +0.12822 -0.105502 -0.979672 +0.115311 -0.104016 -0.975093 +0.110603 -0.112693 -0.967861 +0.106386 -0.09863 -0.966175 +0.102106 -0.0885 -0.953641 +0.104124 -0.095288 -0.938938 +0.103842 -0.077671 -0.938697 +0.107859 -0.071404 -0.923031 +0.110488 -0.058521 -0.935082 +0.11693 -0.040685 -0.934599 +-0.074463 -0.077453 0.307102 +-0.078401 -0.059789 0.264934 +-0.060105 -0.068898 0.271716 +-0.094806 -0.048793 0.261802 +-0.092646 -0.040849 0.223745 +-0.098855 -0.019207 0.193097 +-0.093596 -0.035382 0.193387 +-0.097932 -0.038457 0.172604 +-0.082951 -0.049343 0.190749 +-0.078645 -0.064003 0.179784 +-0.067216 -0.061508 0.206435 +-0.051503 -0.065799 0.210524 +-0.056095 -0.066328 0.240663 +-0.042784 -0.069414 0.257042 +-0.030736 -0.06768 0.230518 +-0.025676 -0.071542 0.257965 +-0.009122 -0.069386 0.246128 +-0.02034 -0.08079 0.311141 +-0.015722 -0.091002 0.38749 +-0.038388 -0.094059 0.388783 +-0.063779 -0.102674 0.454682 +-0.069124 -0.099458 0.402922 +-0.099713 -0.101902 0.417736 +-0.082542 -0.092616 0.359743 +-0.092785 -0.07983 0.333374 +-0.110242 -0.06956 0.339931 +-0.102354 -0.060438 0.304457 +-0.103764 -0.036518 0.268755 +-0.167933 0.08371 -0.940143 +-0.179796 0.068401 -0.937492 +-0.156091 0.069644 -0.933635 +-0.197328 0.069899 -0.943036 +-0.194175 0.053199 -0.939661 +-0.201101 0.039592 -0.94641 +-0.188074 0.035687 -0.933876 +-0.179874 0.016891 -0.928091 +-0.169862 0.034443 -0.924958 +-0.151499 0.034695 -0.918932 +-0.152259 0.053199 -0.926886 +-0.134175 0.072774 -0.934358 +-0.119787 0.074944 -0.938938 +-0.124894 0.090931 -0.939902 +-0.123458 0.104295 -0.947616 +-0.140525 0.105246 -0.944 +-0.164355 0.112976 -0.95099 +-0.161647 0.101154 -0.944964 +-0.1825 0.094357 -0.947134 +-0.198444 0.085672 -0.948339 +-0.206982 0.076274 -0.949784 +-0.26955 -0.027566 0.148414 +-0.269784 -0.031868 0.174756 +-0.283577 -0.021435 0.15516 +-0.254123 -0.038395 0.194776 +-0.268151 -0.032264 0.201521 +-0.266811 -0.032233 0.222687 +-0.27862 -0.020605 0.208538 +-0.283485 -0.005696 0.214455 +-0.290721 -0.008551 0.188788 +-0.298912 0.005133 0.168092 +-0.294046 -0.009776 0.162176 +-0.295288 -0.009656 0.136587 +-0.302695 0.003478 0.118243 +-0.294271 -0.005903 0.112597 +-0.294476 0.000933 0.081539 +-0.281762 -0.011371 0.106581 +-0.268831 -0.011324 0.101957 +-0.268751 -0.021254 0.123824 +-0.253482 -0.027507 0.142668 +-0.238511 -0.021268 0.139045 +-0.237648 -0.031749 0.163265 +-0.223083 -0.032097 0.185407 +-0.238055 -0.038335 0.18903 +-0.240273 -0.043833 0.209926 +-0.092235 -0.053103 0.557851 +-0.065838 -0.057596 0.565579 +-0.068238 -0.048313 0.5663 +-0.06415 -0.06796 0.563892 +-0.052196 -0.056957 0.57459 +-0.039091 -0.057596 0.588712 +-0.044625 -0.051689 0.58389 +-0.046306 -0.037344 0.587499 +-0.054252 -0.04554 0.57714 +-0.069674 -0.038068 0.56485 +-0.070156 -0.028909 0.562199 +-0.110436 -0.062109 0.551569 +-0.113088 -0.075607 0.547471 +-0.091271 -0.07118 0.55544 +-0.061976 -0.078684 0.556998 +-0.307034 0.016774 0.123519 +-0.308559 0.021113 0.09218 +-0.309478 0.03837 0.071997 +-0.310004 0.030432 0.052442 +-0.31328 0.040791 0.018598 +-0.304347 0.019634 0.047448 +-0.29708 0.01208 0.043322 +-0.301366 0.02206 0.010747 +-0.286625 0.011456 0.039936 +-0.278617 0.015978 0.038748 +-0.271092 0.000358 0.073531 +-0.255479 -0.00576 0.098726 +-0.10819 0.051484 0.346852 +-0.091984 0.065889 0.328944 +-0.101225 0.039468 0.317848 +-0.099468 0.030274 0.292639 +-0.11085 0.013589 0.310555 +-0.119912 -0.008526 0.319179 +-0.125871 0.000417 0.343807 +-0.129094 -0.019956 0.347048 +-0.14803 0.06051 0.427228 +-0.148512 0.060269 0.40915 +-0.148753 0.077624 0.402329 +-0.144415 0.068705 0.390109 +-0.139835 0.083407 0.375333 +-0.131157 0.07232 0.373959 +-0.113804 0.08148 0.361907 +-0.165551 0.020785 -0.013373 +-0.173778 0.01109 -0.05048 +-0.172771 0.023123 -0.088693 +-0.179595 0.000111 -0.089983 +-0.182354 -0.010619 -0.132109 +-0.255119 -0.04115 0.231815 +-0.262239 -0.034719 0.239786 +-0.256245 -0.039171 0.250488 +-0.263742 -0.029584 0.251808 +-0.261051 -0.028069 0.269964 +-0.267756 -0.016443 0.253074 +-0.267568 -0.003742 0.255398 +-0.275969 -0.003485 0.237115 +-0.282221 0.010851 0.218632 +-0.229679 -0.046571 0.221176 +-0.24261 -0.046618 0.225799 +-0.245496 -0.048147 0.236884 +-0.347738 0.040042 -0.142918 +-0.353667 0.042125 -0.127058 +-0.356317 0.046678 -0.138321 +-0.35461 0.040786 -0.15294 +0.005749 0.069597 0.154621 +-0.016153 0.071343 0.154745 +-0.022133 0.07466 0.135914 +-0.030434 0.070256 0.160134 +-0.048125 0.065048 0.164629 +-0.033519 0.068932 0.193502 +-0.035154 0.071296 0.236562 +-0.016637 0.072116 0.217562 +0.001976 0.076208 0.235974 +-0.001783 0.071803 0.195631 +0.007337 0.068129 0.165708 +0.029494 0.021295 0.609461 +0.015004 0.021967 0.592485 +0.015432 0.027967 0.60197 +0.000534 0.033823 0.598879 +0.022632 0.119945 0.709601 +0.01864 0.122279 0.72591 +0.030456 0.116728 0.725309 +0.007049 0.124805 0.725863 +0.014014 0.119953 0.745819 +0.007809 0.113608 0.764353 +0.022299 0.110654 0.76278 +0.030941 0.098067 0.775004 +0.035492 0.104674 0.76056 +0.047384 0.096111 0.756079 +0.039022 0.10842 0.743 +0.041374 0.10915 0.725558 +0.051577 0.098428 0.724437 +0.043447 0.106507 0.711471 +0.043852 0.103614 0.701769 +0.035145 0.109797 0.700616 +0.026544 0.112935 0.696855 +0.016846 0.119048 0.696408 +0.003831 0.121368 0.693726 +0.001843 0.12455 0.707691 +-0.005359 0.124757 0.725816 +-0.047895 -0.033112 0.632439 +-0.039997 -0.050565 0.611532 +-0.039458 -0.048356 0.630012 +-0.033538 -0.067356 0.584232 +-0.041964 -0.080732 0.563843 +-0.027989 -0.076875 0.581195 +-0.014495 -0.083384 0.576854 +-0.031229 -0.063274 0.607769 +-0.035367 -0.055128 0.641799 +-0.027961 -0.062423 0.652942 +-0.044647 -0.052561 0.666889 +-0.050951 -0.056102 0.687794 +-0.06074 -0.040712 0.687247 +-0.070191 -0.034184 0.705635 +-0.068844 -0.025178 0.6872 +-0.071198 -0.011523 0.684393 +-0.066181 -0.011895 0.672771 +-0.064748 0.000376 0.663475 +-0.05987 -0.005735 0.657821 +-0.057325 0.002532 0.645455 +-0.054149 -0.014155 0.644219 +-0.049165 -0.019113 0.623133 +-0.047295 -0.006621 0.608268 +-0.046321 -0.021741 0.599809 +-0.045823 -0.025293 0.583643 +0.034705 -0.078761 0.707976 +0.038589 -0.07787 0.725791 +0.025665 -0.084619 0.720957 +0.049905 -0.068353 0.725275 +0.040027 -0.073785 0.742972 +0.04014 -0.066351 0.760543 +0.027533 -0.077494 0.753043 +0.013803 -0.079631 0.757756 +0.013991 -0.085154 0.741814 +0.000149 -0.088699 0.731704 +0.013562 -0.088193 0.726908 +0.012945 -0.087998 0.713803 +0.000278 -0.088283 0.705279 +0.014016 -0.084427 0.698771 +0.017632 -0.076473 0.686237 +0.02777 -0.078283 0.694074 +0.04279 -0.069767 0.694988 +0.052085 -0.05596 0.687934 +0.053071 -0.063014 0.706322 +0.060455 -0.056201 0.725898 +-0.124358 0.015972 0.116484 +-0.130583 0.019448 0.097922 +-0.122774 0.038074 0.084373 +-0.134711 0.030922 0.074441 +-0.136144 0.044633 0.047276 +-0.145615 0.026361 0.05254 +-0.155987 0.025318 0.021507 +-0.344947 0.098323 -0.101601 +-0.354748 0.089292 -0.091377 +-0.351936 0.084312 -0.081462 +-0.345374 0.094633 -0.072209 +-0.352176 0.096955 -0.090405 +-0.353859 0.107433 -0.10818 +-0.330378 0.041738 -0.108605 +-0.330965 0.052203 -0.106372 +-0.321655 0.064517 -0.089461 +-0.31604 0.055047 -0.082705 +-0.306991 0.054927 -0.068061 +-0.311581 0.043207 -0.073344 +-0.309078 0.0326 -0.057447 +-0.320561 0.033007 -0.078988 +-0.339723 0.035357 -0.097872 +-0.338386 0.036283 -0.110522 +-0.343031 0.037898 -0.120054 +-0.340279 0.050512 -0.128731 +-0.335034 0.046233 -0.119727 +0.177365 0.094407 -0.947134 +0.156512 0.101205 -0.944964 +0.159221 0.113026 -0.95099 +0.13539 0.105296 -0.944 +0.118323 0.104346 -0.947616 +0.119172 0.111799 -0.956052 +0.107758 0.098325 -0.956293 +0.100776 0.089941 -0.964247 +0.102101 0.083022 -0.956534 +0.102727 0.067415 -0.955328 +0.204447 0.088714 -0.970754 +0.211592 0.068093 -0.96979 +0.199394 0.079435 -0.975334 +0.210757 0.047042 -0.970995 +0.204398 0.026319 -0.97196 +0.198123 0.038895 -0.975816 +0.178143 0.02451 -0.975575 +0.183176 0.049987 -0.977263 +0.158815 0.059781 -0.977745 +0.183612 0.069476 -0.977986 +0.181941 0.088311 -0.978226 +0.159791 0.099559 -0.977263 +0.178263 0.103536 -0.976057 +0.172326 0.114202 -0.9722 +0.19131 0.103879 -0.971959 +0.20086 0.096689 -0.964969 +0.005181 -0.087214 0.062214 +0.000179 -0.071911 0.07624 +-5.2e-05 -0.073248 0.062226 +0.000429 -0.076374 0.04766 +-0.004377 -0.088818 0.033195 +-4.6e-05 -0.066889 0.02081 +-4e-05 -0.073279 0.033488 +0.004541 -0.088818 0.033436 +0.010327 -0.099053 0.017474 +0.014908 -0.11014 0.033623 +0.033467 -0.126531 0.033382 +0.021891 -0.119156 0.04806 +0.032008 -0.122629 0.064184 +0.014856 -0.106955 0.062437 +0.011436 -0.094421 0.076506 +0.021981 -0.100084 0.091465 +0.012149 -0.086864 0.089845 +0.00887 -0.081677 0.105684 +0.004955 -0.078317 0.089565 +0.105559 0.049912 -0.953401 +0.110176 0.031911 -0.950026 +0.101499 0.059253 -0.963042 +0.100973 0.06804 -0.970754 +0.104367 0.051356 -0.969549 +0.117059 0.042578 -0.973164 +0.110071 0.033648 -0.965693 +0.116072 0.013433 -0.960631 +0.111821 0.023545 -0.95581 +0.114597 0.013327 -0.946169 +0.117434 -0.004796 -0.942795 +0.120635 0.005571 -0.934599 +0.126374 0.000526 -0.92062 +0.115697 0.052927 0.064604 +0.12412 0.057123 0.041569 +0.107076 0.06003 0.039268 +0.135992 0.0446 0.047299 +0.135003 0.05898 0.0145 +0.145382 0.056535 -0.015276 +0.128658 0.066975 -0.016655 +0.119967 0.074178 -0.051056 +0.108637 0.070634 -0.018096 +0.085949 0.067766 -0.01774 +0.097938 0.065546 0.010758 +0.089167 0.05798 0.035697 +0.071956 0.050993 0.033388 +0.082586 0.052298 0.053811 +0.080983 0.052559 0.072149 +0.094446 0.048808 0.075378 +0.10232 0.045278 0.09406 +0.109649 0.047386 0.082601 +0.122678 0.038054 0.084387 +0.130502 0.019442 0.097927 +0.134594 0.030903 0.074455 +0.14572 0.026341 0.052554 +0.156332 0.025299 0.02152 +0.152839 0.008468 0.053018 +0.124276 0.015966 0.116488 +0.135361 -0.002784 0.106996 +0.135135 -0.024123 0.114601 +0.143199 -0.018113 0.097486 +0.147489 -0.03303 0.086306 +0.150351 -0.01141 0.074829 +0.158212 -0.009098 0.049444 +0.159315 -0.030186 0.046824 +0.167008 -0.015154 0.017552 +0.173674 -0.025584 -0.015001 +0.171212 -0.002127 -0.013749 +0.173907 0.011057 -0.050457 +0.165909 0.020759 -0.013355 +0.156984 0.041113 -0.013512 +0.154395 0.051967 -0.049846 +-0.124767 -0.083039 0.539078 +-0.094399 -0.086967 0.543967 +-0.074263 -0.095434 0.538294 +-0.063175 -0.088685 0.5489 +-0.181475 -0.068678 0.527955 +-0.167436 -0.07399 0.532045 +-0.164075 -0.086491 0.522695 +-0.147763 -0.082629 0.53208 +-0.124752 -0.090866 0.530217 +-0.114596 -0.099145 0.514863 +-0.094865 -0.097686 0.529563 +-0.062976 -0.100331 0.523284 +-0.148871 -0.086093 -0.13452 +-0.140218 -0.093475 -0.092171 +-0.118388 -0.101384 -0.091998 +-0.130259 -0.101712 -0.053949 +-0.119931 -0.109546 -0.023769 +0.08764 -0.093707 -0.216992 +0.081115 -0.082263 -0.256671 +0.100588 -0.085991 -0.260314 +0.063332 -0.074299 -0.253294 +0.072724 -0.072541 -0.293332 +0.067366 -0.063606 -0.326429 +0.079865 -0.068112 -0.330731 +0.091596 -0.066713 -0.364781 +0.095734 -0.069876 -0.333607 +0.108066 -0.078032 -0.299852 +0.120622 -0.085307 -0.262494 +0.130847 -0.089998 -0.220525 +0.139655 -0.088782 -0.177295 +0.118948 -0.097977 -0.176964 +0.105677 -0.103228 -0.133752 +0.095775 -0.101002 -0.17561 +0.073108 -0.096493 -0.173495 +0.058404 -0.094484 -0.130672 +0.055271 -0.085402 -0.17082 +0.04176 -0.069767 -0.168502 +0.052019 -0.074652 -0.210941 +0.049896 -0.062168 -0.250626 +0.000105 -0.093426 0.416664 +0.016088 -0.091022 0.387503 +0.014568 -0.097048 0.458884 +0.020875 -0.080817 0.311158 +0.038653 -0.094125 0.389 +0.06913 -0.099564 0.403338 +0.063146 -0.102773 0.454611 +0.092277 -0.103655 0.496372 +0.061235 -0.104008 0.501116 +0.062183 -0.100099 0.522373 +0.035222 -0.101386 0.506885 +0.010127 -0.099013 0.5109 +-0.001957 -0.093507 0.542871 +-0.009467 -0.099126 0.510991 +-0.035298 -0.101612 0.507551 +-0.01426 -0.097154 0.458971 +-0.004296 -0.064566 0.210009 +0.009517 -0.069399 0.246137 +0.026069 -0.071576 0.257988 +0.174655 0.01451 -0.214808 +0.173821 0.004595 -0.257441 +0.168303 0.024016 -0.256211 +0.175462 -0.015423 -0.258951 +0.1718 -0.00441 -0.293582 +0.163634 0.005286 -0.322165 +0.152804 0.016459 -0.33883 +0.157001 0.021904 -0.321052 +0.147233 0.035817 -0.319925 +0.159648 0.031627 -0.291239 +0.159558 0.041268 -0.255456 +0.146861 0.054797 -0.254858 +0.155792 0.05037 -0.212704 +0.148643 0.058914 -0.169526 +0.162318 0.04321 -0.170186 +0.16849 0.034677 -0.128877 +0.172439 0.024603 -0.171537 +0.179366 0.002286 -0.173456 +0.182242 -0.010652 -0.132087 +0.181041 -0.019273 -0.175061 +0.177474 -0.03964 -0.176264 +0.177971 -0.027068 -0.217924 +0.134343 0.058148 -0.289554 +0.134626 0.04881 -0.318837 +0.119812 0.057273 -0.317494 +0.133323 0.040997 -0.33629 +0.109918 0.05439 -0.334077 +0.10214 0.060962 -0.315465 +0.086957 0.059268 -0.313516 +0.095394 0.067287 -0.28474 +0.085052 0.069002 -0.247709 +0.107071 0.071003 -0.250465 +0.119164 0.072706 -0.21002 +0.128348 0.065555 -0.253251 +0.028509 -0.062365 0.653021 +0.036108 -0.055071 0.641878 +0.04558 -0.05245 0.66701 +0.032152 -0.063137 0.60769 +0.040205 -0.048321 0.630067 +0.0408 -0.050443 0.611472 +0.048401 -0.033136 0.632478 +0.049234 -0.019221 0.623613 +0.054612 -0.014209 0.644259 +0.057802 0.002485 0.64549 +0.060721 -0.005676 0.65792 +0.065591 0.000503 0.663586 +0.067219 -0.011753 0.672911 +0.072041 -0.011396 0.684503 +0.069883 -0.025037 0.68734 +0.071229 -0.034042 0.705776 +0.061825 -0.04057 0.687388 +0.007749 -0.071412 0.681176 +0.017824 -0.070295 0.667247 +0.012474 -0.06725 0.654764 +0.000418 -0.068214 0.64512 +-0.011874 -0.069195 0.644369 +0.003307 -0.075928 0.612096 +0.003083 -0.085682 0.577241 +0.018014 -0.079302 0.588959 +0.029115 -0.076531 0.580916 +0.041843 0.031531 -0.054264 +0.046956 0.047724 -0.084097 +0.035804 0.02614 -0.082837 +0.06273 0.065099 -0.084751 +0.052552 0.058425 -0.123083 +0.06228 0.064521 -0.163763 +0.045416 0.048726 -0.162482 +0.040365 0.03884 -0.202978 +0.032626 0.029131 -0.160851 +0.024746 0.009647 -0.160336 +0.028611 0.017247 -0.120191 +0.028682 0.006079 -0.082532 +0.024102 -0.012962 -0.083182 +0.028796 -0.00423 -0.056545 +0.028981 -0.018724 -0.042869 +0.032428 -0.000686 -0.037014 +0.038354 0.015013 -0.034427 +0.044616 0.030651 -0.02668 +0.042996 0.03104 -0.012194 +0.053191 0.043575 -0.011379 +0.061717 0.050002 0.009207 +0.066937 0.059209 -0.016982 +0.073686 0.068823 -0.050037 +0.084616 0.075252 -0.085947 +-0.206772 0.026449 0.219402 +-0.207281 0.026054 0.196015 +-0.217749 0.037713 0.203031 +-0.202416 0.011146 0.190098 +-0.210607 0.024829 0.169402 +-0.217843 0.021974 0.143736 +-0.222708 0.036883 0.149652 +-0.235334 0.046607 0.132096 +-0.233176 0.048541 0.156668 +-0.247204 0.054673 0.163415 +-0.231543 0.048146 0.183434 +-0.231777 0.043844 0.209776 +-0.247845 0.043785 0.215521 +-0.232266 0.037564 0.231569 +-0.231348 0.029005 0.24833 +-0.219678 0.028227 0.243814 +-0.208003 0.020542 0.256459 +-0.208212 0.023241 0.238393 +-0.200402 0.013902 0.233699 +-0.193367 0.002578 0.248041 +-0.195636 0.000164 0.228781 +-0.195384 -0.01338 0.226177 +-0.197141 -0.002198 0.208566 +-0.203681 -0.0054 0.185921 +-0.124502 -0.087365 0.436587 +-0.11767 -0.024869 0.314524 +-0.110453 -0.028499 0.285352 +-0.115015 -0.048167 0.327124 +0.13688 -0.018398 -0.453705 +0.145146 -0.023604 -0.458171 +0.14324 -0.013696 -0.469193 +0.152096 -0.012738 -0.486574 +0.141551 -0.005659 -0.486438 +0.139321 1.7e-05 -0.507051 +-0.107862 0.067365 -0.955328 +-0.110694 0.049862 -0.953401 +-0.106635 0.059203 -0.963042 +-0.117119 0.043291 -0.944241 +-0.115311 0.031862 -0.950026 +-0.119732 0.013277 -0.946169 +-0.116956 0.023495 -0.95581 +-0.121207 0.013384 -0.960631 +-0.115206 0.033599 -0.965693 +-0.122194 0.042528 -0.973164 +-0.109501 0.051305 -0.969549 +-0.106108 0.067989 -0.970754 +-0.117075 0.077062 -0.975093 +-0.10601 0.083071 -0.970754 +-0.108153 0.096173 -0.970272 +-0.105911 0.089891 -0.964247 +-0.112893 0.098274 -0.956293 +-0.107236 0.082972 -0.956534 +-0.112695 0.073005 -0.946893 +-0.118653 0.05789 -0.939902 +-0.124689 0.03842 -0.932912 +-0.054251 -0.024329 0.570632 +-0.111988 -0.029166 0.149335 +-0.102827 -0.050105 0.155093 +-0.116449 -0.007149 0.142486 +-0.109537 -0.085282 0.124806 +-0.107266 -0.068478 0.141081 +-0.091664 -0.065896 0.159362 +-0.101448 -0.006364 0.205763 +-0.100792 0.002775 0.176828 +-0.095502 0.023304 0.165658 +-0.106851 0.009749 0.155037 +-0.115526 0.014161 0.134647 +-0.109753 0.047412 0.082583 +-0.115809 0.052959 0.064582 +-0.107196 0.060068 0.03924 +-0.12426 0.057162 0.041541 +-0.135163 0.05902 0.014473 +-0.110525 0.02955 0.124647 +-0.097403 0.04055 0.131958 +-0.102391 0.040374 0.113425 +-0.093323 0.048946 0.102827 +-0.102411 0.045252 0.094047 +-0.094558 0.04884 0.075356 +0.092157 0.048065 -0.362567 +0.080423 0.052717 -0.331143 +0.090473 0.050157 -0.344858 +0.103947 0.049278 -0.349382 +0.115333 0.043908 -0.360673 +0.102967 0.047707 -0.360461 +0.154261 -0.028668 -0.468845 +0.162366 -0.034723 -0.485764 +0.158569 -0.022198 -0.486355 +0.169657 -0.042323 -0.506075 +0.174619 -0.052305 -0.527479 +0.173675 -0.036642 -0.528507 +0.176606 -0.031142 -0.553545 +0.169928 -0.022748 -0.529261 +0.161807 -0.010156 -0.52955 +0.162113 -0.015904 -0.507419 +0.149061 -0.034016 -0.453482 +0.043372 0.087032 0.607996 +0.050796 0.077545 0.612581 +0.057733 0.0781 0.627504 +0.05621 0.065484 0.618657 +0.060708 0.050856 0.625212 +-0.096239 0.027188 0.263362 +-0.090507 0.027446 0.23157 +-0.099124 0.006927 0.234635 +-0.079112 0.04604 0.233137 +-0.082624 0.036539 0.199058 +-0.073807 0.044645 0.17829 +-0.086095 0.035676 0.172444 +-0.091055 0.038716 0.152578 +-0.104198 -0.015731 0.243364 +-0.087677 0.048747 0.283971 +-0.076685 0.070177 0.293928 +-0.076986 0.059499 0.267173 +-0.066782 0.058222 0.233865 +-0.219107 0.005427 0.139559 +-0.227208 0.019759 0.120406 +-0.239844 0.01887 0.100013 +-0.238051 0.031753 0.104238 +-0.251669 0.043873 0.081306 +-0.241312 0.043693 0.110012 +-0.249499 0.05209 0.11651 +-0.266 0.059433 0.093409 +-0.26285 0.057929 0.122881 +-0.276557 0.058314 0.126641 +-0.262711 0.058578 0.145212 +-0.263273 0.054613 0.16916 +-0.062151 -0.104074 0.501967 +-0.09365 -0.103528 0.496735 +-0.120818 -0.100624 0.499053 +-0.140169 -0.09861 0.499327 +-0.121535 -0.102212 0.471815 +-0.120483 -0.100043 0.445758 +0.032502 0.105532 0.635204 +0.040234 0.101373 0.627981 +0.029099 0.103641 0.625923 +-0.159727 -0.11605 -0.943517 +-0.154575 -0.122851 -0.960149 +-0.145729 -0.122779 -0.948098 +-0.159244 -0.119119 -0.973887 +-0.14056 -0.118146 -0.97678 +-0.136031 -0.125444 -0.971237 +-0.124119 -0.121526 -0.966898 +-0.134307 -0.124716 -0.957739 +-0.13451 -0.12082 -0.938938 +-0.125296 -0.116442 -0.93725 +-0.132554 -0.117879 -0.901824 +-0.12834 -0.118523 -0.851712 +-0.137255 -0.119545 -0.85154 +-0.139205 -0.123264 -0.795694 +-0.147709 -0.116859 -0.829962 +-0.1577 -0.113684 -0.792993 +-0.156937 -0.108369 -0.848223 +-0.16437 -0.097422 -0.845784 +-0.162056 -0.10353 -0.890525 +-0.168228 -0.09362 -0.914112 +-0.165287 -0.106934 -0.925923 +-0.172012 -0.105366 -0.947133 +-0.177922 -0.090558 -0.947133 +-0.176295 -0.100282 -0.965211 +-0.17577 -0.091291 -0.975334 +-0.168679 -0.107233 -0.975334 +-0.159577 -0.109299 -0.978226 +0.19361 0.100996 -0.957016 +-0.346552 0.051592 -0.138698 +-0.05331 0.043614 -0.011406 +-0.043116 0.031079 -0.012221 +-0.044736 0.030689 -0.026708 +-0.036458 0.035374 -0.005666 +-0.034593 0.02256 -0.016369 +-0.026002 0.020196 -0.027749 +-0.032548 -0.000647 -0.037041 +-0.038473 0.015053 -0.034454 +-0.042106 0.031463 -0.054643 +-0.036354 0.025857 -0.083922 +-0.047462 0.04744 -0.085184 +-0.053157 0.058033 -0.124522 +-0.063386 0.064816 -0.085837 +-0.085277 0.075076 -0.086681 +-0.074292 0.068754 -0.050417 +-0.086456 0.067805 -0.017768 +-0.067251 0.059248 -0.01701 +-0.061836 0.050042 0.009179 +-0.072076 0.051032 0.033361 +-0.063817 0.04906 0.040277 +-0.070173 0.052519 0.067033 +-0.056287 0.052694 0.047223 +-0.043826 0.062622 0.049801 +-0.041395 0.049328 0.017863 +-0.026846 0.045526 -0.009819 +0.026111 -0.038364 -0.058814 +0.02288 -0.033403 -0.084919 +0.027925 -0.055292 -0.087379 +0.02033 -0.022247 -0.121654 +0.021046 -0.010807 -0.16115 +0.022959 -0.030884 -0.1634 +0.029038 -0.037487 -0.206713 +0.03026 -0.050994 -0.166212 +0.032675 -0.064246 -0.126926 +0.035877 -0.074808 -0.088427 +0.048094 -0.089411 -0.089855 +0.040517 -0.087569 -0.059881 +0.051296 -0.100298 -0.037443 +0.035125 -0.08673 -0.04151 +0.028149 -0.086287 -0.034555 +0.020779 -0.046423 -0.035166 +0.025827 -0.040847 -0.041556 +-5.2e-05 -0.062188 0.00548 +0.006463 -0.067002 -0.005033 +0.012015 -0.073985 -0.017958 +0.011292 -0.086761 -0.001085 +0.019728 -0.107513 0.001326 +-0.351987 0.047534 -0.115422 +-0.14288 0.119413 -0.96321 +-0.164423 0.119848 -0.961836 +-0.124308 0.111749 -0.956052 +-0.124703 0.115167 -0.964728 +-0.131241 0.115405 -0.971237 +-0.146413 0.109641 -0.974611 +-0.154631 0.117995 -0.971237 +-0.177461 0.114153 -0.9722 +-0.183398 0.103486 -0.976057 +-0.196445 0.10383 -0.971959 +-0.209582 0.088665 -0.970754 +-0.205996 0.096639 -0.964969 +-0.206554 0.088005 -0.954943 +-0.198745 0.100946 -0.957016 +-0.186213 0.111705 -0.961681 +-0.101613 0.044489 0.508518 +-0.091777 0.062148 0.497244 +-0.115934 0.045627 0.502282 +-0.065327 0.068468 0.494147 +-0.080758 0.081006 0.485764 +-0.072828 0.096906 0.474952 +-0.094039 0.092566 0.477121 +-0.105515 0.105185 0.467462 +-0.114767 0.087024 0.475675 +-0.128024 0.080515 0.470854 +-0.129064 0.06286 0.483867 +-0.141192 0.04135 0.481454 +-0.131264 0.042973 0.496583 +-0.086956 0.0366 0.517973 +-0.077142 0.031594 0.526258 +-0.062917 0.040509 0.517528 +-0.042954 0.041937 0.516578 +-0.051125 0.05472 0.504975 +-0.037893 0.067004 0.494885 +0.093484 -0.097607 0.529183 +0.073719 -0.095196 0.538101 +0.094082 -0.086854 0.544358 +0.063594 -0.088335 0.548857 +0.062627 -0.07834 0.556719 +0.043104 -0.080268 0.563468 +0.034656 -0.067018 0.583958 +-0.159365 0.009212 -0.972683 +-0.138816 0.025941 -0.973647 +-0.141228 -0.000439 -0.968586 +-0.138367 0.050982 -0.977021 +-0.124538 -0.006661 -0.956534 +-0.130607 -0.004067 -0.963041 +-0.129136 -0.034829 -0.963282 +-0.136905 -0.046366 -0.96979 +-0.13199 -0.067156 -0.975575 +-0.148774 -0.059115 -0.97437 +-0.161709 -0.072079 -0.977504 +-0.16425 -0.051076 -0.974611 +-0.175649 -0.042919 -0.972683 +-0.186474 -0.009328 -0.973165 +-0.175349 -0.002784 -0.973887 +-0.183278 0.02446 -0.975575 +-0.203258 0.038845 -0.975816 +-0.188311 0.049937 -0.977263 +-0.188748 0.069427 -0.977986 +-0.16395 0.05973 -0.977745 +-0.136642 0.068833 -0.977745 +0.020641 0.106222 0.68288 +0.010145 0.114881 0.68217 +-0.029 -0.004162 0.808506 +-0.018674 -0.009285 0.810771 +-0.027318 0.00999 0.810749 +-0.009358 -0.027252 0.807329 +-0.009189 -0.013674 0.811803 +0.000182 -0.009914 0.812958 +-0.008739 -0.00082 0.81441 +-0.026137 0.022133 0.811998 +-0.034725 0.029576 0.807259 +-0.036296 0.04573 0.803895 +-0.043479 0.035008 0.801776 +-0.050999 0.040011 0.794549 +-0.046173 0.020569 0.800655 +-0.049765 0.005415 0.797041 +-0.039102 0.000983 0.803895 +-0.030572 -0.017744 0.803895 +-0.033041 -0.031611 0.797041 +-0.021032 -0.035161 0.800678 +-0.010705 -0.050268 0.794618 +-0.010144 -0.039546 0.801847 +0.000295 -0.037069 0.804011 +-0.129301 0.086215 -0.97678 +-0.133015 -0.094692 0.454036 +-0.160025 -0.094814 0.509594 +-0.154706 -0.098454 0.492353 +-0.167264 -0.097792 0.475314 +-0.148605 -0.096707 0.473117 +-0.143549 -0.091627 0.451185 +-0.072193 0.039441 0.677409 +-0.069623 0.032875 0.660902 +-0.072019 0.033374 0.670516 +-0.073593 0.030982 0.680927 +-0.074948 0.036613 0.688621 +-0.071575 0.047963 0.690054 +-0.070167 0.058917 0.70771 +-0.068208 0.061066 0.690866 +-0.063995 0.074404 0.689656 +-0.065714 0.069965 0.676427 +-0.063559 0.081997 0.666975 +-0.065384 0.069769 0.659252 +-0.062969 0.074671 0.643834 +-0.066039 0.05651 0.649292 +-0.065441 0.044988 0.639524 +-0.067935 0.038771 0.651999 +-0.067598 0.025852 0.654161 +0.006898 0.113222 0.666528 +0.019509 0.109191 0.657762 +0.012014 0.113397 0.650796 +-0.346918 0.12142 -0.117927 +-0.354088 0.121599 -0.124121 +-0.344419 0.115564 -0.103046 +-0.34972 0.117279 -0.110488 +0.127701 -0.017526 -0.914112 +0.134572 -0.007901 -0.908327 +0.143269 -0.019543 -0.89748 +0.143442 -0.032239 -0.887839 +0.151011 -0.035966 -0.894347 +0.156411 -0.049333 -0.89507 +0.158344 -0.037714 -0.906158 +0.165572 -0.036508 -0.92544 +-0.02225 0.059766 0.501427 +-0.023436 0.07472 0.489154 +-0.011365 0.079551 0.484593 +-0.024381 0.086541 0.474952 +-0.015704 0.092085 0.464828 +-0.029443 0.098834 0.459284 +-0.029254 0.111213 0.441758 +-0.039324 0.10751 0.457116 +-0.050653 0.119321 0.4515 +-0.054991 0.107029 0.465552 +-0.0709 0.112091 0.464033 +0.020423 0.102461 0.608305 +0.035992 0.09389 0.606934 +0.025793 0.095655 0.598077 +0.051857 0.097225 0.633387 +0.04315 0.095805 0.617906 +-0.181442 -0.053912 -0.970754 +-0.185342 -0.0355 -0.968585 +-0.188407 -0.024348 -0.958462 +-0.192056 -0.015446 -0.969067 +-0.200678 0.00534 -0.970995 +-0.205676 0.015018 -0.963765 +-0.209533 0.02627 -0.97196 +-0.215892 0.046993 -0.970995 +-0.020731 -0.081957 -0.028567 +-0.015762 -0.05829 -0.029114 +-0.012329 -0.073985 -0.017958 +-0.020393 -0.046404 -0.035179 +-0.012687 -0.019453 -0.035064 +-0.011094 -0.036464 -0.029315 +-5.2e-05 -0.043146 -0.02248 +-0.006793 -0.067002 -0.005033 +-0.011606 -0.086761 -0.001326 +-0.01016 -0.099053 0.016992 +-0.019801 -0.107513 0.000844 +-0.03519 -0.122715 0.002285 +-0.031337 -0.113581 -0.011936 +-0.048416 -0.114586 -0.018931 +-0.031544 -0.100799 -0.024715 +-0.028404 -0.086268 -0.034568 +-0.035628 -0.086704 -0.041528 +-0.02362 -0.066445 -0.03714 +-0.025699 -0.040813 -0.041578 +-0.217927 0.056178 -0.96256 +-0.216728 0.068043 -0.96979 +-0.204529 0.079384 -0.975334 +-0.187076 0.088261 -0.978226 +-0.164926 0.099509 -0.977263 +-0.140782 0.098259 -0.976539 +-0.12071 0.098647 -0.974129 +-0.015554 0.023029 0.566772 +-0.019233 0.031936 0.534362 +-0.006303 0.039781 0.519301 +-0.017401 0.043994 0.515556 +-0.043958 0.004853 0.609394 +-0.051897 0.010736 0.631519 +-0.043078 0.012732 0.61846 +-0.045175 0.024826 0.613791 +-0.029703 0.021295 0.609428 +-0.015003 0.027943 0.601931 +-0.015397 0.021991 0.592477 +-0.005867 0.023916 0.567498 +-0.097654 0.15403 0.414501 +-0.106021 0.1509 0.422436 +-0.114099 0.14311 0.431965 +-0.105181 0.145039 0.431965 +-0.09795 0.134674 0.443848 +-0.094669 0.144955 0.431429 +-0.080782 0.140532 0.434579 +-0.089942 0.14945 0.421973 +-0.084901 0.150248 0.411978 +-0.068533 0.142851 0.413384 +-0.079138 0.149118 0.400054 +-0.068103 0.145349 0.392723 +-0.08706 0.150934 0.389497 +-0.094948 0.154109 0.402241 +-0.104885 0.155597 0.405945 +-0.123565 0.146136 0.401605 +-0.113336 0.154226 0.409681 +-0.122239 0.148005 0.414019 +-0.081095 0.052591 0.072127 +-0.082705 0.052338 0.053784 +-0.089286 0.058018 0.03567 +-0.098251 0.065585 0.010731 +0.28925 0.045602 -0.009567 +0.292529 0.034588 -0.011436 +0.284719 0.030641 0.007874 +0.298173 0.032736 -0.023181 +0.302759 0.030528 -0.010877 +0.312972 0.037178 -0.005232 +0.301493 0.022318 0.010571 +0.296961 0.01204 0.043347 +0.286506 0.011417 0.039962 +0.270972 0.000318 0.073557 +0.278498 0.015939 0.038776 +0.271804 0.025941 0.039055 +0.254717 0.020693 0.073311 +0.269021 0.037206 0.041245 +0.267648 0.047465 0.045042 +0.280242 0.053479 0.012775 +0.288229 0.065783 -0.000674 +0.287556 0.057176 -0.006856 +0.289871 0.057957 -0.019069 +0.295948 0.074193 -0.034474 +0.293578 0.055729 -0.035571 +0.299659 0.054995 -0.052561 +0.298705 0.042921 -0.044444 +0.310441 0.034345 -0.058644 +0.305132 0.033182 -0.038279 +0.235805 -0.050145 0.243698 +0.245376 -0.048187 0.236911 +0.246224 -0.046905 0.24834 +0.242491 -0.046658 0.225826 +0.255 -0.04119 0.231842 +0.266692 -0.032272 0.222715 +0.26212 -0.034758 0.239813 +0.263623 -0.029623 0.251835 +0.256126 -0.03921 0.250514 +0.246606 -0.046318 0.264945 +0.245716 -0.046049 0.285837 +0.236221 -0.052131 0.281348 +0.22488 -0.058924 0.302204 +0.225541 -0.054724 0.276795 +0.214081 -0.053728 0.27235 +0.225508 -0.052151 0.25575 +0.224536 -0.049797 0.239557 +0.214015 -0.045365 0.236305 +0.221176 -0.04779 0.228147 +0.216208 -0.041047 0.217972 +0.229561 -0.046609 0.221203 +0.240155 -0.043871 0.209954 +-0.059417 0.009418 0.787695 +-0.053243 -0.009739 0.790062 +-0.05706 -0.024749 0.778846 +-0.045051 -0.028037 0.790062 +-0.035286 -0.044335 0.787695 +-0.180683 -0.041219 -0.578831 +-0.175537 -0.031146 -0.553571 +-0.176602 -0.025983 -0.580031 +-0.172605 -0.036647 -0.528534 +-0.168859 -0.022753 -0.529288 +-0.161242 -0.015909 -0.507445 +-0.160737 -0.010161 -0.529577 +-0.14849 -0.00131 -0.529345 +-0.159038 -0.005362 -0.554497 +-0.154699 -0.003267 -0.581093 +-0.168224 -0.012792 -0.580668 +-0.175483 -0.022229 -0.612273 +-0.169777 -0.021693 -0.649493 +-0.17872 -0.03387 -0.646851 +-0.179299 -0.047129 -0.689553 +-0.183473 -0.048939 -0.644727 +-0.185221 -0.064888 -0.643884 +-0.184318 -0.052534 -0.60895 +-0.181709 -0.057628 -0.577707 +-0.179005 -0.07687 -0.577528 +-0.177507 -0.063217 -0.551419 +-0.170991 -0.068748 -0.52619 +-0.173551 -0.05231 -0.527506 +-0.168785 -0.042328 -0.506102 +0.092154 -0.05311 0.557856 +0.110348 -0.062122 0.551578 +0.118544 -0.049829 0.550614 +0.091429 -0.071075 0.555353 +0.112759 -0.07562 0.547722 +0.124087 -0.083092 0.539767 +0.123488 -0.090952 0.530619 +0.147208 -0.082727 0.532732 +0.163751 -0.086596 0.523111 +0.167215 -0.074061 0.532267 +0.181355 -0.068716 0.527983 +0.169725 -0.059203 0.536731 +0.16996 -0.044296 0.538715 +0.157488 -0.047446 0.542037 +0.139754 -0.03396 0.544878 +0.138367 -0.044594 0.546776 +0.117945 -0.037202 0.550143 +0.109874 -0.02361 0.549189 +0.093007 -0.030242 0.555939 +0.070108 -0.028687 0.562213 +0.069634 -0.03808 0.564859 +0.054196 -0.045559 0.577154 +0.068181 -0.04832 0.566305 +0.066004 -0.057483 0.565488 +0.064556 -0.067734 0.563709 +0.140035 -0.093514 -0.092145 +0.148605 -0.086131 -0.134493 +0.157508 -0.080765 -0.09277 +0.155489 -0.074654 -0.177276 +0.169056 -0.0578 -0.17712 +0.175859 -0.05251 -0.134745 +0.178984 -0.044802 -0.092636 +0.171196 -0.063998 -0.093178 +0.162918 -0.075809 -0.054259 +0.162813 -0.069618 -0.0175 +0.15164 -0.088465 -0.018245 +0.141603 -0.099259 0.009952 +0.13688 -0.101658 -0.018535 +0.1209 -0.109584 -0.023743 +0.130686 -0.101752 -0.053921 +0.117814 -0.101416 -0.091734 +0.091184 -0.103428 -0.091451 +0.019782 0.089341 0.584488 +-0.056232 0.093284 0.679412 +-0.072636 0.057242 0.724416 +-0.066049 0.072554 0.719436 +-0.059213 0.086421 0.724422 +-0.059279 0.085457 0.712483 +-0.051306 0.093238 0.699401 +-0.058462 0.085324 0.699207 +-0.047663 0.097322 0.687755 +0.005499 0.057805 -0.010951 +0.012745 0.065075 0.010335 +0.015692 0.053492 -0.012164 +0.007199 0.074492 0.042059 +0.026725 0.070387 0.044149 +0.042585 0.072901 0.084773 +0.044231 0.062609 0.049328 +0.056432 0.052674 0.046995 +0.041781 0.04931 0.017394 +0.036587 0.035342 -0.005884 +0.027221 0.045504 -0.010286 +0.018781 0.04154 -0.022529 +0.019554 0.029015 -0.029508 +0.012607 0.034629 -0.03064 +-6.1e-05 0.037879 -0.031299 +-6.3e-05 0.049097 -0.021972 +-0.005372 0.057808 -0.010711 +-0.015317 0.053505 -0.011692 +-0.01235 0.065084 0.010811 +-0.026311 0.0704 0.044621 +-0.007026 0.074499 0.042295 +-3e-05 0.078582 0.077556 +0.034475 0.02252 -0.016341 +0.027855 0.006828 -0.029477 +0.025882 0.020158 -0.027721 +0.070069 0.052493 0.067051 +0.063705 0.049027 0.040299 +0.1708 -0.048786 -0.016719 +0.178692 -0.035781 -0.052935 +0.15983 -0.064509 0.01593 +0.181799 -0.02331 -0.091181 +0.179477 7.1e-05 -0.089955 +0.172651 0.023084 -0.088665 +0.151966 -0.0452 0.070189 +0.15501 -0.056338 0.04692 +0.149717 -0.077178 0.043467 +-0.060997 0.014244 0.640455 +0.029437 0.020503 0.56901 +0.036427 0.012307 0.596246 +0.027474 0.026888 0.545481 +0.022206 0.024118 0.555512 +0.014975 0.022983 0.566758 +0.005541 0.023894 0.567491 +0.051751 0.010618 0.631502 +0.042587 0.012608 0.618431 +0.04362 0.004713 0.609833 +0.047187 -0.006745 0.609202 +0.044487 0.007977 0.591049 +0.04478 0.004342 0.572568 +0.039869 0.016341 0.56975 +0.035477 0.023909 0.55068 +0.231228 0.028966 0.248357 +0.232147 0.037525 0.231595 +0.219558 0.028188 0.243842 +0.247727 0.043746 0.215549 +0.231658 0.043805 0.209803 +0.231425 0.048107 0.183461 +0.217631 0.037675 0.203057 +0.207163 0.026016 0.196042 +0.206654 0.026409 0.219429 +0.200283 0.013862 0.233726 +0.208092 0.023203 0.238421 +0.207884 0.020504 0.256487 +0.197155 0.012116 0.273302 +0.206211 0.017357 0.277512 +0.203911 0.01663 0.302668 +0.21645 0.019107 0.281786 +0.22791 0.018111 0.286231 +0.229794 0.023032 0.265276 +0.244364 0.024711 0.252074 +0.25506 0.017995 0.254749 +0.260255 0.027033 0.238936 +0.274955 0.025766 0.220281 +0.262699 0.037508 0.219172 +0.263562 0.047989 0.194952 +-0.055273 -0.045936 -0.519443 +-0.057572 -0.052139 -0.497425 +-0.056619 -0.062531 -0.518394 +-0.061513 -0.042844 -0.478046 +-0.062629 -0.056837 -0.477045 +-0.067662 -0.05984 -0.460489 +-0.06743 -0.071226 -0.476496 +-0.075061 -0.082434 -0.476421 +-0.067654 -0.082092 -0.4964 +-0.071384 -0.093884 -0.51898 +-0.0619 -0.078097 -0.517919 +-0.057834 -0.072877 -0.544067 +-0.062274 -0.085678 -0.573733 +-0.056388 -0.067092 -0.573757 +-0.059578 -0.062913 -0.61318 +-0.054796 -0.050683 -0.574783 +-0.057039 -0.034769 -0.576001 +-0.054895 -0.039873 -0.546142 +-0.057761 -0.030459 -0.520797 +-0.064086 -0.016652 -0.522259 +-0.0623 -0.022607 -0.50001 +-0.068868 -0.018314 -0.480453 +-0.063753 -0.02879 -0.479277 +-0.065459 -0.034827 -0.462233 +-0.109923 0.097139 0.355728 +-0.090865 0.091656 0.35474 +-0.071542 0.095326 0.351671 +-0.056188 0.093387 0.339369 +-0.077145 0.084673 0.334731 +-0.079236 0.078124 0.313693 +-0.100936 -0.085951 -0.260342 +-0.120891 -0.085269 -0.26252 +-0.108267 -0.077994 -0.29988 +-0.131186 -0.08996 -0.220553 +-0.137402 -0.07889 -0.262806 +-0.152178 -0.065241 -0.261914 +-0.141676 -0.067763 -0.300371 +-0.143641 -0.056073 -0.33192 +-0.129503 -0.065865 -0.335172 +-0.121419 -0.06514 -0.367938 +-0.112603 -0.069717 -0.334966 +-0.095862 -0.069837 -0.333635 +-0.091719 -0.066681 -0.364809 +-0.079744 -0.06832 -0.330753 +-0.066743 -0.06431 -0.326443 +-0.072427 -0.072997 -0.293351 +-0.062856 -0.075002 -0.253308 +-0.081215 -0.082472 -0.256694 +-0.088531 -0.093675 -0.217257 +-0.074539 -0.096574 -0.174349 +-0.097136 -0.100976 -0.176111 +-0.106882 -0.103202 -0.134252 +-0.119835 -0.097944 -0.177229 +-0.139993 -0.088744 -0.177323 +0.018239 0.107256 0.671512 +-0.063667 -0.030882 -0.613891 +-0.063363 -0.02034 -0.57735 +-0.07406 -0.008564 -0.578235 +-0.067545 -0.011638 -0.548953 +-0.073747 -0.006272 -0.523751 +-0.08742 0.000865 -0.525118 +-0.080661 -0.003753 -0.502829 +-0.085059 -0.001355 -0.479605 +-0.077569 -0.009841 -0.481781 +-0.076309 -0.016679 -0.464386 +0.000655 0.093766 0.458566 +-0.002688 0.085095 0.474469 +0.01032 0.079544 0.484598 +0.022375 0.074701 0.489167 +0.023779 0.086522 0.474965 +0.029284 0.098801 0.459307 +0.016284 0.094475 0.458816 +0.003999 0.097856 0.43977 +0.008865 0.104204 0.421525 +-0.001514 0.098828 0.420243 +-0.002447 0.094977 0.395652 +-0.010454 0.103501 0.414762 +-0.019698 0.112569 0.404978 +-0.021112 0.113229 0.423026 +-0.034872 0.124633 0.422118 +-0.108977 -0.07772 -0.938697 +-0.109259 -0.095338 -0.938938 +-0.107241 -0.088549 -0.953641 +-0.113633 -0.098839 -0.923753 +-0.116069 -0.109289 -0.937974 +-0.115738 -0.112744 -0.967861 +-0.120446 -0.104066 -0.975093 +-0.111521 -0.09868 -0.966175 +-0.110599 -0.079589 -0.965452 +-0.118576 -0.073285 -0.973405 +-0.114012 -0.059077 -0.964005 +-0.119658 -0.041412 -0.959667 +-0.117299 -0.048889 -0.948579 +-0.122066 -0.040734 -0.934599 +-0.115623 -0.05857 -0.935082 +-0.113042 -0.071006 -0.923031 +-0.116876 -0.070528 -0.907364 +-0.114063 -0.084261 -0.914594 +-0.114361 -0.096332 -0.891874 +0.092611 -0.019001 0.551748 +0.121772 -0.017925 0.544638 +0.115293 -0.000121 0.538382 +0.097639 0.005006 0.540291 +0.071429 0.018691 0.538825 +0.0619 0.007477 0.546542 +0.052729 -0.000966 0.55956 +0.070088 -0.007715 0.552091 +0.070817 -0.020132 0.558359 +0.054188 -0.023873 0.571133 +-0.138607 0.02768 -0.35295 +-0.145511 0.020286 -0.352691 +-0.152296 0.016528 -0.338967 +-0.143987 0.017902 -0.368736 +-0.136642 0.01861 -0.394138 +-0.143563 0.00789 -0.390521 +-0.137355 0.00316 -0.416302 +-0.146884 -0.00545 -0.387504 +-0.144468 -0.015412 -0.390165 +-0.15644 -0.015421 -0.353648 +-0.161971 -0.027536 -0.325287 +-0.165474 -0.010869 -0.323954 +-0.17139 -0.004353 -0.293696 +-0.163127 0.005355 -0.322302 +-0.156493 0.021972 -0.321189 +-0.159237 0.031684 -0.291354 +-0.14683 0.03588 -0.320043 +-0.134195 0.048864 -0.318919 +-0.133129 0.041051 -0.336372 +-0.119072 0.0442 -0.348832 +-0.130775 0.036182 -0.351647 +-0.126896 0.038675 -0.36124 +-0.077792 -0.020144 -0.448556 +-0.069431 -0.035049 -0.448115 +-0.326873 0.070892 -0.095212 +-0.323667 0.076354 -0.088293 +-0.325844 0.086822 -0.08913 +-0.303517 0.08504 -0.050481 +-0.305062 0.071913 -0.060507 +-0.298297 0.05325 -0.051365 +-0.335226 0.071435 -0.105031 +-0.336521 0.091323 -0.101414 +0.095477 0.114936 0.354458 +0.074906 0.108128 0.353034 +0.083691 0.118559 0.354297 +0.055065 0.107161 0.357513 +0.036546 0.108676 0.369588 +0.052728 0.116812 0.361744 +0.062169 0.129533 0.366115 +0.070881 0.123564 0.35888 +0.088753 0.131816 0.363311 +0.090854 0.143024 0.374301 +0.100805 0.141939 0.376978 +0.109457 0.13928 0.37762 +0.118765 0.132885 0.374621 +0.109192 0.125534 0.364114 +0.109846 0.110711 0.356521 +0.12183 0.107697 0.360592 +0.109803 0.0971 0.355756 +0.113684 0.08144 0.361935 +0.090746 0.091617 0.354768 +0.071427 0.095289 0.351696 +0.139244 -0.091472 0.060615 +0.142666 -0.093081 0.038234 +0.130994 -0.108076 0.032687 +-0.01243 0.098552 0.594381 +-0.025239 0.095565 0.597988 +-0.075025 0.108167 0.353006 +-0.055184 0.107199 0.357485 +-0.052847 0.11685 0.361717 +-0.036657 0.108709 0.369566 +-0.025423 0.111393 0.38572 +-0.024164 0.101644 0.37025 +-0.014121 0.093803 0.35882 +-0.031916 0.096268 0.349002 +-0.04652 0.092123 0.322665 +-0.040688 0.086532 0.306737 +-0.061795 0.083175 0.305975 +-0.000933 0.105174 0.600462 +-0.035943 -0.066474 0.205451 +-0.0186 -0.064312 0.200394 +-0.08577 -0.092697 -0.476498 +-0.097479 -0.099416 -0.476688 +-0.090447 -0.106019 -0.497618 +-0.101793 -0.091054 -0.460571 +-0.110883 -0.10191 -0.477474 +-0.123514 -0.099711 -0.478696 +-0.118719 -0.11206 -0.49993 +-0.128373 -0.119869 -0.525079 +-0.112883 -0.122273 -0.52364 +-0.106856 -0.129589 -0.550984 +-0.098014 -0.118725 -0.522114 +-0.083467 -0.107549 -0.52012 +-0.077161 -0.107657 -0.546619 +-0.082954 -0.081004 -0.460122 +-0.081541 -0.070322 -0.446183 +-0.092255 -0.079879 -0.45023 +-0.104304 -0.079581 -0.445782 +-0.155674 -0.074622 -0.177299 +-0.16916 -0.057773 -0.177139 +-0.161643 -0.062189 -0.219637 +-0.16425 -0.050033 -0.260919 +0.127133 0.121437 0.370812 +0.139715 0.083369 0.375361 +0.134388 0.103125 0.368146 +-0.068844 0.012849 0.774859 +-0.063682 0.02886 0.78246 +-0.050887 0.056595 0.78944 +-0.058855 0.0443 0.785701 +-0.064074 0.048032 0.775737 +-0.060763 0.065345 0.769511 +-0.069069 0.051193 0.763654 +-0.071972 0.053638 0.751696 +-0.076084 0.035166 0.755922 +-0.080451 0.020839 0.742337 +-0.076253 0.015994 0.760403 +-0.07311 -0.002734 0.762646 +-0.075242 -0.016457 0.745698 +-0.067498 -0.021175 0.764266 +-0.059978 -0.038044 0.764266 +-0.009606 0.112575 0.67871 +-0.015597 0.118906 0.696268 +-0.00595 0.117643 0.685904 +0.226135 -0.009565 0.137964 +0.240308 -0.009492 0.116998 +0.238392 -0.021308 0.139073 +0.245799 0.004574 0.098227 +0.25536 -0.0058 0.098753 +0.268712 -0.011363 0.101984 +0.281644 -0.011411 0.106607 +0.268632 -0.021294 0.123851 +0.269432 -0.027605 0.148441 +0.253363 -0.027546 0.142696 +0.237528 -0.031788 0.163293 +0.237937 -0.038374 0.189058 +0.222965 -0.032136 0.185436 +0.211831 -0.032069 0.2031 +0.210709 -0.020394 0.184326 +0.203562 -0.00544 0.185949 +0.213017 -0.008303 0.161075 +0.218989 0.005389 0.139586 +0.217724 0.021935 0.143763 +0.227089 0.01972 0.120432 +0.237932 0.031715 0.104265 +0.239727 0.018832 0.100041 +0.130115 0.072367 -0.087024 +0.108208 0.07774 -0.08661 +0.095777 0.07914 -0.125732 +-0.345718 0.120033 -0.130569 +-0.3545 0.113702 -0.130478 +0.162066 0.04364 -0.087898 +0.14782 0.060596 -0.087193 +0.014973 -0.081226 0.124942 +0.1861 -0.077551 0.515917 +0.193564 -0.061189 0.519585 +0.202622 -0.050908 0.506478 +0.194808 -0.044467 0.52252 +0.189091 -0.027347 0.524711 +0.182835 -0.037088 0.532901 +0.171853 -0.028443 0.534873 +0.159701 -0.094919 0.51001 +0.175026 -0.088818 0.512794 +0.18688 -0.085619 0.498698 +0.112989 -0.099264 0.514738 +0.119816 -0.10079 0.499372 +0.12111 -0.102352 0.472425 +0.139744 -0.098748 0.499938 +0.154484 -0.098526 0.492574 +-0.057988 0.086278 0.737506 +-0.05054 0.098287 0.724297 +-0.040337 0.109009 0.725419 +-0.04241 0.106365 0.71133 +-0.034774 0.10754 0.698319 +-0.043208 0.101822 0.699208 +-0.038916 0.101242 0.690966 +-0.037643 -0.056629 0.774859 +-0.024399 -0.058749 0.782484 +-0.012425 -0.070703 0.772788 +-0.011827 -0.061132 0.78577 +0.000295 -0.058512 0.789555 +-0.060763 -0.048909 0.745698 +-0.050326 -0.053627 0.762646 +-0.039102 -0.066493 0.760403 +0.059026 0.08642 0.737646 +0.06025 0.086563 0.724562 +0.067086 0.072696 0.719576 +0.060317 0.085599 0.712624 +0.040503 0.101825 0.693342 +0.032757 0.103531 0.684267 +-0.0291 -0.018684 -0.042895 +-0.02906 -0.004299 -0.056925 +-0.026374 -0.038433 -0.059194 +-0.028475 -0.055575 -0.088465 +-0.02343 -0.033688 -0.086005 +-0.020775 -0.022888 -0.123089 +-0.024652 -0.013246 -0.084268 +-0.029232 0.005796 -0.083617 +-0.029056 0.016608 -0.121626 +-0.015647 0.006899 -0.037608 +-0.017185 -0.003649 -0.036607 +-0.008532 -0.002853 -0.039356 +-0.058424 0.097628 0.648 +-0.054499 0.096907 0.633642 +-0.059248 0.088844 0.638419 +-0.05795 0.078018 0.627512 +-0.007443 -0.071435 0.681152 +-0.116739 -0.136397 -0.580844 +-0.100306 -0.131872 -0.579544 +-0.097219 -0.130442 -0.616268 +-0.085158 -0.121117 -0.577172 +-0.072117 -0.104671 -0.575223 +-0.342027 0.053983 -0.116465 +-0.062924 0.055834 0.198102 +-0.06222 0.057004 0.16879 +-0.062482 0.064178 0.147523 +-0.056597 0.070898 0.121654 +-0.074757 0.060879 0.130228 +-0.084062 0.055142 0.11443 +-0.086826 0.049633 0.133312 +-0.050465 0.066241 0.237 +-0.036428 0.078589 0.278784 +0.130528 0.070492 -0.168451 +0.140569 0.067337 -0.127511 +0.107643 0.076923 -0.167086 +0.083619 0.075084 -0.165215 +0.073121 0.068867 -0.205393 +0.105061 0.144999 0.431993 +0.113979 0.143071 0.431993 +0.118559 0.131743 0.442671 +-0.010837 0.077735 0.109421 +0.01077 0.077721 0.10943 +0.022527 0.074634 0.135932 +0.03386 0.076146 0.114557 +0.056505 0.070872 0.121672 +-0.107441 -0.101702 0.109968 +-0.099315 -0.114582 0.096708 +-0.089676 -0.125385 0.083379 +-0.069075 -0.050206 -0.446984 +-0.073878 -0.060366 -0.446474 +-0.074237 -0.059456 -0.424272 +-0.03394 0.076172 0.114539 +-0.042417 0.072921 0.085 +-0.014258 -0.11014 0.033141 +-0.157132 -0.090422 0.430384 +-0.140529 -0.081659 0.429758 +-0.145454 0.048158 0.463138 +-0.017373 -0.070336 0.667195 +-0.127703 -0.087862 -0.461567 +-0.13624 -0.094361 -0.479914 +-0.146737 -0.087653 -0.48091 +-0.145564 -0.100101 -0.501844 +-0.155102 -0.101739 -0.525344 +-0.142492 -0.11326 -0.525774 +-0.138801 -0.12618 -0.553256 +-0.148871 -0.128621 -0.581038 +-0.133194 -0.135391 -0.581677 +-0.127663 -0.138176 -0.616978 +-0.150984 -0.068803 0.390182 +-0.078496 0.031985 0.709579 +-0.080563 0.025413 0.7244 +-0.077425 0.041899 0.723785 +-0.073202 0.055797 0.737495 +-0.06539 0.070966 0.745601 +-0.056146 0.084691 0.751708 +-0.018935 0.105176 0.619158 +-0.007126 0.109562 0.629151 +-0.051168 0.076338 0.775737 +-0.054422 0.081515 0.76366 +-0.046346 0.095969 0.755939 +-0.034454 0.104531 0.76042 +-0.037984 0.108279 0.742859 +-0.029419 0.116585 0.725169 +-0.047298 0.024671 -0.310259 +-0.037849 0.017816 -0.27972 +-0.040346 0.008987 -0.311486 +-0.038052 0.027864 -0.243939 +-0.030119 0.00961 -0.244461 +-0.02382 4.2e-05 -0.203348 +-0.027026 -0.008049 -0.245536 +-0.029351 -0.026807 -0.247007 +-0.031831 -0.016508 -0.282364 +-0.038859 -0.023333 -0.315602 +-0.037421 -0.00768 -0.313056 +-0.044946 0.004605 -0.332387 +-0.053967 0.004087 -0.358133 +-0.054656 0.017964 -0.342715 +-0.065584 0.033024 -0.344384 +-0.056152 0.029739 -0.330176 +-0.058364 0.04014 -0.310503 +-0.070722 0.05147 -0.311389 +-0.060798 0.050331 -0.280418 +-0.06604 0.059635 -0.245537 +-0.049933 0.044911 -0.244394 +-0.039882 0.038028 -0.203344 +-0.038989 -0.073927 0.742832 +-0.026679 -0.077612 0.752926 +-0.013508 -0.085225 0.741744 +-0.013247 -0.079702 0.757686 +0.000222 -0.077653 0.76359 +-0.123761 -0.022861 -0.937492 +-0.128272 -0.030546 -0.92279 +-0.132998 -0.017576 -0.914112 +-0.133664 -0.033246 -0.905194 +-0.132777 -0.045326 -0.894106 +-0.124311 -0.054024 -0.87316 +-0.124777 -0.059983 -0.896517 +-0.11425 -0.070799 -0.880902 +-0.105875 -0.070266 -0.84386 +-0.105368 -0.07996 -0.844737 +-0.09669 -0.083735 -0.796708 +-0.108292 -0.09176 -0.848477 +-0.114245 -0.103894 -0.85013 +-0.075698 -0.022685 -0.438931 +-0.066752 -0.020972 -0.416209 +-0.093239 0.007916 -0.466902 +-0.086006 0.003262 -0.460468 +-0.079904 0.004909 -0.444729 +-0.07522 -0.005856 -0.439366 +-0.066779 0.011585 -0.400071 +-0.061683 0.002995 -0.386192 +-0.058576 -0.009739 -0.386374 +-0.060099 -0.025929 -0.40223 +-0.214472 0.06587 -0.955087 +-0.211744 0.045888 -0.955087 +-0.205851 0.025451 -0.955328 +0.210488 0.024791 0.16943 +0.202298 0.011107 0.190126 +0.197022 -0.002238 0.208593 +0.195264 -0.013418 0.226204 +0.195517 0.000125 0.228806 +0.193247 0.002539 0.248068 +0.185983 -0.008188 0.266481 +0.190326 0.003492 0.269578 +0.184997 0.005073 0.293798 +0.06669 0.059844 -0.245514 +0.0619 0.051035 -0.280406 +0.050798 0.045614 -0.24438 +0.039176 0.029063 -0.243934 +-0.00915 0.088447 0.337107 +-0.005564 0.08412 0.313582 +-0.021113 0.08532 0.31136 +0.355453 0.089966 -0.127158 +0.350899 0.086456 -0.117554 +0.350983 0.097265 -0.137094 +0.072647 0.03954 0.677462 +0.072418 0.04809 0.690165 +0.075207 0.036698 0.688642 +0.073504 0.031041 0.680896 +-0.178961 -0.090729 -0.609801 +-0.172446 -0.097083 -0.578269 +-0.162075 -0.115543 -0.579879 +-0.164614 -0.10158 -0.551666 +-0.164655 -0.086899 -0.525697 +-0.163951 -0.073973 -0.503455 +-0.155571 -0.076365 -0.482167 +-0.160899 -0.062998 -0.48358 +-0.157142 -0.052747 -0.467235 +-0.163173 -0.047792 -0.48491 +-0.161891 -0.034728 -0.485791 +-0.06518 -0.059918 -0.358386 +-0.054486 -0.054171 -0.322075 +-0.045494 -0.039634 -0.317862 +-0.046159 -0.051222 -0.286305 +-0.037043 -0.045221 -0.248688 +-0.048844 -0.063367 -0.25063 +-0.051682 -0.075463 -0.211308 +-0.041886 -0.070545 -0.169579 +-0.056049 -0.085832 -0.171786 +-0.060066 -0.09468 -0.131873 +0.254005 -0.038433 0.194803 +0.269666 -0.031907 0.174783 +0.268033 -0.032302 0.201549 +0.278501 -0.020645 0.208564 +-0.073666 0.038526 -0.352224 +-0.076882 0.044216 -0.344481 +-0.090345 0.049949 -0.34488 +-0.079808 0.052262 -0.33116 +-0.086114 0.059059 -0.313538 +-0.101546 0.061002 -0.315492 +-0.094518 0.067326 -0.284767 +-0.106388 0.071042 -0.250493 +-0.084369 0.069042 -0.247735 +-0.073014 0.068797 -0.205774 +-0.065091 0.019151 0.646697 +-0.068711 0.018789 0.660834 +-0.022373 0.108586 0.644445 +-0.013195 0.113326 0.640083 +0.000977 0.116141 0.634141 +0.004099 0.080202 0.281578 +0.005222 0.084113 0.313587 +-1.8e-05 0.091348 0.36624 +0.014112 0.093784 0.358834 +0.009048 0.088435 0.337116 +0.020807 0.0853 0.31137 +0.040382 0.086506 0.306746 +0.036479 0.078562 0.278794 +0.051479 0.066209 0.237014 +0.036242 0.07127 0.236575 +0.035281 0.068899 0.193525 +0.019448 0.074097 0.237143 +0.05561 -0.052972 -0.32207 +0.06282 0.040489 0.517542 +0.051013 0.054687 0.504998 +0.042858 0.041918 0.516592 +0.065208 0.06843 0.494175 +0.037299 0.066972 0.494908 +0.02119 0.059746 0.501441 +0.02674 0.044809 0.514452 +0.038298 0.035426 0.525313 +0.071308 0.027007 0.533041 +0.077062 0.031587 0.526262 +0.086859 0.03658 0.517987 +0.11124 0.022703 0.520396 +0.101501 0.044457 0.508542 +0.115815 0.045589 0.502309 +0.091657 0.06211 0.497272 +0.080638 0.080966 0.485791 +0.07637 0.070151 0.293696 +0.087457 0.048722 0.283502 +0.077093 0.059473 0.2667 +0.099334 0.030242 0.29242 +0.092998 0.032041 0.26222 +0.090592 0.023644 0.230841 +0.082636 0.042793 0.245419 +0.067806 0.058189 0.233638 +0.064536 0.055795 0.198126 +0.04641 0.092096 0.322679 +0.061535 0.083149 0.305984 +0.079014 0.078099 0.313706 +0.07703 0.084647 0.334748 +0.091849 0.065857 0.328967 +0.108059 0.051446 0.346879 +0.101082 0.039429 0.317875 +0.110719 0.01355 0.310582 +0.047532 -0.049776 -0.286305 +0.046867 -0.038187 -0.317862 +0.040231 -0.021887 -0.315602 +-0.039663 0.064887 0.794549 +-0.04651 0.07132 0.785701 +-0.038541 0.084186 0.78246 +-0.032592 0.059025 0.801776 +-0.024175 0.069175 0.800655 +-0.007675 0.0712 0.803918 +-0.015757 0.080755 0.797041 +-0.00745 0.092215 0.790085 +-0.023277 0.089332 0.787695 +-0.029903 0.097925 0.774864 +-0.13845 3.5e-05 -0.507078 +-0.133365 0.003981 -0.52854 +-0.118126 0.005381 -0.527499 +-0.127763 0.005296 -0.553194 +-0.121695 0.003929 -0.580238 +-0.13855 0.0021 -0.580595 +-0.148539 -0.004026 -0.614203 +-0.141288 -0.008128 -0.65282 +-0.156359 -0.013014 -0.650997 +0.066578 -0.099193 -0.090488 +-0.020935 0.110536 0.762662 +-0.074793 -0.027179 0.725758 +-0.078767 -0.010184 0.725264 +-0.080834 0.008385 0.724145 +-0.078249 -0.000118 0.707505 +-0.076155 0.000473 0.689176 +-0.067835 -0.042762 0.725758 +-0.059417 -0.056343 0.725758 +-0.000277 0.032619 0.537537 +0.005267 0.039781 0.519301 +-0.051985 -0.063156 0.706182 +-0.048867 -0.068495 0.725135 +-0.037495 -0.077989 0.725432 +0.024103 0.101619 0.370269 +0.0319 0.096241 0.34902 +0.056128 0.09336 0.339387 +-0.109701 0.006933 -0.501361 +-0.102506 0.004261 -0.526311 +-0.095928 0.003411 -0.551193 +-0.088769 -0.000652 -0.579106 +-0.105482 0.003165 -0.579427 +-0.115801 0 -0.614731 +-0.111182 -0.006827 -0.655254 +-0.125404 -0.005933 -0.653705 +-0.179507 -0.072641 -0.973405 +-0.179736 -0.06291 -0.959185 +-0.177626 -0.071836 -0.943035 +-0.176403 -0.051299 -0.941831 +-0.170692 -0.036558 -0.92544 +-0.181173 -0.031149 -0.943277 +-0.188787 -0.011566 -0.948821 +-0.184485 0.00236 -0.937492 +-0.197436 0.007013 -0.952195 +0.22727 0.014598 0.31173 +0.238361 0.013681 0.289874 +0.247408 0.005987 0.292578 +0.250942 0.011887 0.271593 +0.260531 0.00639 0.26041 +0.061291 0.014473 0.640484 +0.064319 0.036648 0.631863 +0.064492 0.023752 0.636896 +0.065379 0.01969 0.646737 +0.067876 0.0264 0.654196 +0.046216 -0.02182 0.600769 +-0.048478 -0.033459 -0.350673 +-0.028058 -0.038793 -0.20707 +-0.022763 -0.03191 -0.164472 +-0.030063 -0.052021 -0.167285 +-0.03312 -0.064886 -0.128361 +-0.078161 0.023262 0.691248 +-0.080978 0.011701 0.692981 +-0.076344 0.010431 0.681702 +-0.064465 -0.038248 -0.423846 +0.025319 0.111368 0.385738 +0.303574 0.01443 0.063373 +0.310434 0.028986 0.052327 +0.307717 0.015772 0.090519 +0.314679 0.044944 0.019966 +0.310561 0.042088 0.055983 +0.303049 0.051605 0.07469 +0.306827 0.04219 0.097537 +0.299525 0.043386 0.128354 +0.305899 0.030051 0.126908 +0.304373 0.01851 0.147806 +0.306915 0.016735 0.123546 +0.302576 0.003439 0.11827 +0.295169 -0.009694 0.136612 +0.294152 -0.005943 0.112623 +0.294359 0.000894 0.081565 +0.323461 0.051886 -0.012258 +0.31762 0.048353 -0.001508 +0.314347 0.060279 0.004129 +0.049496 0.065008 0.164657 +0.031795 0.070223 0.160157 +0.017269 0.071324 0.154758 +-0.146941 -0.076726 -0.464747 +-0.158396 -0.12559 -0.614064 +0.015532 0.006863 -0.037583 +0.015801 0.017143 -0.036113 +0.006115 0.011659 -0.039677 +0.008433 -0.002873 -0.039343 +-4.7e-05 0.003617 -0.040655 +-0.006218 0.011679 -0.039691 +-0.012729 0.034655 -0.030658 +-0.12596 0.005521 -0.934599 +-0.122568 -0.004845 -0.942795 +-0.123125 -0.014672 -0.948338 +-0.123788 -0.025068 -0.956534 +-0.341135 0.114455 -0.093223 +0.283327 0.067105 0.019638 +0.293591 0.074868 0.000607 +0.303696 0.077585 0.000797 +0.301369 0.085086 -0.0127 +0.302858 0.089175 -0.033159 +0.304878 0.086785 -0.051677 +-0.141076 -0.005643 -0.486465 +-0.1314 0.000337 -0.482105 +-0.127069 -0.001274 -0.460449 +-0.123182 0.007158 -0.467277 +-0.119739 0.015043 -0.450152 +-0.114817 0.010324 -0.473312 +-0.10375 0.010983 -0.473422 +-0.012278 -0.088021 0.713298 +-0.013227 -0.084451 0.698266 +-0.026636 -0.078353 0.693522 +-0.016851 -0.07652 0.685949 +-0.006472 0.093335 0.584256 +-0.008884 0.08669 0.576962 +-0.018959 0.089208 0.584357 +-0.026719 0.077269 0.582716 +-0.035274 0.075271 0.589531 +-0.043146 0.064831 0.596409 +-0.043718 0.076945 0.600527 +-0.051105 0.077523 0.612557 +-0.046133 0.086744 0.60819 +-0.043245 0.096606 0.616926 +-0.037863 0.093977 0.607513 +0.075393 0.14152 0.374137 +0.086941 0.150895 0.389525 +0.104766 0.155557 0.405971 +0.055472 0.135163 0.378477 +0.099553 -0.102008 0.418153 +0.12026 -0.100115 0.445981 +0.124401 -0.087157 0.436601 +0.132895 -0.094732 0.454064 +0.14343 -0.091666 0.451211 +0.148486 -0.096747 0.473145 +0.167144 -0.097831 0.475341 +0.082814 -0.092688 0.359965 +-0.134686 -0.019262 -0.431128 +-0.130583 -0.011949 -0.44932 +-0.137 -0.01838 -0.453731 +-0.128557 -0.005102 -0.442871 +-0.151621 -0.012736 -0.486601 +-0.143161 -0.013686 -0.46922 +-0.145265 -0.023601 -0.458198 +-0.154182 -0.028671 -0.468872 +-0.14918 -0.034021 -0.45351 +-0.150857 -0.045031 -0.452753 +-0.147695 -0.037346 -0.42967 +-0.14553 -0.040588 -0.397805 +-0.143761 -0.031171 -0.410357 +-0.139706 -0.022557 -0.408004 +-0.133982 -0.011764 -0.420962 +-0.130189 -2.3e-05 -0.434238 +-0.130731 -0.059971 -0.402214 +-0.121147 -0.064038 -0.400681 +-0.125873 -0.068746 -0.427178 +-0.108189 -0.06546 -0.398883 +-0.095316 -0.066502 -0.397735 +-0.083395 -0.008128 -0.615211 +-0.083465 -0.018747 -0.656748 +-0.09549 -0.01114 -0.656054 +-0.10805 -0.017318 -0.701964 +-0.345052 0.050757 -0.075733 +-0.340852 0.040872 -0.077866 +-0.336268 0.045862 -0.051459 +-0.332708 0.03217 -0.080015 +-0.320226 0.028715 -0.057207 +-0.315513 0.029836 -0.034959 +-0.309902 0.030384 -0.019142 +-0.323412 0.038281 -0.031359 +-0.328846 0.05104 -0.026909 +-0.322347 0.05044 -0.011265 +-0.328773 0.070373 -0.02519 +-0.324824 0.085885 -0.025156 +-0.335409 0.090632 -0.048177 +-0.340413 0.105961 -0.07299 +-0.355039 0.079322 -0.09346 +-0.354729 0.070641 -0.094084 +-0.352009 0.069524 -0.083348 +-0.351945 0.060515 -0.095032 +-0.349183 0.058431 -0.083911 +-0.349528 0.052311 -0.095002 +-0.013355 0.039852 0.594693 +-0.024763 0.044374 0.593548 +-0.054537 0.031464 0.616147 +-0.041883 0.035996 0.605149 +-0.033948 0.049288 0.593243 +-0.0564 0.065484 0.618657 +-0.047072 0.09933 0.625654 +-0.038556 0.102139 0.627143 +0.038416 -0.043774 -0.248688 +-0.148015 -0.107669 -0.979672 +-0.133355 -0.105551 -0.979672 +-0.125761 -0.088709 -0.978708 +-0.158094 -0.022202 -0.486381 +-0.172153 -0.033047 -0.260095 +-0.164795 -0.038678 -0.296103 +-0.154686 -0.042198 -0.328648 +-0.146163 -0.046645 -0.362297 +-0.175247 -0.088746 0.512573 +-0.115347 0.043952 -0.360718 +-0.123524 0.03786 -0.375678 +-0.117734 0.034503 -0.397764 +-0.126675 0.028729 -0.39779 +-0.127998 0.019027 -0.419943 +-0.128992 0.067014 -0.016683 +-0.145541 0.056575 -0.015304 +-0.154535 0.052006 -0.049873 +-0.156895 0.041146 -0.013534 +0.030724 -0.025361 -0.247007 +0.361949 0.095031 -0.136579 +0.017074 -0.003681 -0.036584 +0.01259 -0.019473 -0.035051 +0.011123 -0.036474 -0.029308 +0.348279 0.123165 -0.119123 +0.351082 0.119023 -0.111683 +0.355221 0.109178 -0.109376 +0.355448 0.123344 -0.125317 +0.354377 0.127636 -0.135881 +-0.090973 0.143064 0.374274 +-0.075512 0.14156 0.374109 +-0.050079 0.136278 0.403542 +-0.051536 0.13747 0.389704 +-0.034916 0.125166 0.390231 +-0.055592 0.135202 0.378449 +-0.062288 0.129571 0.366088 +-0.071 0.123602 0.358853 +-0.083811 0.118598 0.35427 +-0.088873 0.131855 0.363284 +-0.100924 0.141979 0.37695 +-0.109577 0.13932 0.377592 +0.082637 0.018265 0.705134 +-0.039964 0.056346 0.594023 +-0.007824 0.077675 0.574045 +-0.052266 -0.100272 -0.03746 +-0.041446 -0.087651 -0.060252 +-0.061914 -0.113622 -0.024715 +-0.073924 -0.108272 -0.035007 +-0.097503 -0.11114 -0.028186 +-0.081836 -0.105332 -0.058315 +-0.092393 -0.103408 -0.091947 +-0.068174 -0.099288 -0.091332 +-0.049442 -0.089607 -0.090816 +-0.036659 -0.075099 -0.089509 +-0.065044 -0.123263 -0.010253 +-0.009836 0.115743 0.648737 +0.327237 0.060264 -0.098716 +0.317402 0.056792 -0.083903 +0.323016 0.066262 -0.090659 +0.325027 0.078099 -0.08949 +0.328238 0.072616 -0.096421 +0.336604 0.073106 -0.106272 +0.341772 0.073187 -0.117031 +0.345532 0.067716 -0.130501 +0.344925 0.062626 -0.120462 +0.341867 0.06099 -0.11061 +0.364758 0.09914 -0.149249 +-0.109966 0.110749 0.356492 +-0.109311 0.125573 0.364086 +-0.095597 0.114976 0.35443 +-0.118885 0.132924 0.374594 +-0.12195 0.107737 0.360565 +-0.134508 0.103165 0.368117 +-0.127253 0.121475 0.370784 +-0.125349 0.136668 0.384532 +-0.056701 0.132894 0.430754 +-0.05099 0.134502 0.41673 +0.039707 -0.057384 0.588539 +0.044777 -0.051595 0.583812 +0.046226 -0.037371 0.588 +0.045744 -0.025078 0.584625 +-0.353014 0.125885 -0.134685 +-0.171172 -0.082649 -0.978226 +-0.160534 -0.093928 -0.979672 +-0.174519 -0.08255 -0.925923 +-0.108082 -0.108063 -0.798143 +-0.11853 -0.1142 -0.831174 +-0.124871 -0.122406 -0.796678 +-0.149434 -0.05543 -0.451883 +0.104513 -0.119856 -0.006538 +0.0978 -0.111172 -0.028164 +0.073252 -0.108298 -0.034989 +0.080957 -0.10535 -0.058061 +0.131654 -0.0198 0.347079 +0.125752 0.000379 0.343833 +0.135014 0.009127 0.377787 +0.126958 0.034315 0.373538 +0.142655 0.022913 0.401574 +0.148499 0.040696 0.417274 +0.149856 0.024669 0.463786 +0.145439 0.003689 0.46021 +0.124828 -0.071875 0.412865 +0.122421 -0.065139 0.370851 +0.111641 -0.069488 0.340433 +0.117589 -0.047997 0.327629 +0.111689 -0.028426 0.285855 +0.120236 -0.024706 0.314792 +0.121132 -0.008466 0.319208 +-0.172876 -0.001797 -0.921102 +-0.166697 -0.021132 -0.914594 +-0.161038 -0.003711 -0.909773 +-0.140046 -0.007952 -0.908327 +-0.150225 -0.003921 -0.904471 +-0.149673 0.015229 -0.910738 +-0.136725 0.029957 -0.921102 +0.020953 0.113196 0.423049 +0.019571 0.112543 0.404996 +0.029089 0.111175 0.441785 +0.03473 0.124593 0.422144 +0.050871 0.134464 0.416757 +0.049959 0.136238 0.403569 +0.067984 0.14531 0.39275 +0.051416 0.137432 0.389731 +0.034804 0.125134 0.390255 +0.039183 0.107472 0.457142 +0.015921 -0.058296 -0.02911 +0.020451 -0.081964 -0.028563 +-0.338809 0.059569 -0.107587 +-0.343537 0.061005 -0.11919 +-0.340383 0.071566 -0.115761 +-0.130676 0.136435 0.392085 +-0.139835 0.118599 0.388783 +-0.135497 0.131855 0.40028 +-0.140317 0.11908 0.41144 +-0.131158 0.137399 0.411849 +-0.127783 0.139086 0.421009 +0.075004 -0.077493 0.30713 +0.093165 -0.079862 0.333638 +0.102578 -0.060463 0.304957 +0.078029 0.023319 0.691211 +-0.057586 -0.034515 -0.389729 +-0.132363 0.124624 0.43241 +-0.123592 0.137482 0.430947 +-0.118679 0.131781 0.442643 +0.002832 0.090679 0.579852 +-0.141763 0.100521 0.378563 +-0.355368 0.062627 -0.114814 +-0.349396 0.078605 -0.105548 +-0.288757 0.056509 -0.018076 +-0.286935 0.056323 -0.006271 +-0.288629 0.044749 -0.008982 +-0.287855 0.065227 -0.000294 +-0.280113 0.053221 0.012952 +-0.267767 0.047503 0.045014 +-0.269139 0.037245 0.041218 +-0.254836 0.020732 0.073284 +-0.271923 0.02598 0.039028 +-0.284591 0.030382 0.008051 +-0.295296 0.031881 -0.01158 +-0.30377 0.031437 -0.037083 +-0.297344 0.041176 -0.043246 +-0.292216 0.053984 -0.034375 +-0.294587 0.072448 -0.033277 +-0.301496 0.08743 -0.031962 +-0.300253 0.083639 -0.011707 +-0.302827 0.076435 0.001586 +-0.292969 0.074014 0.001193 +-0.281513 0.06279 0.019897 +0.086165 0.000232 0.696923 +0.008936 0.071248 0.803965 +0.000872 0.107344 0.613001 +-0.351867 0.045028 -0.160324 +-0.184334 -0.090987 0.478655 +-0.180866 -0.093797 0.455579 +-0.192861 -0.084703 0.435922 +-0.175791 -0.091508 0.432581 +-0.17016 -0.086396 0.41059 +-0.101383 -0.071651 -0.425071 +-0.117583 -0.078589 -0.445556 +-0.129201 -0.076726 -0.446516 +-0.062555 -0.045563 -0.390664 +-0.070265 -0.057484 -0.394165 +-0.081778 -0.063749 -0.395622 +-0.018647 0.109092 0.657665 +-0.01261 0.109655 0.669207 +0.0612 -0.113649 -0.024697 +0.064575 -0.12329 -0.010235 +0.047695 -0.114606 -0.018917 +0.030815 -0.113587 -0.011691 +0.030815 -0.100812 -0.024706 +0.045019 -0.011214 0.573057 +-0.137942 -0.072298 -0.448699 +-0.144563 -0.066044 -0.450807 +-0.025506 0.112792 0.696714 +-0.017276 0.122161 0.725792 +-0.021595 0.119803 0.709461 +-0.142078 -0.058424 -0.430818 +-0.056606 -0.128518 0.003003 +-0.08336 -0.12659 0.001076 +-0.097366 -0.127499 0.015291 +-0.103809 -0.119817 -0.006566 +0.039376 0.10617 0.663733 +0.028632 0.106834 0.66425 +0.033957 0.107383 0.64946 +0.016359 0.043991 0.515559 +0.044878 0.100847 0.681489 +0.045223 0.103968 0.670256 +0.052914 0.104231 0.65826 +0.044621 0.10681 0.652899 +0.076213 0.010488 0.681667 +0.071646 0.023293 0.67267 +0.362776 0.097661 -0.123083 +0.359574 0.088524 -0.112757 +0.312943 0.044951 -0.07454 +0.308352 0.056671 -0.069257 +0.306424 0.073658 -0.061704 +0.314519 0.093868 -0.068159 +0.324215 0.107993 -0.078378 +0.321862 0.097355 -0.08002 +0.329791 0.096935 -0.09254 +0.327202 0.088566 -0.090317 +0.321923 0.034752 -0.080185 +0.024801 0.001349 -0.202991 +0.031492 0.011056 -0.244461 +0.028399 -0.006603 -0.245536 +0.033205 -0.015061 -0.282364 +-0.028547 0.103573 0.625856 +-0.032026 0.105467 0.63514 +-0.081198 0.054688 0.090468 +0.058176 0.097731 0.647868 +0.060032 0.093784 0.669735 +0.063961 0.082176 0.666919 +0.06654 0.070166 0.676537 +0.065813 0.070004 0.659307 +0.066309 0.056881 0.649331 +0.062987 0.07486 0.643841 +0.059015 0.089006 0.6384 +-0.03838 0.035432 0.525308 +-0.071382 0.027007 0.533041 +-0.009674 0.106762 0.611441 +-0.019029 0.103085 0.608624 +0.039222 0.019263 -0.27972 +0.362573 0.106105 -0.142558 +0.357631 0.10375 -0.131248 +0.353224 0.101317 -0.116185 +0.296483 0.061471 0.059544 +0.291315 0.057051 0.098559 +0.276438 0.058275 0.126668 +0.289726 0.052774 0.12868 +0.290853 0.046799 0.150876 +0.310791 0.072738 0.004206 +0.303663 0.06729 0.025 +0.284757 0.064199 0.058181 +-0.139835 0.103654 0.435928 +-0.132604 0.110644 0.44557 +-0.130916 0.094977 0.458971 +-0.124074 0.116031 0.45141 +-0.111929 0.11973 0.45508 +-0.100119 0.120694 0.456286 +-0.086715 0.123262 0.455026 +-0.360632 0.093308 -0.135502 +-0.349583 0.084734 -0.116476 +-0.358255 0.086801 -0.111672 +-0.131878 0.000476 -0.92062 +-0.12911 0.019234 -0.927128 +0.198889 -0.026778 0.227738 +0.192185 -0.023207 0.243713 +0.188352 -0.031844 0.264659 +0.185173 -0.020391 0.264729 +0.176599 -0.017861 0.287774 +-0.013024 -0.088241 0.72662 +-0.0247 -0.08469 0.720404 +-0.033508 -0.078855 0.707401 +-0.0416 -0.069884 0.69463 +-0.027312 0.044822 0.514443 +0.018951 0.105409 0.618947 +0.057516 0.093339 0.67944 +0.278125 0.048336 0.17281 +0.263154 0.054575 0.169187 +0.262592 0.058539 0.14524 +0.247085 0.054634 0.163441 +0.233057 0.048502 0.156696 +0.282102 0.010812 0.218659 +0.288074 0.024504 0.19717 +0.297528 0.02164 0.172297 +0.290382 0.036594 0.173918 +0.065553 0.045494 0.639548 +0.068224 0.03931 0.65204 +0.069891 0.033191 0.660931 +-0.083804 0.074907 -0.165948 +-0.062697 0.064238 -0.16485 +-0.045673 0.048194 -0.163564 +-0.03268 0.028352 -0.161928 +-0.127843 0.065599 -0.253296 +-0.118913 0.072745 -0.210048 +-0.130514 0.070531 -0.168479 +-0.107684 0.076855 -0.167466 +-0.096156 0.078963 -0.126465 +-0.083653 -0.133012 0.031918 +-0.10907 -0.123137 0.031923 +-0.118079 -0.11643 0.049941 +-0.159349 0.041309 -0.255529 +-0.155791 0.0504 -0.21274 +-0.146525 0.05484 -0.254917 +-0.162423 0.043235 -0.170205 +-0.148711 0.058947 -0.169549 +-0.1406 0.067376 -0.127538 +-0.133779 0.058198 -0.289618 +-0.11935 0.057318 -0.31754 +-0.168086 0.02405 -0.256279 +-0.173604 0.00463 -0.257509 +-0.17464 0.014528 -0.214836 +-0.179462 0.002306 -0.17347 +-0.172535 0.024622 -0.171551 +-0.168602 0.03471 -0.128899 +-0.06391 0.131373 0.441954 +0.095027 -0.048825 0.262066 +0.103824 -0.036545 0.269255 +0.104098 -0.015764 0.243629 +-0.0991 0.017944 -0.450663 +0.262731 0.05789 0.122908 +0.265882 0.059393 0.093437 +0.249381 0.05205 0.116538 +0.241193 0.043654 0.110039 +0.235215 0.046569 0.132123 +0.222589 0.036845 0.14968 +0.146018 0.020217 -0.352553 +0.143756 0.007836 -0.390439 +0.14439 0.017838 -0.368618 +0.139017 0.027615 -0.35283 +0.12709 0.038621 -0.361158 +0.131022 0.036125 -0.351555 +0.355028 0.04387 -0.128252 +0.353348 0.049278 -0.116616 +0.349146 0.056385 -0.106308 +0.35089 0.054056 -0.096199 +0.350544 0.060176 -0.085108 +0.346414 0.052502 -0.07693 +0.337629 0.047607 -0.052655 +0.342214 0.042617 -0.079063 +0.33407 0.033915 -0.081211 +0.341084 0.037101 -0.099069 +0.344392 0.039642 -0.121249 +-0.141336 -0.051267 -0.399824 +0.298792 0.005093 0.16812 +0.290602 -0.00859 0.188816 +0.293927 -0.009816 0.162202 +0.283459 -0.021475 0.155186 +0.27585 -0.003524 0.237141 +0.283366 -0.005735 0.214482 +-0.351907 0.099595 -0.115108 +-0.34249 0.092899 -0.112093 +-0.345768 0.096557 -0.122526 +-0.349666 0.095542 -0.136017 +-0.356314 0.102028 -0.130171 +-0.312351 0.036325 -0.004647 +-0.305342 0.029027 -0.008073 +0.191614 0.011738 0.323365 +0.203097 0.014525 0.328262 +0.202498 0.011696 0.352268 +0.214997 0.013491 0.332878 +0.226236 0.008727 0.336797 +0.351124 0.080663 -0.106541 +0.356378 0.081055 -0.094596 +0.353367 0.071267 -0.084535 +0.356095 0.072365 -0.095294 +0.35739 0.073907 -0.111625 +0.337536 0.088233 -0.102831 +0.343807 0.094623 -0.113171 +-0.02455 0.00862 -0.161409 +-0.02085 -0.011834 -0.162222 +-0.012323 0.119858 0.745726 +0.154903 -0.108342 -0.84821 +0.157955 -0.103488 -0.89052 +0.162337 -0.097395 -0.84577 +0.160152 -0.106884 -0.925923 +0.163093 -0.093571 -0.914112 +0.169384 -0.0825 -0.925923 +0.163187 -0.078345 -0.907845 +0.158667 -0.064508 -0.89748 +0.162831 -0.075833 -0.88042 +0.167599 -0.073578 -0.841037 +0.167119 -0.084966 -0.841937 +0.172359 -0.091098 -0.788958 +0.179314 -0.084468 -0.735822 +0.174054 -0.098491 -0.737941 +0.172653 -0.108092 -0.692848 +0.166863 -0.110119 -0.739513 +0.156392 -0.119247 -0.743145 +0.157734 -0.113673 -0.792971 +0.146709 -0.116839 -0.829945 +0.139238 -0.123252 -0.795671 +0.135221 -0.119519 -0.851526 +0.126098 -0.118567 -0.85216 +0.128453 -0.117836 -0.90182 +0.120161 -0.116392 -0.93725 +0.129375 -0.12077 -0.938938 +0.129172 -0.124665 -0.957739 +0.140594 -0.122729 -0.948098 +0.14944 -0.122801 -0.960149 +0.154592 -0.115999 -0.943517 +0.166877 -0.105317 -0.947133 +0.065331 0.074522 0.689774 +0.069246 0.061208 0.691007 +0.071205 0.059059 0.707851 +0.35675 0.064282 -0.116065 +0.353311 0.062239 -0.096241 +0.355051 0.062522 -0.127898 +0.361289 0.071473 -0.127738 +0.35018 0.077184 -0.123737 +0.20152 -0.056893 0.293143 +0.203629 -0.049299 0.268706 +0.194583 -0.041604 0.266002 +0.204535 -0.04329 0.248854 +0.205009 -0.037103 0.231537 +0.140183 -0.036419 -0.8667 +0.134024 -0.037348 -0.836944 +0.143458 -0.038358 -0.836309 +0.122109 -0.041502 -0.8244 +0.125832 -0.031999 -0.795256 +0.117287 -0.025912 -0.751044 +0.128883 -0.023115 -0.748801 +0.134097 -0.01391 -0.699996 +0.139656 -0.024143 -0.746943 +0.152452 -0.029692 -0.74438 +0.146039 -0.034038 -0.792764 +0.153367 -0.04399 -0.821778 +0.163786 -0.047167 -0.789462 +0.160572 -0.053136 -0.836354 +0.165379 -0.062623 -0.837463 +0.159322 -0.056693 -0.872748 +0.135524 -0.036067 -0.889768 +0.128481 -0.032994 -0.905194 +0.127864 -0.044391 -0.893624 +0.12024 -0.059215 -0.895553 +0.120564 -0.052687 -0.872655 +0.109133 -0.059543 -0.840626 +0.116183 -0.050045 -0.839212 +0.109217 -0.043154 -0.796386 +-0.019678 0.02905 -0.029532 +-0.015918 0.01718 -0.036137 +-0.027782 0.104204 0.685235 +-0.018875 0.106096 0.678748 +-0.044037 0.103878 0.670194 +-0.038982 0.102331 0.681333 +-0.128709 0.010087 -0.432263 +-0.12282 0.016837 -0.434716 +-0.115201 0.022097 -0.434582 +-0.111437 0.030062 -0.419783 +-0.105629 0.024339 -0.434984 +-0.093734 0.023146 -0.434976 +-0.181137 -0.019254 -0.175074 +-0.162184 0.043679 -0.087926 +-0.175245 -0.015389 -0.25902 +-0.177955 -0.02705 -0.217951 +-0.177569 -0.03962 -0.176278 +0.035154 -0.122722 0.002771 +-0.052378 0.104259 0.65844 +-0.043756 0.106801 0.652945 +-0.03289 0.107281 0.649359 +0.093246 0.049027 0.102835 +0.102315 0.040455 0.113433 +0.081108 0.054715 0.090481 +0.083986 0.055223 0.114438 +0.074667 0.060907 0.130241 +0.086741 0.049708 0.133324 +0.090962 0.038731 0.152601 +0.097319 0.040624 0.13197 +0.110442 0.029584 0.124656 +0.11543 0.014141 0.134661 +-0.146583 0.097629 0.39423 +-0.147066 0.097629 0.408934 +-0.144415 0.099798 0.424119 +-0.075479 0.127276 0.449668 +-0.137907 0.071356 0.459767 +-0.145378 0.063402 0.446028 +0.002666 0.099924 0.594166 +-0.208136 -0.06588 0.463823 +-0.213936 -0.055034 0.444634 +-0.206495 -0.070744 0.440044 +-0.214464 -0.036885 0.448758 +-0.221066 -0.043897 0.422246 +-0.2282 -0.032688 0.395547 +-0.227826 -0.044995 0.392802 +-0.236249 -0.044725 0.363377 +-0.221761 -0.057822 0.388709 +-0.212382 -0.068579 0.384699 +-0.20756 -0.072435 0.413562 +-0.187 -0.085581 0.498671 +-0.197969 -0.077029 0.482777 +-0.206481 -0.058685 0.487615 +-0.203208 -0.050881 0.507905 +-0.20808 -0.037902 0.491988 +-0.201669 -0.018703 0.494444 +-0.210263 -0.026948 0.47232 +-0.208053 -0.017687 0.451214 +0.16996 -0.027323 0.310191 +0.170357 -0.015106 0.311774 +0.166048 -0.012376 0.331959 +0.17441 -0.003851 0.314699 +0.181753 0.005421 0.318704 +-0.1098 0.054429 -0.334104 +-0.14562 0.080033 0.435592 +0.060488 -0.068938 0.271743 +0.078781 -0.059829 0.264962 +0.092697 -0.040887 0.223772 +0.093348 -0.035422 0.193415 +0.098751 -0.019246 0.193125 +0.100697 0.002736 0.176856 +0.101353 -0.006403 0.205791 +0.095902 0.006341 0.221401 +0.038794 -0.006233 -0.313056 +0.041719 0.010433 -0.311486 +0.066881 -0.020749 -0.416186 +0.029509 -0.087462 0.126235 +0.022217 -0.076703 0.144776 +0.032796 -0.071917 0.162044 +0.011425 -0.071799 0.162332 +0.002712 -0.068146 0.176001 +0.070781 0.112051 0.464061 +0.054872 0.10699 0.465579 +0.072708 0.096866 0.474979 +0.050534 0.119282 0.451526 +0.093919 0.092528 0.477149 +0.114648 0.086984 0.475703 +0.105395 0.105146 0.46749 +0.111811 0.119691 0.455108 +0.1 0.120655 0.456313 +0.097831 0.134636 0.443876 +0.086595 0.123223 0.455053 +0.07536 0.127236 0.449695 +0.080663 0.140493 0.434606 +0.06379 0.131334 0.441982 +0.056581 0.132855 0.430781 +0.179852 0.005927 0.340541 +0.048671 0.026118 -0.310259 +0.059727 0.041338 -0.310499 +0.027022 0.037246 0.523155 +-0.080366 0.042976 -0.359844 +-0.072915 0.03363 -0.369455 +-0.070227 0.013891 -0.415307 +-0.069241 0.022839 -0.390911 +-0.076875 0.030483 -0.393719 +-0.085119 0.027745 -0.418972 +-0.086507 0.036825 -0.396545 +-0.096641 0.042094 -0.387468 +-0.087837 0.044531 -0.374711 +-0.092277 0.048103 -0.362594 +-0.103086 0.047746 -0.360488 +-0.104066 0.049317 -0.349409 +0.275432 0.062709 0.0551 +0.269288 0.055547 0.049494 +0.25155 0.043835 0.081332 +0.312765 0.085286 -0.010822 +-0.006119 0.113561 0.764306 +0.000738 0.103834 0.778899 +0.056372 -0.128537 0.003258 +0.044313 -0.130652 0.018679 +0.05757 -0.134243 0.033141 +0.083339 -0.126623 0.001097 +-0.155492 -0.028329 0.356208 +-0.160749 -0.032802 0.345004 +-0.161044 -0.021111 0.346178 +-0.166167 -0.012336 0.331933 +-0.164113 -0.010625 0.349268 +-0.170574 -0.001596 0.353845 +-0.16219 -0.006465 0.362443 +-0.164373 -0.002133 0.375173 +-0.154414 -0.01074 0.370753 +-0.163482 -0.053438 0.350923 +-0.160466 -0.043558 0.34741 +-0.165475 -0.036245 0.329176 +-0.18622 -0.077512 0.51589 +-0.193917 -0.061157 0.520285 +-0.195394 -0.04444 0.523948 +-0.176073 0.003874 0.375161 +-0.167484 0.004652 0.390015 +-0.172233 0.009446 0.407131 +0.075831 -0.027037 0.725898 +0.068873 -0.04262 0.725898 +0.061801 -0.048767 0.745837 +-0.27555 0.062748 0.055073 +-0.269406 0.055586 0.049467 +0.103573 0.004244 -0.526283 +0.096997 0.003408 -0.551167 +0.088488 0.000855 -0.525091 +0.106551 0.00317 -0.5794 +0.089838 -0.000647 -0.579079 +0.084256 -0.008196 -0.615646 +0.075129 -0.008559 -0.578207 +0.064432 -0.020335 -0.577323 +0.068614 -0.011633 -0.548927 +0.065155 -0.016647 -0.522231 +0.074816 -0.006275 -0.523723 +0.081531 -0.003771 -0.502802 +0.078044 -0.009858 -0.481754 +0.119195 0.005364 -0.527473 +0.134434 0.003972 -0.528513 +0.128831 0.005293 -0.553166 +0.139619 0.002105 -0.580567 +0.122764 0.003934 -0.580212 +0.116662 -6.7e-05 -0.615168 +0.349613 0.067725 -0.140039 +0.356089 0.091025 -0.092513 +0.353287 0.086051 -0.082628 +0.068413 0.142812 0.41341 +0.206081 -0.033746 0.219483 +0.078094 -0.064043 0.179812 +0.082596 -0.049383 0.190776 +0.067026 -0.061547 0.206463 +0.097546 -0.038496 0.172632 +0.043016 -0.069454 0.25707 +0.056181 -0.066368 0.240689 +0.051476 -0.065838 0.21055 +0.035925 -0.066514 0.205479 +0.04313 -0.069236 0.184395 +0.050298 -0.074615 0.161752 +0.055473 -0.086776 0.145536 +0.066807 -0.080225 0.156563 +0.080701 -0.090169 0.145461 +0.078786 -0.075462 0.161242 +0.090862 -0.065936 0.159389 +0.106573 -0.068518 0.141109 +0.102218 -0.050143 0.15512 +0.111741 -0.029199 0.149358 +0.112008 -0.070891 -0.906881 +0.11057 -0.07076 -0.880396 +0.102978 -0.080562 -0.84563 +0.103886 -0.06994 -0.844271 +0.096618 -0.061591 -0.797183 +0.0882 -0.060651 -0.752721 +0.091109 -0.04912 -0.75201 +0.08615 -0.033149 -0.705572 +0.09666 -0.040182 -0.752628 +0.106557 -0.032314 -0.752003 +0.14956 -0.001313 -0.529318 +0.144855 -0.124841 -0.745393 +0.149778 -0.127288 -0.700318 +0.177309 -0.101695 -0.647878 +0.167115 -0.118095 -0.650531 +0.159465 -0.125585 -0.614036 +0.154711 -0.128161 -0.654369 +0.139654 -0.133976 -0.656646 +0.128731 -0.138171 -0.616951 +0.126036 -0.135417 -0.659802 +0.111818 -0.132281 -0.660855 +0.124126 -0.130883 -0.706812 +0.122834 -0.126243 -0.750746 +0.132821 -0.126997 -0.748729 +0.058829 -0.030454 -0.520769 +0.06317 -0.022602 -0.499983 +0.064227 -0.028785 -0.47925 +0.069343 -0.018317 -0.480426 +0.076388 -0.016697 -0.464359 +0.077672 -0.020169 -0.448528 +0.353537 0.0987 -0.091601 +0.346735 0.096378 -0.073406 +0.341774 0.107706 -0.074186 +0.336771 0.092377 -0.049375 +0.326186 0.08763 -0.026353 +0.330134 0.072118 -0.026387 +0.330207 0.052784 -0.028106 +0.324773 0.040026 -0.032555 +0.124965 -0.02669 0.131936 +0.116345 -0.007176 0.142504 +0.106752 0.009716 0.15506 +0.095406 0.023264 0.165686 +0.09808 -0.130509 -0.616704 +0.099527 -0.124822 -0.662285 +0.087129 -0.110283 -0.661788 +0.102193 -0.117634 -0.710458 +0.104738 -0.111843 -0.754197 +0.113192 -0.120451 -0.752962 +0.120129 -0.121547 -0.798324 +0.116907 -0.114396 -0.832544 +0.108758 -0.085321 0.124834 +0.121176 -0.06699 0.122271 +0.13404 -0.064346 0.105192 +0.130046 -0.046107 0.118861 +0.339622 0.118009 -0.112037 +0.334976 0.116204 -0.088929 +0.342497 0.1162 -0.094419 +0.073351 0.008257 0.671288 +0.10554 -0.092843 -0.849852 +0.095638 -0.08453 -0.799001 +0.090968 -0.087454 -0.754633 +0.088128 -0.07297 -0.753181 +0.078976 -0.058512 -0.707107 +-0.075424 0.009123 -0.435344 +0.316876 0.03158 -0.036155 +0.086249 0.027853 0.199251 +0.08641 0.035636 0.172472 +0.075024 0.044607 0.178318 +0.063516 0.056965 0.168818 +-0.106758 -0.031887 -0.749253 +-0.096861 -0.039755 -0.749878 +-0.110329 -0.043009 -0.794095 +-0.118654 -0.050742 -0.838319 +-0.111159 -0.06052 -0.840215 +0.162173 -0.024273 -0.695147 +0.163692 -0.037602 -0.74226 +0.172937 -0.048276 -0.73928 +0.180361 -0.047124 -0.689526 +0.178379 -0.05903 -0.736715 +0.180512 -0.072165 -0.735769 +0.174036 -0.067408 -0.788005 +-0.01866 0.041565 -0.022306 +0.160106 -0.005357 -0.554471 +-0.33064 0.11508 -0.074983 +-0.311601 0.083779 -0.009789 +-0.314357 0.096724 -0.02922 +-0.32229 0.110319 -0.053009 +0.184241 -0.076208 -0.687737 +-0.083623 0.018781 -0.434761 +0.183078 -0.041209 0.287631 +0.077348 -0.10364 0.128523 +0.094965 -0.097446 0.127334 +0.10717 -0.101699 0.110056 +0.05856 0.088006 0.689861 +0.051062 0.096194 0.687617 +0.348306 0.101164 -0.105268 +0.347888 0.106007 -0.114837 +0.355884 0.113699 -0.120724 +0.009858 0.094499 0.586661 +0.108833 -0.085105 -0.914594 +0.10845 -0.099236 -0.923753 +0.109957 -0.097255 -0.892333 +0.111542 -0.104529 -0.851505 +0.097953 -0.099412 -0.47666 +0.111357 -0.101905 -0.477447 +0.119589 -0.112055 -0.499903 +0.059946 0.08543 0.699312 +-0.170476 -0.015066 0.311747 +-0.174529 -0.003813 0.314673 +-0.185116 0.005111 0.293771 +-0.181873 0.00546 0.318677 +-0.191732 0.011776 0.323338 +-0.179971 0.005966 0.340514 +-0.179228 0.004671 0.361014 +-0.19039 0.007652 0.367844 +0.072639 -0.099523 -0.614258 +0.0775 -0.093825 -0.66106 +0.071185 -0.075868 -0.660395 +0.084233 -0.088981 -0.709949 +0.096406 -0.099648 -0.754415 +0.107078 -0.108411 -0.800435 +0.26745 -0.00378 0.255424 +0.267637 -0.016481 0.253101 +0.260933 -0.028107 0.26999 +0.261114 -0.004045 0.273784 +0.25364 -0.003772 0.29392 +0.256818 -0.015226 0.29385 +0.252192 -0.024433 0.317098 +0.256449 -0.026587 0.292379 +0.252545 -0.037425 0.289561 +-0.148709 -0.127292 -0.700346 +-0.138585 -0.133981 -0.656673 +-0.153642 -0.128166 -0.654397 +-0.125176 -0.13535 -0.659367 +-0.166046 -0.1181 -0.650557 +-0.176239 -0.101699 -0.647905 +-0.171584 -0.108097 -0.692875 +-0.172985 -0.098496 -0.737968 +-0.165794 -0.110124 -0.73954 +-0.155324 -0.119252 -0.743172 +-0.143786 -0.124846 -0.74542 +-0.131961 -0.12693 -0.748294 +-0.12239 -0.126032 -0.749386 +-0.123473 -0.130744 -0.705913 +-0.111165 -0.132142 -0.659956 +0.160924 -0.021151 0.346205 +0.163994 -0.010665 0.349294 +0.16207 -0.006504 0.36247 +0.170454 -0.001635 0.353872 +0.179108 0.004632 0.361041 +0.175953 0.003835 0.375187 +0.19027 0.007613 0.367871 +0.198506 0.009604 0.379501 +0.060439 -0.06298 -0.613617 +0.068768 -0.059883 -0.659006 +0.070615 -0.043766 -0.658497 +0.353396 0.053089 -0.127413 +0.241181 -0.04512 0.336466 +0.245234 -0.033866 0.339391 +0.23613 -0.044764 0.363403 +0.245631 -0.02165 0.340974 +0.242331 -0.009756 0.341047 +0.2369 -0.020241 0.36773 +0.225764 -0.019446 0.394917 +0.228079 -0.032727 0.395573 +0.220946 -0.043937 0.422273 +0.227706 -0.045034 0.392829 +0.221642 -0.057862 0.388736 +0.207441 -0.072475 0.413589 +0.212263 -0.068618 0.384727 +0.201974 -0.072895 0.375283 +0.219409 -0.064794 0.355389 +0.223978 -0.060709 0.3278 +0.233838 -0.054393 0.332461 +0.244235 -0.046525 0.311355 +-0.246344 -0.046865 0.248314 +-0.246726 -0.046279 0.264919 +-0.23634 -0.052093 0.281321 +-0.245836 -0.04601 0.28581 +-0.244354 -0.046486 0.311327 +-0.252664 -0.037387 0.289534 +-0.256568 -0.026548 0.292352 +-0.235924 -0.050107 0.243671 +-0.224655 -0.049757 0.239529 +-0.225627 -0.052112 0.255722 +-0.2142 -0.053688 0.272322 +-0.22566 -0.054685 0.276767 +-0.224999 -0.058884 0.302177 +0.172787 -0.090507 -0.947133 +0.172491 -0.071786 -0.943035 +0.174601 -0.062859 -0.959185 +0.171268 -0.051249 -0.941831 +0.176039 -0.031099 -0.943277 +0.183272 -0.024297 -0.958462 +-0.182568 -0.083839 -0.644914 +-0.183173 -0.076213 -0.687764 +-0.179451 -0.07217 -0.735796 +-0.178245 -0.084473 -0.73585 +-0.172324 -0.091111 -0.788981 +-0.165041 -0.06852 0.372711 +-0.165792 -0.076618 0.390739 +-0.182487 -0.078599 0.387542 +-0.202092 -0.072856 0.375256 +-0.192214 -0.07212 0.365583 +-0.198164 -0.066806 0.340792 +-0.181422 -0.0669 0.357366 +-0.169688 -0.061476 0.354624 +-0.175191 -0.05662 0.334131 +-0.190446 0.003531 0.269551 +-0.197274 0.012155 0.273275 +-0.20633 0.017395 0.277484 +-0.216569 0.019145 0.281759 +-0.204031 0.016668 0.302641 +-0.203217 0.014564 0.328234 +-0.170079 -0.027283 0.310164 +-0.176718 -0.017823 0.287748 +-0.185292 -0.020352 0.264702 +-0.186102 -0.008149 0.266454 +0.363363 0.07473 -0.150603 +-0.107552 0.038514 -0.397668 +-0.177325 -0.059035 -0.736742 +-0.173896 -0.068383 -0.788751 +0.360636 0.075305 -0.160142 +-0.189058 0.010659 0.403907 +-0.198626 0.009643 0.379475 +-0.202387 0.007865 0.396803 +-0.212762 0.002657 0.389941 +-0.206169 -0.001395 0.41268 +-0.213588 -0.012012 0.413809 +-0.205328 -0.008308 0.434608 +-0.195397 0.000483 0.451738 +-0.18388 0.006758 0.473118 +-0.187941 -0.003168 0.494719 +0.062872 0.064146 0.147546 +0.159288 0.119897 -0.961836 +0.119568 0.115217 -0.964728 +0.343388 0.055728 -0.117661 +0.347914 0.053336 -0.139892 +0.341641 0.052257 -0.129927 +0.336395 0.047978 -0.120921 +0.321588 0.03046 -0.058404 +-0.03278 -0.126524 0.032896 +0.123153 -0.030496 -0.92279 +-0.219528 -0.064755 0.355362 +0.17326 -0.039216 0.310118 +0.165356 -0.036284 0.329203 +0.160346 -0.043597 0.347437 +0.160629 -0.032842 0.345031 +0.155372 -0.028368 0.356235 +0.150337 -0.035624 0.365792 +0.150665 -0.022157 0.367637 +0.154294 -0.010779 0.370781 +0.164253 -0.002173 0.375198 +0.035841 0.029887 0.534583 +0.079019 0.149079 0.400081 +0.349098 0.041786 -0.144112 +0.356004 0.042531 -0.154123 +-0.099083 -0.124611 -0.660924 +-0.113162 -0.120096 -0.750675 +-0.104916 -0.111415 -0.751447 +-0.102164 -0.117278 -0.708171 +-0.086684 -0.110071 -0.660427 +0.084782 0.150209 0.412004 +0.089822 0.149412 0.422 +0.097534 0.153991 0.414529 +0.212494 -0.063496 0.322903 +0.108072 -0.017674 -0.704252 +0.085284 0.006734 0.70766 +0.013054 -0.070632 0.772858 +-0.359252 0.073551 -0.158881 +0.35586 0.115447 -0.131675 +0.347078 0.121777 -0.131765 +0.11799 -0.014622 -0.948338 +0.118626 -0.022811 -0.937492 +0.077762 0.003004 0.692549 +0.149496 0.118045 -0.971237 +0.353272 0.046773 -0.161503 +-0.169398 -0.075532 -0.842498 +-0.166691 -0.077802 -0.88187 +-0.169032 -0.085956 -0.842673 +0.17116 -0.100231 -0.965211 +0.078337 0.029774 0.697426 +0.078949 0.032083 0.70963 +0.078269 0.042027 0.723896 +0.081211 0.025526 0.724481 +0.081294 0.020966 0.742447 +0.081482 0.008499 0.724226 +0.079611 -0.010056 0.725374 +0.078703 -2e-05 0.707557 +-0.212613 -0.063458 0.322876 +-0.200714 -0.062423 0.31826 +-0.201639 -0.056854 0.293115 +-0.189474 -0.05766 0.314342 +-0.18008 -0.049671 0.311534 +0.009779 -0.013603 0.811873 +-0.348227 0.066029 -0.138773 +-0.188471 -0.031806 0.264632 +-0.192304 -0.023168 0.243687 +-0.199008 -0.02674 0.227711 +0.195543 0.005391 -0.970995 +-0.001704 -0.064566 0.188111 +0.123955 0.115992 0.451438 +0.130797 0.094938 0.458999 +-0.244484 0.024749 0.252048 +-0.229914 0.023071 0.265249 +-0.22803 0.018149 0.286204 +0.181303 -0.06694 0.357393 +0.198046 -0.066846 0.34082 +0.192095 -0.072159 0.365611 +0.200594 -0.062462 0.318287 +0.182369 -0.078638 0.387568 +0.170041 -0.086435 0.410617 +0.165672 -0.076658 0.390767 +0.150865 -0.068843 0.390208 +0.164921 -0.06856 0.372738 +0.169569 -0.061516 0.35465 +0.163363 -0.053478 0.350949 +0.175072 -0.056658 0.334159 +0.179961 -0.04971 0.311561 +0.189355 -0.0577 0.314369 +0.135647 0.09831 -0.976539 +0.141277 0.10969 -0.974611 +0.126106 0.115455 -0.971237 +0.109014 0.105489 -0.967742 +0.224509 0.002044 0.362953 +0.23563 0.000737 0.339605 +0.245713 -0.001086 0.317242 +-0.233957 -0.054354 0.332434 +-0.224097 -0.06067 0.327773 +0.144342 -0.12144 -0.974756 +0.154109 -0.119068 -0.973887 +0.154443 -0.109248 -0.978226 +0.163545 -0.107182 -0.975334 +0.170636 -0.091241 -0.975334 +0.119337 0.007039 0.530859 +0.178109 -0.018702 0.526682 +0.164945 -0.013349 0.526722 +0.162346 0.000925 0.509971 +0.151118 -0.002354 0.520824 +0.134781 0.020876 0.511031 +0.132609 0.005697 0.523097 +-0.306945 0.042229 0.09751 +-0.303168 0.051644 0.074662 +-0.291433 0.05709 0.098532 +-0.296602 0.06151 0.059517 +-0.284876 0.064238 0.058154 +-0.303535 0.067031 0.025178 +-0.310169 0.071885 0.004791 +-0.313972 0.059722 0.00451 +-0.316999 0.047501 -0.000922 +-0.304492 0.018549 0.147779 +-0.306017 0.030089 0.12688 +-0.299644 0.043425 0.128327 +-0.290972 0.046839 0.150848 +-0.289846 0.052813 0.128654 +-0.288193 0.024543 0.197143 +-0.290501 0.036632 0.173892 +-0.297647 0.021679 0.172269 +-0.278244 0.048375 0.172783 +-0.275074 0.025804 0.220254 +-0.260374 0.027071 0.23891 +-0.262818 0.037546 0.219144 +-0.263681 0.048027 0.194925 +0.212654 0.002962 0.390454 +0.220072 -0.007654 0.391584 +0.213481 -0.011706 0.414323 +0.119387 -0.116328 0.050137 +0.110117 -0.123127 0.032005 +0.084289 -0.133045 0.031941 +0.097998 -0.127538 0.015318 +0.09455 0.144916 0.431457 +-0.055893 0.052902 0.614078 +-0.050027 0.052827 0.603861 +0.188938 0.01062 0.403933 +0.202273 0.007998 0.397072 +0.206061 -0.00109 0.413194 +-0.085415 0.006678 0.707696 +-0.086296 0.000176 0.696959 +-0.183198 -0.04117 0.287604 +-0.173379 -0.039177 0.310091 +0.205214 -0.008175 0.434878 +0.195277 0.000444 0.451765 +0.207934 -0.017726 0.451241 +0.209911 -0.026981 0.471619 +0.214345 -0.036924 0.448785 +0.213816 -0.055074 0.444661 +0.025324 -0.058631 0.7826 +0.01253 -0.061062 0.78584 +0.011407 -0.050197 0.794688 +0.206377 -0.070783 0.44007 +0.192742 -0.084742 0.435948 +0.180747 -0.093837 0.455606 +0.175672 -0.091547 0.432608 +0.157013 -0.090462 0.43041 +0.355823 0.129013 -0.15251 +0.167365 0.004612 0.390041 +0.154985 0.004237 0.403646 +0.172113 0.009406 0.407158 +0.174958 0.013221 0.428688 +-0.202617 0.011735 0.352242 +-0.359857 0.065634 -0.137145 +-0.358469 0.06381 -0.148531 +0.339127 0.109041 -0.111258 +-0.355676 0.07573 -0.135227 +-0.361979 0.072975 -0.149342 +0.100875 0.083121 -0.970754 +0.103018 0.096223 -0.970272 +0.115575 0.098697 -0.974129 +0.124166 0.086266 -0.97678 +0.131507 0.068883 -0.977745 +0.111941 0.077113 -0.975093 +0.208017 -0.065919 0.46385 +-0.078302 0.029706 0.697437 +0.058903 -0.072872 -0.54404 +0.057457 -0.067087 -0.57373 +0.063343 -0.085673 -0.573707 +0.055864 -0.050678 -0.574756 +0.073186 -0.104666 -0.575196 +0.086227 -0.121112 -0.577144 +0.078229 -0.107652 -0.546591 +0.084535 -0.107544 -0.520092 +0.072452 -0.093879 -0.518952 +0.062968 -0.078092 -0.517891 +0.057687 -0.062526 -0.518367 +0.058442 -0.052134 -0.497397 +0.056341 -0.045931 -0.519415 +0.055964 -0.039868 -0.546115 +0.058107 -0.034764 -0.575973 +-0.245354 -0.033827 0.339365 +-0.2413 -0.045081 0.336438 +0.341625 0.09724 -0.100812 +0.129441 -0.119864 -0.525052 +0.139869 -0.126175 -0.553229 +0.143561 -0.113256 -0.525748 +0.134261 -0.135387 -0.581649 +0.149939 -0.128617 -0.58101 +0.163143 -0.115538 -0.579851 +0.173515 -0.097078 -0.578243 +0.165684 -0.101575 -0.551639 +0.165725 -0.086894 -0.52567 +0.156171 -0.101734 -0.525316 +0.113951 -0.122268 -0.523613 +0.099083 -0.11872 -0.522086 +0.107925 -0.129584 -0.550956 +0.101374 -0.131867 -0.579516 +0.117808 -0.136392 -0.580816 +0.141849 0.001235 0.469012 +0.15723 0.009821 0.447299 +0.17877 0.011883 0.450648 +0.18376 0.006719 0.473145 +-0.06438 0.023247 0.636874 +-0.063112 0.03358 0.628538 +-0.205128 -0.037064 0.23151 +-0.204654 -0.043252 0.248828 +-0.214134 -0.045326 0.236279 +-0.194701 -0.041565 0.265974 +-0.203749 -0.049259 0.268678 +-0.221296 -0.04775 0.22812 +-0.216327 -0.041007 0.217945 +-0.211949 -0.032031 0.203072 +-0.2062 -0.033706 0.219456 +0.176307 -0.053861 -0.970754 +0.174371 -0.07259 -0.973405 +0.166037 -0.0826 -0.978226 +0.1554 -0.093877 -0.979672 +0.156574 -0.072029 -0.977504 +0.159115 -0.051027 -0.974611 +0.173162 -0.027178 -0.973164 +0.170514 -0.042869 -0.972683 +0.180207 -0.035449 -0.968585 +0.186921 -0.015395 -0.969067 +-0.074434 -0.029724 -0.656313 +-0.086129 -0.032795 -0.703283 +-0.091303 -0.048692 -0.74926 +-0.088386 -0.060223 -0.749972 +-0.097276 -0.061724 -0.795373 +-0.19108 -0.014562 0.512553 +0.091317 -0.106014 -0.49759 +0.172606 0.006961 0.492825 +0.153428 0.00626 0.488919 +0.145335 0.048119 0.463165 +0.141073 0.041311 0.48148 +0.128945 0.062822 0.483894 +0.131145 0.042934 0.49661 +0.190494 -0.014589 0.511124 +0.201084 -0.01873 0.493017 +0.187589 -0.0032 0.494019 +0.181753 -0.041215 -0.578804 +0.177671 -0.025978 -0.580003 +0.182778 -0.057623 -0.577679 +0.185387 -0.052529 -0.608923 +0.18629 -0.064883 -0.643856 +0.184542 -0.048934 -0.6447 +0.179789 -0.033866 -0.646825 +0.170847 -0.021688 -0.649466 +0.176552 -0.022224 -0.612247 +0.169293 -0.012787 -0.580641 +0.155768 -0.003262 -0.581065 +0.172059 -0.068744 -0.526163 +0.178575 -0.063213 -0.551391 +0.180073 -0.076866 -0.577502 +-0.360641 0.10223 -0.157401 +-0.362883 0.096396 -0.150932 +0.183637 -0.083834 -0.644886 +0.18003 -0.090725 -0.609774 +0.149608 -0.004021 -0.614175 +0.33473 0.104244 -0.099942 +0.064528 -0.030949 -0.614326 +0.074879 -0.029935 -0.657674 +0.061988 -0.042839 -0.478018 +0.065538 -0.034823 -0.462206 +0.095934 -0.011351 -0.657415 +0.08391 -0.018958 -0.65811 +-0.362464 0.103486 -0.146037 +0.362126 0.105398 -0.155069 +-0.096585 -0.099221 -0.751664 +-0.091146 -0.087026 -0.751883 +-0.084203 -0.088625 -0.70766 +-0.07074 -0.075656 -0.659033 +-0.077055 -0.093614 -0.659699 +-0.071779 -0.099456 -0.613822 +-0.088308 -0.072542 -0.75043 +0.008712 0.092262 0.790131 +0.025323 0.069293 0.800772 +0.017018 0.080849 0.797134 +0.024425 0.08945 0.787811 +0.039578 0.084327 0.7826 +0.0407 0.065028 0.794688 +0.181339 -0.009278 -0.973165 +0.170214 -0.002734 -0.973887 +0.15423 0.009263 -0.972683 +0.010846 -0.039475 0.801916 +0.010059 -0.027181 0.807399 +0.019509 -0.009221 0.810943 +-0.189676 -0.02732 0.526138 +0.157428 -0.013009 -0.65097 +0.142149 -0.008196 -0.653256 +0.126057 -0.006072 -0.654604 +0.111627 -0.007039 -0.656616 +-0.168201 -0.079358 -0.908568 +0.153661 -0.045747 0.361072 +0.021957 -0.035043 0.800794 +-0.245751 -0.02161 0.340947 +-0.252311 -0.024394 0.317072 +-0.256938 -0.015188 0.293823 +0.315719 0.098469 -0.030418 +0.323652 0.112064 -0.054205 +0.069311 -0.035047 -0.448088 +-0.225878 -0.01958 0.394647 +-0.23702 -0.020202 0.367703 +-0.242451 -0.009717 0.34102 +-0.06082 0.050615 0.625212 +-0.238481 0.01372 0.289848 +-0.22739 0.014637 0.311703 +-0.226356 0.008767 0.336769 +-0.215117 0.013529 0.332851 +-0.23575 0.000777 0.339577 +-0.245832 -0.001047 0.317215 +-0.247528 0.006025 0.292552 +-0.253759 -0.003734 0.293895 +-0.261233 -0.004006 0.273759 +-0.22018 -0.00796 0.391069 +-0.224623 0.001911 0.362682 +0.010481 0.086772 0.577044 +0.143699 -0.044555 0.377004 +-0.251061 0.011926 0.271567 +-0.070169 -0.043554 -0.657136 +-0.068323 -0.059671 -0.657643 +-0.078948 -0.058156 -0.70482 +0.138631 -0.069856 0.409434 +0.140603 -0.056375 0.391026 +0.144295 0.068666 0.390136 +0.131039 0.072281 0.373987 +0.119545 0.061258 0.365376 +0.148392 0.060229 0.409178 +0.14791 0.06047 0.427255 +0.061194 -0.101996 0.128584 +0.068223 -0.115659 0.112662 +0.055213 -0.122019 0.097083 +0.078208 -0.122298 0.098267 +0.090109 -0.125325 0.083517 +0.099539 -0.114531 0.09685 +0.119083 -0.100139 0.094385 +0.130689 -0.095004 0.080497 +0.133536 -0.081117 0.092084 +0.14342 -0.057279 0.089751 +0.079745 -0.131592 0.067127 +0.103922 -0.122391 0.067669 +0.055395 -0.131208 0.065896 +0.069381 -0.135449 0.049532 +0.124957 -0.10646 0.067114 +0.036158 -0.114652 0.094315 +0.042764 -0.124662 0.081103 +0.033238 -0.101133 0.108597 +0.045264 -0.094838 0.127286 +0.206129 -0.058717 0.486915 +0.197849 -0.077068 0.482803 +0.184214 -0.091027 0.478681 +0.140409 -0.081699 0.429785 +0.127904 0.080476 0.470882 +0.137787 0.071317 0.459794 +0.145259 0.063363 0.446056 +-0.26065 0.006429 0.260385 +0.119403 -0.006611 -0.956534 +-0.25518 0.018034 0.254722 +0.01688 -0.065543 0.196797 +0.03084 -0.067719 0.230546 +0.110934 -0.109238 -0.937974 +0.116602 -0.118716 -0.954292 +0.038681 -0.056487 0.774999 +0.036323 -0.044192 0.787834 +0.046088 -0.027895 0.790202 +0.034078 -0.03147 0.797181 +0.031518 -0.017656 0.804089 +0.142881 -0.10762 -0.979672 +0.073673 0.057384 0.724556 +0.074239 0.055939 0.737636 +0.133232 0.051032 -0.977021 +0.332002 0.116825 -0.076179 +0.059894 0.044442 0.78584 +0.051924 0.056737 0.789578 +0.051946 0.0401 0.794742 +0.047547 0.071461 0.78584 +0.047029 0.020604 0.800903 +0.039958 0.001019 0.804143 +0.050712 0.005504 0.797234 +0.054282 -0.009597 0.790202 +0.060455 0.00956 0.787834 +0.069883 0.012992 0.774999 +0.06472 0.029003 0.7826 +0.065112 0.048174 0.775876 +0.070107 0.051334 0.763795 +0.061801 0.065488 0.769652 +0.05546 0.081657 0.7638 +0.052205 0.076479 0.775876 +0.057184 0.084832 0.751847 +0.029857 -0.004128 0.808754 +-0.210827 -0.020354 0.184299 +-0.213135 -0.008264 0.161048 +0.125472 -0.004017 -0.963041 +0.077122 0.035307 0.756062 +0.07301 0.053779 0.751837 +0.066428 0.071108 0.745741 +0.074148 -0.002592 0.762786 +0.077291 0.016137 0.760543 +-0.240427 -0.009453 0.11697 +-0.226254 -0.009526 0.137936 +0.07628 -0.016315 0.745837 +-0.108726 0.077671 -0.08699 +-0.147896 0.060634 -0.08722 +-0.130339 0.072406 -0.087051 +-0.120429 0.074217 -0.051083 +-0.245918 0.004612 0.0982 +0.118984 -0.121476 -0.966898 +0.130897 -0.125393 -0.971237 +0.135425 -0.118096 -0.97678 +0.13368 0.025991 -0.973647 +0.136093 -0.000389 -0.968586 +0.207495 -0.037927 0.490559 +0.051363 -0.053485 0.762786 +0.071824 0.052174 -0.311375 +-0.109144 0.070674 -0.018124 +-0.068987 -0.135429 0.049518 +-0.079129 -0.131615 0.067054 +-0.102803 -0.122492 0.067471 +-0.057114 -0.134223 0.032887 +-0.044072 -0.130639 0.018188 +0.058097 -0.024607 0.778987 +0.068536 -0.021033 0.764406 +0.061016 -0.037903 0.764406 diff --git a/src/cython/example/ex_clustering.py b/src/cython/example/ex_clustering.py index bee82414..44b3d610 100644 --- a/src/cython/example/ex_clustering.py +++ b/src/cython/example/ex_clustering.py @@ -6,7 +6,7 @@ import sys sys.path.append("../sktda/") from clustering import * -X = np.loadtxt("human") +X = np.loadtxt("../../../data/points/human") print("Mapper computation with point cloud") mapper = MapperComplex(inp="point cloud", -- cgit v1.2.3 From f247f597baa4bf6ca2cd7371de73bb14e130d74e Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Wed, 19 Jun 2019 12:29:04 -0400 Subject: removed clustering files --- data/points/human | 4706 ----------------------------------- src/cython/example/ex_clustering.py | 64 - src/cython/sktda/clustering.py | 269 -- 3 files changed, 5039 deletions(-) delete mode 100644 data/points/human delete mode 100644 src/cython/example/ex_clustering.py delete mode 100644 src/cython/sktda/clustering.py (limited to 'src') diff --git a/data/points/human b/data/points/human deleted file mode 100644 index feaa0dd1..00000000 --- a/data/points/human +++ /dev/null @@ -1,4706 +0,0 @@ -0.144529 -0.014328 0.381773 --0.15378 -0.045707 0.361045 --0.153917 -0.057275 0.371308 --0.172725 0.007 0.492798 --0.119664 0.061296 0.365349 -0.128166 -0.068572 0.432762 -0.089198 0.014048 -0.447694 -0.075569 0.021145 -0.417112 - 0.098403 0.03188 -0.416332 --0.135518 -0.031058 -0.793657 --0.155207 -0.043962 -0.867084 --0.155561 -0.039537 -0.782486 -0.047747 0.043681 0.604458 -0.01706 0.058681 0.583267 -0.153119 -0.065476 -0.465974 -0.134237 -0.06509 -0.429525 -0.138009 -0.083138 -0.463111 -0.061936 0.021354 -0.365004 --0.327282 0.105737 -0.088733 --0.333168 0.111481 -0.099092 -0.02915 0.038215 0.809093 -0.000855 0.039818 0.813241 -0.188052 0.020848 -0.942916 -0.200327 0.058375 -0.947736 -0.170213 0.052306 -0.931828 --0.051119 -0.110776 0.111316 --0.024531 -0.111686 0.078082 --0.021008 -0.089762 0.10657 -0.158999 0.015347 -0.916402 -0.127084 0.053037 -0.930089 -0.110535 0.0176 -0.450151 -0.12031 0.025078 -0.419837 -0.125486 0.007107 -0.444987 -0.087186 -0.068787 -0.428022 -0.068096 -0.048487 -0.423994 -0.074575 -0.071291 -0.460151 --0.129151 -0.031845 0.547791 --0.092828 -0.001702 0.544926 --0.146897 -0.021057 0.536567 --0.040908 -0.080471 0.144988 --0.070348 -0.092228 0.142231 -0.135184 -0.05792 -0.366185 -0.150502 -0.030301 -0.366514 -0.154969 -0.053619 -0.297939 --0.046419 0.016608 0.551426 --0.140395 -0.04129 0.102368 --0.15361 -0.023507 0.066896 --0.143445 -0.07334 0.071094 --0.150698 -0.083179 0.012934 --0.164552 -0.040183 0.016637 --0.172922 -0.056311 -0.054118 -0.142078 0.114992 0.400106 -0.136581 0.121451 0.422073 -0.118363 -0.052599 -0.969187 -0.110299 -0.090045 -0.973743 -0.106227 -0.067834 -0.951593 --0.075572 -0.055159 0.227875 --0.048656 -0.08199 0.316154 --0.088566 -0.065864 0.297905 --0.175443 0.052256 -0.931828 --0.142719 0.087605 -0.938697 --0.186188 0.080344 -0.942939 --0.281944 -0.02183 0.181925 --0.282953 -0.017408 0.129994 --0.25389 -0.034093 0.168434 --0.092047 -0.041549 0.557254 --0.085291 -0.062035 0.558917 --0.302715 0.009876 0.086968 --0.282908 -0.00097 0.077127 --0.117228 0.023298 0.342619 --0.177404 -0.012747 -0.051392 --0.272247 -0.020141 0.233158 --0.254475 -0.039985 0.216094 --0.017864 0.070352 0.178973 --0.003695 0.067376 0.163481 -0.030308 0.031315 0.603728 -0.02661 0.115199 0.744608 -0.033244 0.11394 0.710348 -0.012279 0.123046 0.708533 --0.05487 -0.030769 0.660855 --0.04444 -0.039595 0.603866 -0.027038 -0.082087 0.737178 -0.023403 -0.083864 0.708068 -0.044858 -0.07186 0.710449 --0.141696 0.005473 0.082272 --0.322537 0.044383 -0.093982 -0.137584 0.087655 -0.938697 -0.110404 0.089056 -0.947375 -0.201593 0.059307 -0.975093 -0.19232 0.094332 -0.975768 -0.209811 0.077354 -0.962319 -0.008617 -0.099163 0.047708 -0.005395 -0.079622 0.076357 -0.106371 0.041851 -0.959546 -0.116565 0.024136 -0.939902 -0.10789 0.05867 -0.947567 -0.117802 0.064238 0.011831 -0.099654 0.053985 0.060307 -0.12682 0.046382 0.064253 -0.14171 0.005468 0.082276 -0.162647 0.005991 0.019937 -0.146819 0.043902 0.018713 --0.086735 -0.081196 0.550877 --0.142522 -0.064292 0.541934 --0.101551 -0.092019 0.536874 --0.147816 -0.090727 -0.053942 -0.089669 -0.075237 -0.296805 -0.110958 -0.094262 -0.219465 -0.067718 -0.086145 -0.213647 -0.035525 -0.10073 0.457763 -0.000152 -0.097146 0.473706 -0.000252 -0.080555 0.318839 -0.16693 0.014297 -0.292232 -0.165909 0.033768 -0.213443 -0.177974 -0.006912 -0.216479 -0.122756 0.049674 -0.332788 -0.11496 0.064838 -0.287502 -0.148695 0.045817 -0.290571 -0.055746 -0.030683 0.660961 -0.033546 -0.066936 0.676952 -0.017439 -0.069226 0.625575 -0.039048 0.038642 -0.121677 -0.035134 0.012056 -0.055409 -0.054628 0.051615 -0.046746 --0.219382 0.038108 0.176265 --0.218586 0.03396 0.225697 --0.200001 0.012939 0.213512 -0.134152 -0.008037 -0.466849 --0.111507 0.041802 -0.959546 --0.105326 0.074882 -0.964246 --0.113025 0.05862 -0.947567 --0.054482 -0.034932 0.574847 --0.090128 -0.021415 0.553574 --0.116313 -0.048454 0.13681 --0.09038 -0.053234 0.172229 --0.105186 -0.014928 0.166671 --0.126942 0.046414 0.064231 --0.116088 0.034063 0.10285 -0.078883 0.038902 -0.375952 -0.08771 0.046925 -0.353531 -0.166615 -0.02867 -0.506862 -0.15046 -0.019487 -0.47215 -0.156742 -0.040331 -0.46813 -0.049615 0.064657 0.606895 --0.093081 0.017267 0.197077 --0.107564 -0.001066 0.279921 --0.087365 0.043211 0.25934 --0.229315 0.033998 0.125778 --0.248089 0.054546 0.138866 -0.106254 -0.066109 -0.3667 -0.125371 -0.075616 -0.30084 --0.035641 -0.100747 0.457937 --0.095086 -0.103368 0.460305 -0.018598 0.108161 0.632002 -0.008009 0.110019 0.630723 --0.145172 -0.126207 -0.962029 --0.147995 -0.115103 -0.901861 --0.165635 -0.112794 -0.963402 -0.181052 0.080394 -0.942939 --0.037409 0.016634 -0.022456 --0.054935 0.051562 -0.047076 --0.051074 0.0426 0.012895 -0.024454 -0.043608 -0.124411 -0.030596 -0.06583 -0.057455 -0.026133 -0.020196 -0.061267 -0.004091 -0.07642 0.016114 --0.143275 0.11508 -0.950869 --0.10902 0.06955 0.488685 --0.119427 0.028724 0.512181 --0.077684 0.050975 0.507111 -0.03316 -0.09173 0.54611 --0.126953 0.020019 -0.968705 --0.153991 -0.027525 -0.970453 --0.162328 0.037423 -0.975575 -0.019284 0.112865 0.690206 --0.017888 0.004075 0.813135 --0.036857 0.015351 0.806263 --0.019684 -0.022438 0.806286 --0.118067 0.060321 -0.975334 --0.11084 -0.089559 0.388201 --0.136103 -0.098275 0.477748 --0.068592 0.049409 0.668449 --0.354522 0.111955 -0.119529 -0.133555 0.0107 -0.91375 -0.152919 -0.021289 -0.903386 --0.047277 0.088388 0.477505 -0.028943 0.100045 0.614915 --0.178297 -0.027228 -0.973164 --0.195107 0.015542 -0.975093 --0.009711 -0.053847 -0.021292 --0.020989 -0.095217 -0.015669 --0.206728 0.059258 -0.975093 --0.160538 0.080674 -0.978019 --0.028046 0.017736 0.594311 --0.099769 0.054021 0.060283 -0.292938 0.024667 0.005698 -0.281872 0.042386 0.010061 -0.293815 0.043479 -0.024205 -0.253755 -0.042591 0.24146 -0.236374 -0.050375 0.26034 -0.2334 -0.049604 0.232122 --0.041908 -0.013527 0.798038 --0.169026 -0.017468 -0.554275 --0.180719 -0.036864 -0.610601 --0.177641 -0.046876 -0.552508 -0.142302 -0.064331 0.542099 -0.091978 -0.041556 0.557259 -0.085357 -0.061971 0.558866 -0.163846 -0.07001 -0.134881 -0.147918 -0.090765 -0.053915 -0.128677 -0.096438 -0.134035 -0.033866 0.08705 0.595856 --0.061852 0.08285 0.67803 --0.064405 0.0745 0.705228 --0.057315 0.087943 0.688282 -0.026825 0.058063 0.011471 -0.00959 0.046503 -0.022489 -7.5e-05 0.067016 0.012148 -0.037289 0.016594 -0.022429 -0.027248 0.032898 -0.019611 -0.051064 0.042566 0.012814 -0.172917 -0.056346 -0.054093 -0.177533 -0.01278 -0.051369 -0.165297 -0.040174 0.016676 --0.049827 -0.001275 0.628239 --0.046082 -0.007732 0.588234 -0.027282 0.017653 0.594293 -0.218467 0.03392 0.225725 -0.218303 0.022488 0.260748 -0.246836 0.034226 0.236039 -0.180761 -0.032705 -0.133588 --0.061259 -0.067857 -0.496632 --0.054909 -0.056275 -0.544951 --0.05886 -0.036687 -0.498712 --0.096128 0.07647 0.350771 --0.125425 -0.075572 -0.300885 --0.089749 -0.075321 -0.29683 --0.111609 -0.094226 -0.21961 --0.059916 -0.025146 -0.5475 --0.070274 -0.011925 -0.501404 --0.014861 0.085999 0.475193 -0.011944 0.087545 0.471872 --0.013265 0.101124 0.440331 --0.111865 -0.105085 -0.952919 --0.111362 -0.067884 -0.951593 --0.108817 -0.085906 -0.928478 -0.129068 -0.032432 0.547796 -0.092747 -0.004496 0.54493 --0.154642 0.003294 -0.352704 --0.142743 0.028986 -0.337671 --0.069935 -0.024902 -0.463284 --0.315481 0.074492 -0.075996 --0.325874 0.058528 -0.097514 --0.332121 0.079166 -0.097184 -0.065756 0.113544 0.355893 -0.099174 0.128438 0.362935 -0.091993 0.102855 0.353431 -0.150875 -0.083168 0.013016 --0.108399 -0.048104 0.306063 --0.047271 0.099741 0.356288 --0.066219 0.088165 0.321493 --0.035225 -0.092767 0.543027 --0.009859 0.104972 0.60128 --0.043422 -0.067033 0.234382 --0.197455 0.094282 -0.975768 --0.104462 -0.111554 -0.498426 --0.078262 -0.094503 -0.497102 --0.092157 -0.088636 -0.463068 --0.146801 -0.077454 -0.220163 -0.086804 -0.081061 0.550911 -0.134521 0.11988 0.378405 -0.118328 0.120392 0.364735 -0.115393 0.14537 0.390518 --0.054843 -0.015197 0.563407 --0.05504 0.025 0.792306 --0.070388 0.032156 0.769815 --0.063682 -0.006236 0.777538 --0.000626 0.114562 0.672841 -0.253835 -0.01734 0.119652 -0.223827 -0.021655 0.161215 -0.231867 0.004314 0.117834 --0.114756 0.089415 -0.973165 --0.127269 0.107695 -0.974322 -0.096751 0.074035 -0.05055 -0.078066 0.059374 0.009685 -0.000726 0.11622 0.656618 -0.165173 0.032563 -0.050176 -0.000689 -0.080625 0.101576 --0.005268 -0.079602 0.076344 -0.183917 -0.053008 0.530657 -0.17545 -0.080203 0.523558 -0.141167 -0.093629 0.517571 --0.051253 0.096483 0.711906 --0.022715 -0.04717 0.792329 --0.047913 -0.041404 0.777538 -0.052291 0.096624 0.712047 --0.026424 -0.020287 -0.061717 --0.035397 0.011988 -0.055788 --0.024475 -0.015906 -0.03417 --0.062564 0.085644 0.653961 -0.006432 -0.067984 0.669362 --0.091524 -0.120719 -0.548864 --0.352035 0.051345 -0.126219 --0.078178 0.050942 0.154885 --0.07227 0.047244 0.203935 --0.048281 0.062448 0.195881 -0.155341 0.052205 -0.127805 -0.177243 0.012794 -0.130554 -0.072937 0.07156 -0.124618 -0.118217 0.146923 0.421907 -0.020198 0.076819 0.079276 -0.139978 0.062527 -0.211926 --0.089177 -0.111241 0.111846 --0.09518 -0.081905 0.14393 --0.074495 -0.071296 -0.460179 --0.042869 0.071572 0.14139 -0.00073 -0.05162 -0.010499 --0.004172 -0.07642 0.016012 --0.131034 -0.082145 0.442399 --0.100523 -0.027685 0.22648 --0.015731 -0.07176 0.615875 -0.119466 0.075689 -0.126799 --0.132503 -0.106466 -0.501087 --0.122962 -0.130064 -0.552684 --0.074738 0.044114 0.706666 --0.066077 0.071903 0.732644 --0.062916 0.068521 0.757684 --0.048496 0.098136 0.740433 --0.033166 0.000252 -0.280952 --0.048184 0.017041 -0.328096 --0.048173 0.0348 -0.27966 --0.025707 -0.068824 0.768329 --0.166577 0.1103 -0.974177 --0.124674 -0.051241 -0.914051 --0.112361 -0.082599 -0.886027 --0.08053 -0.007338 -0.458902 --0.068099 -0.00941 -0.416012 --0.214946 0.077303 -0.962319 --0.205462 0.058324 -0.947736 -0.138907 0.065218 -0.050517 -0.199882 0.012901 0.213538 -0.200105 0.01313 0.252288 -0.055229 0.054786 -0.204024 --0.024533 0.091239 0.330841 --0.172277 -0.081982 -0.551109 --0.167673 -0.057628 -0.50493 --0.058987 -0.06452 -0.289445 --0.068156 -0.086411 -0.213966 -0.254357 -0.040025 0.216122 --0.067674 0.042176 -0.330429 --0.076952 0.060866 -0.28229 -0.013977 0.113422 0.640177 --6.9e-05 0.086527 0.3376 -0.019179 0.079744 0.281051 --0.015722 0.079249 0.274606 -0.115072 -0.070048 -0.42498 -0.077551 -0.065157 -0.362486 -0.033381 0.053515 0.505504 -0.054667 0.033265 0.526511 -0.077572 0.050943 0.507134 -0.086553 0.046191 0.265442 -0.059227 0.073277 0.277063 -0.088572 0.057052 0.304907 -0.057158 -0.046385 -0.353944 --0.056639 0.061106 0.779722 --0.043816 0.051377 0.797042 --0.031526 0.076752 0.792306 -0.171482 -0.044588 -0.21885 --0.143382 0.00154 -0.554016 --0.164175 -0.011574 -0.613396 -0.045344 -0.080279 -0.128946 --0.043257 0.090499 0.76982 --0.078577 0.001976 0.744204 --0.07502 -0.017208 0.703979 --0.068733 -0.033112 0.745947 --0.009464 0.029546 0.541617 --0.050437 -0.062276 0.744451 --0.078293 0.038525 0.740162 -0.024463 0.091212 0.330858 --0.112377 0.005125 -0.552117 --0.131688 -0.001292 -0.614531 --0.183022 -0.043595 -0.957618 --0.196997 -0.004429 -0.960993 -0.240756 0.018538 0.268716 -0.095305 0.074055 -0.207363 -0.056798 0.020951 0.627455 -0.062692 0.009616 0.652266 -0.049787 -0.001444 0.628412 -0.146665 -0.077493 -0.22013 --0.037163 -0.033757 -0.284242 --0.038849 -0.058215 -0.209167 --0.065469 -0.047111 -0.461306 -0.047178 0.099707 0.356312 -0.308841 0.028975 0.094589 -0.301247 0.005445 0.09387 -0.30888 0.029103 0.019447 -0.017844 0.071064 0.186113 --0.155734 -0.088774 -0.50245 --0.152652 -0.115873 -0.552632 -0.011229 0.02562 -0.035193 --5.3e-05 0.023896 -0.036713 --0.088766 0.057083 0.305007 --0.120964 0.004341 -0.951472 --0.115539 0.089006 -0.947375 -0.292644 0.072606 -0.014141 --0.123809 0.004777 -0.499938 -0.000277 -0.080877 0.691549 --0.034692 0.087566 0.596768 -0.103311 0.149984 0.391528 -0.078795 0.134336 0.365284 -0.135882 -0.098348 0.47797 -0.094456 -0.103522 0.46077 --0.134073 -0.008009 -0.466876 --0.142742 -0.028419 -0.439643 --0.106323 -0.066074 -0.366736 --0.080573 -0.002643 -0.550185 --0.099477 -0.002733 -0.614967 --0.329349 0.034688 -0.054928 --0.342692 0.069348 -0.059398 --0.347817 0.042787 -0.102173 -0.000763 0.044977 0.590746 -1.7e-05 0.025278 0.592935 --0.029687 0.031244 0.60365 --0.053206 0.088406 0.622436 -0.059948 -0.063445 -0.289438 --0.127417 -0.115851 -0.974611 --0.121738 -0.118767 -0.954292 --0.151626 -0.006646 -0.507398 --0.154614 -0.053562 -0.298047 --0.141938 -0.093481 0.516984 --0.165744 -0.028673 -0.506889 --0.133184 0.029585 -0.373943 --0.146871 0.043935 0.01869 --0.161897 0.006001 0.019931 -0.039669 -0.057032 -0.208807 --0.028046 0.038112 0.808937 --4.6e-05 -0.020914 -0.03562 --0.071917 0.145224 0.383713 --0.078914 0.134376 0.365257 --0.103429 0.150023 0.391501 --0.016033 0.0586 0.583186 --0.150342 -0.019487 -0.472176 --0.058188 -0.098083 -0.059091 --0.030917 -0.065889 -0.057781 --0.0438 -0.102119 -0.030128 --0.099293 0.128477 0.362907 --0.092113 0.102894 0.353403 --0.118447 0.120432 0.364707 --0.0642 0.142063 0.402239 -0.044609 -0.039585 0.604051 --0.168519 -0.098024 -0.977937 --0.179056 -0.08227 -0.963161 --0.122882 -0.110201 -0.896682 --0.156663 -0.040336 -0.468156 -0.107748 -0.105176 -0.056081 -0.1285 -0.039664 0.355589 --0.158194 -0.021338 -0.903386 --0.164797 0.015297 -0.916402 --0.176744 -0.016583 -0.931828 -0.033356 0.125884 0.406133 -0.010034 0.099979 0.392826 -0.01712 0.102705 0.44066 -0.024443 -0.015939 -0.034147 -0.009626 -0.053851 -0.021289 -0.042447 0.121595 0.37503 --0.347781 0.05466 -0.105101 --0.115513 0.145409 0.39049 -0.111316 -0.089478 0.388386 --0.128517 -0.00972 0.345704 -0.080848 0.011758 0.692945 --0.067863 -0.031032 -0.431189 --0.118337 0.146961 0.421881 -0.045869 -0.069312 0.571091 --0.126084 0.090514 0.363964 --0.356352 0.06532 -0.102173 --0.281744 0.042127 0.010238 --0.292665 0.041989 -0.023182 --0.2916 0.071244 -0.013206 --0.016178 0.060644 0.806268 --0.34744 0.048022 -0.151125 --0.114149 0.105438 -0.967742 --0.162121 -0.095588 0.45288 --0.114846 -0.090481 -0.461001 --0.087198 -0.068891 -0.428048 -0.042983 -0.102146 -0.030109 -0.023895 -0.066464 -0.037126 -0.045982 -0.007651 0.589677 --0.13249 -0.021343 -0.963114 --0.12112 -0.031941 -0.947976 --0.123497 -0.052648 -0.969187 --0.173337 -0.062024 -0.975816 --0.212735 0.034612 -0.964126 --0.13793 -0.083142 -0.463138 --0.032206 0.113799 0.710208 --0.018056 0.112723 0.690066 --0.153041 -0.06548 -0.466002 --0.084204 -0.118301 -0.016723 --0.107774 -0.105145 -0.056224 --0.357731 0.118728 -0.141076 --0.012173 0.100156 0.388899 --0.042563 0.12163 0.375005 --0.033484 0.125919 0.406108 -0.00895 0.029531 0.541612 -0.034096 0.103917 0.67485 -0.316842 0.076237 -0.077194 -0.323899 0.046128 -0.095179 -0.025035 -0.017397 -0.204731 -0.022904 -0.002982 -0.119922 --0.017702 0.108056 0.631897 --0.090769 0.050144 0.086125 --0.071768 0.049796 0.05205 -0.058343 0.098285 0.662255 -0.062548 0.085885 0.653903 -0.047412 0.103597 0.643011 --0.356011 0.072244 -0.110379 --0.094694 0.016232 0.53498 -0.031138 0.0202 -0.202722 -0.023257 0.108698 0.644556 -0.298563 0.049907 0.106045 -0.309127 0.055705 0.032301 --0.127245 0.125928 0.44046 --0.107979 0.133071 0.444265 -0.002793 0.120311 0.643807 --0.139345 0.01065 -0.91375 -0.191354 -0.010656 0.245307 -0.000195 -0.089014 0.717793 --0.022347 -0.083906 0.707302 --0.033776 0.053541 0.505486 -0.0076 0.108722 0.620553 -0.009016 0.106372 0.608066 -0.05338 0.099889 0.672242 -0.247319 0.050332 0.189783 -0.277263 0.037855 0.19703 -0.066826 0.030362 0.643759 -0.068832 0.018953 0.660837 --0.05508 0.054469 -0.2044 --0.095081 0.07404 -0.207566 --0.121146 -0.113881 0.007467 --0.007983 -0.067736 0.665032 --0.139878 0.062566 -0.211961 --0.148273 0.045873 -0.29067 --0.165897 0.033789 -0.213473 --0.041772 0.121934 0.43798 -0.109205 -0.048064 0.306801 --0.094235 0.003354 -0.498127 -0.277358 0.054616 0.14883 -0.247971 0.054507 0.138894 -0.15507 0.003229 -0.352581 -0.143146 0.028923 -0.337551 -0.349178 0.044531 -0.10337 --0.134886 -0.057869 -0.366286 -0.301726 0.003593 0.142683 -0.299445 0.033456 0.150221 -0.291556 0.007949 0.193786 --0.36146 0.095938 -0.122006 --0.292738 0.024324 0.005932 -0.215633 0.016904 0.307366 -0.333478 0.080898 -0.098369 --0.354136 0.088244 -0.126082 --0.024054 -0.018703 -0.205089 --0.030282 0.019018 -0.203081 --0.023349 -0.003622 -0.121357 --0.009637 0.122749 0.70621 -0.162028 -0.090011 -0.885108 -0.165776 -0.102924 -0.790685 -0.143895 -0.115061 -0.901856 -0.051724 0.088579 0.622321 -0.061724 0.063033 0.633357 -0.06924 0.049621 0.668534 -0.35772 0.067034 -0.103388 -0.214633 -0.048801 0.252022 -0.135277 -0.031016 -0.794097 -0.151255 -0.043483 -0.866807 -0.130025 -0.042014 -0.864241 --0.027974 0.006868 -0.029505 --0.024899 -0.044248 -0.125845 -0.142675 -0.028427 -0.439606 -0.146143 -0.047721 -0.430244 --0.344331 0.038782 -0.132348 --0.033038 0.103786 0.67472 --0.125521 0.007147 -0.445015 --0.110456 0.017639 -0.450178 --0.177355 0.012827 -0.130577 --0.177958 -0.006894 -0.216507 -0.020331 -0.09522 -0.015547 --0.047532 0.103303 0.641395 --0.028376 0.100234 0.614667 --0.134303 -0.065085 -0.42956 --0.354421 0.12716 -0.151317 --0.348795 0.121213 -0.142322 -0.092382 0.046406 0.119475 -0.116004 0.034072 0.102858 -0.090667 0.050145 0.086144 --0.13464 0.11992 0.378378 --0.145812 0.082925 0.387467 --0.142197 0.115031 0.400078 --0.073683 0.142378 0.425002 --0.11906 0.10213 0.463337 --0.137325 0.059317 0.473022 --0.216529 -0.057972 0.41774 --0.196262 -0.081978 0.459561 --0.211776 -0.046564 0.468374 -0.179611 -0.005892 0.290372 -0.072121 0.033447 0.670515 --0.114173 0.06488 -0.287538 -0.086455 -0.101061 0.513489 --0.148602 0.042639 0.439711 --0.122607 0.04972 -0.332836 --0.13875 0.086783 0.448968 --0.166519 0.014354 -0.292347 -0.089044 -0.065899 0.298075 -0.100498 -0.027722 0.226627 -0.048519 -0.014071 -0.352485 -0.038536 -0.03231 -0.284242 -0.000144 -0.076816 0.142161 --0.067966 -0.048732 -0.424016 -0.046995 0.088353 0.477529 -0.088208 0.107273 0.467101 -0.061381 0.118752 0.456477 -0.193747 0.011959 0.298081 -0.034539 0.001698 -0.280952 -0.048886 0.018356 -0.328229 --0.000763 0.058573 0.503145 --0.064384 0.02103 -0.364348 --0.079524 0.038923 -0.375843 --0.088095 0.046844 -0.35346 -0.279857 0.058875 0.019882 -0.256561 0.051897 0.087347 -0.293923 0.07157 0.020631 --0.025409 0.115069 0.74448 -0.001008 0.120894 0.74601 -0.024428 -0.118227 0.018318 -0.045667 -0.121959 -0.007542 -0.252255 0.033409 0.076695 --0.158045 -0.017034 0.358421 --0.15663 -0.037984 0.35548 --0.148633 0.077503 0.419924 --0.180872 -0.032671 -0.13361 --0.17169 -0.094525 0.495191 --0.196785 -0.069355 0.503 --0.136701 0.121491 0.422046 --0.088328 0.107312 0.467073 --0.356358 0.050663 -0.148806 --0.129249 -0.096403 -0.134181 --0.163998 -0.069974 -0.134907 --0.150151 -0.030244 -0.366624 -0.062655 -0.048958 0.705925 -0.081642 -0.002642 -0.550158 -0.095049 0.003325 -0.498101 -0.113446 0.005123 -0.552089 --0.17147 -0.044567 -0.218881 --0.356494 0.095415 -0.106381 --0.081569 -0.101578 -0.133396 -0.359248 0.088392 -0.100782 --0.046231 -0.080746 -0.130322 -0.041642 0.121894 0.438006 -0.129402 -0.009686 0.345732 -0.201901 -0.019149 0.207794 -0.209533 0.008252 0.16446 --0.329022 0.034296 -0.095499 --0.065874 0.113583 0.355867 -0.075559 -0.055198 0.227902 -0.061849 -0.069654 0.179403 -0.089743 -0.053273 0.172255 --0.115191 -0.070046 -0.425006 --0.115434 -0.090095 -0.973743 -0.278928 0.059992 0.097192 -0.113573 -0.062643 -0.871804 -0.101579 -0.052474 -0.79698 -0.124623 0.004748 -0.499911 -0.161939 -0.11913 -0.696457 -0.136289 -0.130094 -0.703771 -0.149847 -0.119757 -0.783024 -0.225358 -0.039917 0.205755 -0.071144 -0.011928 -0.501376 -0.080559 -0.007372 -0.458874 -0.344051 0.071092 -0.060589 --0.357919 0.086664 -0.099675 --0.061593 -0.049099 0.705784 --0.032553 -0.067034 0.676746 --0.077423 -0.065374 -0.362509 -0.105019 -0.014965 0.166697 -0.212039 -0.041769 0.226301 -0.113461 -0.126095 -0.709147 -0.12885 -0.123528 -0.796839 --0.047846 -0.015163 -0.352407 -0.119073 0.044154 -0.348785 -0.116024 -0.048486 0.136832 -0.345781 0.117308 -0.104241 -0.351121 0.109018 -0.094476 -0.052426 -0.056817 0.574477 -0.095396 -0.072379 -0.798043 --0.328432 0.095191 -0.091348 --0.061499 0.118792 0.456451 --0.056158 -0.047468 -0.353951 --0.069627 0.003538 -0.420243 --0.094278 0.054827 -0.332642 -0.31826 0.03996 -0.016181 --0.043757 -0.071989 0.710178 -0.105996 0.001263 0.27446 -0.076423 0.042651 0.208272 --0.116755 -0.036153 -0.785482 --0.134088 -0.042806 -0.864056 -0.253771 -0.034132 0.168461 -0.155978 -0.039528 -0.782461 -0.169688 -0.057425 -0.788607 --0.009594 0.046517 -0.022378 -0.152497 -0.006648 -0.507371 --0.342275 0.113004 -0.121218 --0.328432 0.10443 -0.05057 --0.333615 0.114459 -0.087733 --0.349759 0.107273 -0.09328 --0.087764 0.135617 0.442196 -0.162192 -0.06653 -0.871875 -0.174311 -0.078585 -0.788353 --0.089218 0.014086 -0.447721 -0.197414 -0.033855 0.245837 -0.094128 -0.081944 0.143956 --0.169532 -0.022453 -0.295043 -0.01708 0.100498 0.598854 -0.093526 0.014904 0.191271 -0.282834 -0.017447 0.13002 -0.282789 -0.00101 0.077153 -0.108091 -0.083728 -0.886243 -0.114925 -0.090476 -0.460974 -0.229196 0.03396 0.125804 --0.011346 0.025647 -0.035213 -0.062826 0.08297 0.678076 --0.172061 -0.002328 0.335921 --0.169846 -0.00047 0.366421 -0.09249 -0.103709 -0.710652 -0.112314 -0.116058 -0.788678 -0.281825 -0.02187 0.181952 -0.272129 -0.02018 0.233186 -0.269184 0.012064 0.241483 -0.262166 -0.016175 0.27233 --0.143576 -0.133746 -0.615782 --0.160869 -0.119135 -0.696484 --0.135325 -0.130063 -0.703567 -0.171942 -0.002366 0.335949 -0.190821 0.010089 0.346412 -0.080132 -0.074061 -0.708891 -0.100808 -0.096899 -0.79974 -0.357678 0.048422 -0.139516 --0.071777 0.023237 0.672706 -0.237792 -0.032614 0.366062 -0.228778 -0.055637 0.359697 -0.2494 -0.035982 0.31464 -0.219264 0.03807 0.176292 --0.25479 -0.03794 0.26771 --0.253873 -0.042551 0.241433 --0.236493 -0.050336 0.260313 --0.13773 -0.005931 -0.414142 --0.134495 0.011459 -0.41487 -0.163194 -0.058909 -0.917034 -0.17152 -0.016533 -0.931828 --0.169841 -0.110147 -0.611986 --0.178531 -0.092624 -0.689969 --0.153471 -0.080507 0.409463 --0.176946 -0.072394 0.374346 --0.193866 0.011998 0.298053 --0.17973 -0.005852 0.290346 --0.200223 0.013169 0.252261 --0.120376 0.02512 -0.419874 --0.183095 -0.070451 -0.608863 --0.182665 -0.061429 -0.687606 --0.193025 0.006998 0.426495 --0.146159 -0.047714 -0.430289 -0.050021 0.062413 0.195904 -0.352977 0.0731 -0.151044 -0.138139 0.11513 -0.950869 -0.330384 0.03604 -0.096696 -0.339748 0.038028 -0.111719 --0.008455 -0.099159 0.047585 -0.134852 -0.023383 -0.900373 --0.19131 -0.081309 0.407231 -0.33174 0.043482 -0.109801 -0.332326 0.053947 -0.107568 -0.164425 -0.024219 0.330085 -0.157925 -0.017074 0.358447 -0.25467 -0.037979 0.267736 -0.04634 0.016295 0.551532 -0.071798 0.145186 0.383741 --0.149476 -0.121491 -0.974756 --0.144727 -0.085602 -0.97833 --0.112139 -0.135913 -0.616767 --0.113224 -0.125811 -0.707323 -0.06408 0.142024 0.402264 -0.094829 0.15407 0.402267 -0.235265 -0.053833 0.306932 -0.357062 0.077449 -0.136492 -0.116128 -0.036368 -0.78768 -0.357733 0.052408 -0.149996 --0.112323 0.042538 -0.375643 --0.014254 0.018502 0.815081 --0.026157 -0.082194 0.736953 -0.000167 -0.084481 0.746748 -0.122329 -0.012356 -0.929538 -0.181078 0.111755 -0.961681 -0.348813 0.049767 -0.152316 --0.098522 0.031918 -0.41636 --0.097817 0.045961 -0.374305 --0.17422 -0.079079 -0.788737 -0.169811 -0.096837 -0.930936 -0.066086 0.088146 0.321504 --0.149399 -0.119766 -0.783049 --0.186242 -0.062921 0.337147 --0.16574 -0.102936 -0.790708 -0.000149 0.001448 0.814988 --0.191474 -0.010617 0.245282 -0.057009 -0.098046 -0.058783 -0.345692 0.040527 -0.133543 -0.261673 0.008906 0.072566 -0.207601 0.034662 -0.964126 -0.069559 -0.002266 0.673485 -0.361242 0.06737 -0.13841 --0.017244 -0.065856 0.221488 -0.127126 0.125889 0.440487 --0.218422 0.022526 0.260721 -0.209082 -0.067097 0.348097 -0.176827 -0.072432 0.374373 -0.186123 -0.06296 0.337175 -0.161442 0.11035 -0.974177 -0.137745 0.119463 -0.96321 -0.237267 0.007735 0.314909 --0.073333 0.00819 0.671301 -0.250165 -0.012465 0.317687 -0.255373 0.003608 0.276391 -0.079771 -0.101541 -0.132485 -0.133483 0.029525 -0.373842 --0.2092 -0.067057 0.348069 -0.1605 -0.112745 -0.963402 -0.213158 -0.059199 0.297507 -0.191524 -0.05003 0.289964 -0.054443 -0.034838 0.575107 -0.054752 -0.015075 0.563712 -0.119634 -0.051103 -0.913871 --0.17563 -0.080145 0.523413 -0.146787 -0.021377 0.536589 -0.097999 0.024274 0.525773 -0.094615 0.015938 0.534984 --0.309169 0.052202 0.035263 --0.308531 0.029294 0.103042 --0.298682 0.049946 0.106018 --0.297572 0.066955 -0.047369 --0.299564 0.033494 0.150195 --0.291675 0.007987 0.193759 --0.277382 0.037895 0.197003 -0.231763 -0.00842 0.365842 --0.301845 0.003632 0.142656 --0.277477 0.054656 0.148803 --0.247437 0.050371 0.189756 --0.054748 0.033272 0.526506 -0.122437 -0.113899 0.007519 --0.193282 0.020798 -0.942916 -0.073563 0.142338 0.425029 -0.066203 0.062522 0.091972 -0.000232 0.073015 0.137844 --0.047791 0.043658 0.604435 -0.212468 0.008589 0.36307 -0.176166 0.106477 -0.95205 -0.178626 -0.029829 0.287186 --0.079237 0.005393 0.695935 --0.076123 0.021169 -0.417027 --0.169275 -0.04711 0.331153 -0.216739 -0.026752 0.424907 -0.000277 -0.068158 0.778329 -0.191192 -0.081347 0.407258 --0.098086 0.024286 0.525765 -0.350164 0.122984 -0.143516 -0.183996 0.007199 0.388155 -0.212216 -0.004421 0.405766 --0.184116 0.007238 0.38813 -0.10786 0.133032 0.444293 --0.315013 0.1033 -0.056114 -0.343636 0.114749 -0.122414 -0.105901 0.15086 0.422464 -0.087645 0.135578 0.442222 --0.279045 0.060032 0.097165 -0.018609 0.031918 0.534297 -0.109621 0.089465 -0.973165 -0.100191 0.074932 -0.964246 --0.146831 -0.018057 -0.697226 -0.21641 -0.058012 0.417767 -0.065129 -0.080297 -0.613455 -0.067307 -0.090024 -0.544694 -0.055978 -0.05627 -0.544923 --0.228896 -0.055598 0.359671 -0.153721 -0.115869 -0.552604 -0.133373 -0.106461 -0.501061 -0.12403 -0.130059 -0.552657 -0.192908 0.007033 0.426625 -0.15375 -0.002896 0.38574 -0.169726 -0.000509 0.366447 --0.062061 0.009439 0.652188 --0.056839 0.020863 0.627445 --0.121258 -0.014342 -0.700516 --0.214751 -0.048761 0.251994 --0.212159 -0.041729 0.226274 --0.197533 -0.033816 0.24581 -0.173921 -0.082221 -0.963161 -0.168202 -0.061975 -0.975816 -0.177887 -0.043544 -0.957618 --0.095604 -0.024233 -0.703071 --0.102468 -0.052468 -0.79493 --0.198954 -0.008168 0.473288 -0.105332 -0.111549 -0.498398 -0.092592 -0.120714 -0.548836 --0.213277 -0.05916 0.297479 --0.191642 -0.049991 0.289936 -0.359854 0.065564 -0.149794 -0.14721 0.014138 0.493835 -0.175852 -0.005093 0.511355 -0.181789 -0.036859 -0.610575 -0.170095 -0.017463 -0.554248 -0.17871 -0.046872 -0.552482 -0.079132 -0.094498 -0.497075 --0.031564 0.064185 0.586557 --0.068814 -0.002386 0.673389 -0.144645 -0.133741 -0.615754 --0.169618 -0.057919 -0.788991 --0.164544 -0.02418 0.330059 --0.178745 -0.029791 0.287159 --0.353449 0.100297 -0.144988 -0.170909 -0.110143 -0.611958 -0.173346 -0.081978 -0.551082 -0.144451 0.001541 -0.553988 -0.184164 -0.070446 -0.608835 -0.058245 0.041798 0.617799 --0.359902 0.069812 -0.126473 --0.351593 0.071362 -0.14978 --0.348792 0.075551 -0.122467 -0.052327 0.025048 0.537409 -0.156605 -0.088769 -0.502422 -0.113103 -0.135944 -0.616971 -0.061286 -0.046683 -0.614011 -0.060984 -0.025141 -0.547473 -0.059732 -0.036682 -0.498685 -0.072668 -0.018394 -0.615161 --0.308402 0.029099 0.014168 --0.320004 0.067021 -0.007001 -0.06213 -0.067852 -0.496605 --0.112863 -0.115721 -0.78648 -0.122134 0.107746 -0.974322 -0.084451 -0.117277 -0.615599 --0.09246 -0.103354 -0.708364 --0.101836 -0.096328 -0.797447 -0.000687 0.081648 0.798072 -0.015952 0.101963 0.777649 --0.014695 0.101868 0.777555 -0.017383 0.060739 0.806361 -0.189972 0.015591 -0.975093 -0.191862 -0.004379 -0.960993 -0.000267 -0.024418 0.808928 --0.184152 -0.052972 0.530994 -0.180228 -0.027037 0.531658 -0.132653 -0.001324 -0.614735 --0.293724 0.071226 0.020867 -0.164928 0.009787 0.470594 -0.165244 -0.011569 -0.613368 -0.33453 0.113225 -0.100287 --0.317145 0.038512 -0.015189 --0.235384 -0.053793 0.306905 --0.165729 -0.069459 -0.874049 -0.168543 -0.057623 -0.504902 -0.169155 -0.04715 0.33118 -0.000295 -0.048076 0.797157 -0.100339 -0.002801 -0.615402 --0.24952 -0.035943 0.314613 --0.344146 0.066069 -0.129231 --0.353663 0.060882 -0.126628 -0.321049 0.068382 -0.007936 --0.233519 -0.049564 0.232094 --0.176089 -0.005058 0.511692 -0.065548 -0.047106 -0.461278 --0.237912 -0.032574 0.366035 -0.354767 0.102019 -0.146066 --0.049903 0.064645 0.606883 -0.092275 -0.088632 -0.463041 --0.215753 0.016943 0.30734 --0.19094 0.010127 0.346385 -0.347086 0.098279 -0.123603 --0.250285 -0.012426 0.317661 --0.23188 -0.008467 0.365694 -0.119313 0.028689 0.512206 --0.212585 0.008554 0.362939 --0.212317 -0.0049 0.405009 -0.156511 -0.038022 0.355508 --0.027271 0.106695 0.664112 --0.237387 0.007774 0.314882 -0.100543 -0.091974 0.537372 --0.061758 0.062859 0.633349 --0.066238 -0.090029 -0.544722 --0.0584 0.041725 0.617799 --0.066718 0.02961 0.643738 --0.081869 -0.044364 -0.703365 --0.096247 -0.072048 -0.795993 --0.171555 -0.034578 -0.692527 -0.153797 -0.057313 0.371336 --0.080103 -0.073706 -0.706604 --0.216857 -0.026786 0.424776 --0.14007 -0.023534 -0.900373 -0.125964 0.090476 0.363992 -0.096007 0.076435 0.350796 -0.138416 0.051197 0.39081 --0.052406 0.099858 0.672279 --0.058922 0.096015 0.666199 --0.082735 0.018206 0.705165 -0.088088 -0.111252 0.1119 -0.122322 -0.083917 0.107098 -0.094708 -0.129284 0.049443 -0.111728 -0.112324 0.082435 -0.06644 -0.127597 0.083167 -0.117103 0.023259 0.342647 -0.024221 -0.111692 0.078087 -0.049197 -0.110788 0.111325 -0.020854 -0.089789 0.106589 --0.039595 0.038126 -0.123114 -0.196143 -0.082017 0.459588 -0.071653 0.049761 0.052075 -0.068917 -0.092263 0.142257 -0.153351 -0.080547 0.40949 -0.162002 -0.095627 0.452908 -0.17152 -0.09458 0.495315 -0.138631 0.086743 0.448996 --0.262284 -0.016136 0.272303 -0.115985 -0.03189 -0.947976 --0.240876 0.018577 0.268688 --0.021682 -0.068079 0.181314 -0.024394 -0.068695 0.1802 -0.103653 -0.086124 -0.928478 -0.118736 -0.11038 -0.897024 -0.023641 -0.047052 0.792444 -0.130924 -0.08206 0.442419 -0.163385 -0.097974 -0.977937 -0.065571 0.074633 0.705359 -0.075525 0.044237 0.706769 -0.155403 0.080724 -0.978019 --0.200349 -0.031877 0.511405 --0.180522 -0.027003 0.532176 -0.316374 0.105045 -0.057311 -0.298933 0.0687 -0.048566 -0.101867 0.027765 0.144359 -0.078465 0.050928 0.154908 --0.255493 0.003647 0.276365 -0.044808 0.051491 0.797208 -0.056032 0.025116 0.792472 -0.057676 0.061247 0.779862 -0.032619 0.076882 0.792434 -0.044295 0.090641 0.76996 -0.153823 -0.023485 0.066935 -0.140312 -0.041279 0.102405 -0.143659 -0.073205 0.071343 -0.044913 -0.130195 0.048916 -0.328643 0.107482 -0.089929 -0.359103 0.120508 -0.142271 --0.116711 -0.063894 -0.872701 -0.037623 0.015333 0.806564 -0.329793 0.106175 -0.051768 -0.013631 -0.065663 0.215941 -0.018712 0.004167 0.813279 --0.202019 -0.01911 0.207767 -0.03988 -0.080503 0.145011 -0.11583 0.00439 -0.951472 -0.063954 0.068662 0.757824 --0.088107 -0.10108 0.514606 -0.304269 0.044884 -0.059759 -0.071426 0.032298 0.769955 -0.33071 0.036433 -0.056124 --0.059257 0.073308 0.277159 -0.067868 -0.030914 -0.431164 -0.051605 0.094388 0.699984 -0.308124 0.031521 -0.022304 --0.223946 -0.021616 0.161188 --0.209652 0.008291 0.164432 -0.11894 0.10209 0.463365 -0.179601 -0.092619 -0.689941 --0.302907 0.043139 -0.058563 --0.225476 -0.039879 0.205727 -0.067115 0.072045 0.732783 -0.049534 0.098277 0.740572 --0.037865 0.106055 0.663632 -0.134121 -0.101168 0.050634 -0.049119 -0.08204 0.316253 -0.075891 -0.017079 0.704095 -0.145693 0.082887 0.387494 -0.148513 0.077463 0.419952 --0.020141 0.076839 0.079383 --0.073478 0.071222 -0.125881 -0.18373 -0.061424 -0.687579 --0.119577 0.075674 -0.127002 --0.256679 0.051936 0.08732 --0.252373 0.033449 0.076668 --0.08359 -0.11721 -0.615163 --0.155435 0.052239 -0.127829 -0.140036 -0.126156 -0.962029 -0.112933 0.060372 -0.975334 --0.060424 -0.046616 -0.613575 --0.071807 -0.018327 -0.614725 -0.126995 -0.004658 0.124934 -0.121818 0.020068 -0.968705 --0.132384 0.052987 -0.930089 -0.020565 -0.022346 0.80643 -0.172617 -0.034573 -0.6925 -0.075579 -0.022709 -0.438903 -0.067669 -0.009307 -0.416099 -0.14779 -0.018088 -0.697429 -0.127355 -0.021293 -0.963114 -0.081894 -0.04472 -0.705653 -0.095626 -0.024589 -0.705358 -0.121488 -0.014626 -0.70234 --0.168278 -0.059321 -0.917305 -0.079233 0.03866 0.740287 --0.261791 0.008945 0.072539 --0.231986 0.004352 0.117807 -0.21154 -0.046599 0.468037 -0.198718 -0.008203 0.472951 -0.043258 0.071539 0.141414 --0.127551 -0.012406 -0.929538 --0.026188 0.058074 0.012186 -0.112256 0.042497 -0.375606 -0.134501 0.011415 -0.414821 -0.026596 -0.068706 0.768445 -0.097698 0.045923 -0.374278 -0.137736 -0.005975 -0.414093 -0.122283 -0.115801 -0.974611 --0.064269 -0.08023 -0.613019 -0.19953 -0.031896 0.50925 -0.10673 -0.105034 -0.952919 --0.174945 -0.096887 -0.930936 --0.166068 -0.090536 -0.885475 -0.137206 0.059278 0.473049 -0.108901 0.069512 0.488713 -0.043453 -0.067073 0.234408 -0.148483 0.0426 0.439737 --0.121795 0.024085 -0.939902 -0.070013 -0.024905 -0.463256 -0.094521 0.054911 -0.332617 --0.044988 -0.069707 0.571411 -0.196549 -0.06939 0.502664 --0.269302 0.012103 0.241456 -0.139593 -0.085551 -0.97833 --0.1813 0.106426 -0.95205 -0.084087 -0.118334 -0.0167 -0.070692 -0.131623 0.017117 --0.027246 0.032932 -0.019514 -0.068233 0.043141 -0.330533 -0.169942 -0.022509 -0.294929 --0.066197 0.062519 0.092052 --0.246955 0.034265 0.236012 -0.000944 0.079609 0.57354 -0.157193 0.037473 -0.975575 --0.017007 0.074555 0.577188 --0.092451 0.046276 0.119469 --0.101966 0.027771 0.14434 -0.148856 -0.027476 -0.970453 -0.357818 0.09714 -0.107473 --0.253954 -0.017301 0.119624 --0.062212 -0.069614 0.179375 --0.118028 0.064277 0.011804 --0.165178 0.032598 -0.050201 --0.078283 0.059413 0.009658 --0.139111 0.065256 -0.050545 --0.067102 -0.127608 0.083129 --0.093764 -0.129297 0.049364 --0.044257 -0.130188 0.048792 --0.02388 -0.118223 0.017593 --0.133511 -0.101327 0.05037 --0.070802 -0.131593 0.016976 --0.045983 -0.121947 -0.007695 --0.12708 -0.004649 0.124927 --0.122402 -0.083924 0.107013 -0.042901 -0.013411 0.798205 --0.110434 -0.112461 0.082173 --0.300537 0.030467 -0.022794 -0.064719 -0.006094 0.777678 -0.06977 -0.03297 0.746087 -0.079517 0.002111 0.744329 --0.097479 0.07402 -0.050754 -0.04895 -0.041262 0.777678 -0.051476 -0.062134 0.744591 -0.077936 0.061074 -0.282267 -0.049416 0.035999 -0.279656 -0.085 0.027706 -0.418945 -0.093614 0.023107 -0.434949 -0.083504 0.018742 -0.434733 -0.10551 0.024301 -0.434957 -0.099179 0.017905 -0.450637 -0.104225 0.010943 -0.473395 -0.093515 0.007878 -0.466875 -0.085533 -0.001387 -0.479578 -0.086084 0.003223 -0.460441 -0.079785 0.00487 -0.444702 -0.075304 0.009084 -0.435316 -0.069238 0.013881 -0.415505 -0.064922 0.011603 -0.400495 -0.067382 0.022857 -0.391336 -0.071056 0.033647 -0.36988 -0.075886 0.030472 -0.393918 -0.086388 0.036786 -0.396518 -0.087717 0.044492 -0.374683 -0.096522 0.042056 -0.387442 -0.107434 0.038476 -0.397641 -0.11772 0.03446 -0.397719 -0.111318 0.030022 -0.419755 -0.115082 0.022058 -0.434555 --0.154396 -0.04401 -0.821796 --0.146033 -0.03405 -0.792788 --0.14559 -0.038588 -0.836323 --0.151405 -0.029697 -0.744407 --0.138817 -0.024076 -0.746508 --0.133452 -0.013771 -0.699098 --0.128459 -0.022903 -0.747439 --0.117281 -0.025556 -0.748756 --0.126527 -0.031998 -0.793891 --0.123915 -0.04171 -0.82303 --0.136441 -0.037708 -0.836495 --0.144431 -0.036866 -0.866704 --0.140784 -0.036521 -0.889768 --0.14861 -0.032492 -0.887839 --0.148537 -0.019592 -0.89748 --0.156102 -0.036015 -0.894347 --0.163448 -0.037764 -0.906158 --0.161411 -0.050346 -0.895794 --0.16356 -0.066484 -0.898927 --0.163188 -0.058661 -0.874199 --0.167186 -0.064576 -0.838923 --0.162506 -0.054127 -0.837091 --0.16378 -0.04718 -0.789485 --0.17189 -0.048281 -0.739308 --0.162645 -0.037607 -0.742288 --0.161111 -0.024278 -0.695175 -0.034314 0.049359 0.593313 -0.031715 0.064242 0.586614 -0.039882 0.05637 0.594047 -0.018033 0.074655 0.577287 -0.027023 0.077375 0.582822 -0.035109 0.075342 0.589601 -0.043328 0.076991 0.600573 -0.042798 0.064854 0.596433 -0.049759 0.052827 0.603861 -0.055703 0.052902 0.614078 -0.06172 0.031236 0.625212 -0.054571 0.031488 0.61617 -0.04518 0.02485 0.613831 -0.042287 0.036067 0.605219 -0.025777 0.044468 0.593641 -0.014607 0.039923 0.594763 -0.008979 0.05128 0.586359 --0.007541 0.051232 0.586311 -0.00073 0.061307 0.579418 --0.004866 0.069214 0.575659 -0.006351 0.069261 0.575706 -0.009533 0.077746 0.574115 -0.137823 -0.072293 -0.448672 -0.14702 -0.076721 -0.464719 -0.144444 -0.066039 -0.45078 -0.147212 -0.087648 -0.480882 -0.156045 -0.07636 -0.482139 -0.164822 -0.073968 -0.503427 -0.161374 -0.062994 -0.483552 -0.163648 -0.047787 -0.484884 -0.157219 -0.052742 -0.467207 -0.150737 -0.045026 -0.452725 -0.149314 -0.055426 -0.451855 -0.142063 -0.058432 -0.430772 -0.141529 -0.051298 -0.399741 -0.13082 -0.059998 -0.402149 -0.121509 -0.065181 -0.367875 -0.121133 -0.064061 -0.400636 -0.10807 -0.065477 -0.398856 -0.125755 -0.068748 -0.427151 -0.129082 -0.076721 -0.446488 -0.117464 -0.078584 -0.445528 -0.127782 -0.087858 -0.46154 -0.123989 -0.099707 -0.478668 -0.136715 -0.094357 -0.479887 -0.146434 -0.100096 -0.501818 -0.0751 -0.005894 -0.439339 -0.068813 0.003522 -0.420396 -0.060073 0.00326 -0.38662 -0.058085 -0.009248 -0.386583 -0.052855 0.004846 -0.358571 -0.045201 0.005832 -0.332608 -0.053793 0.018972 -0.343158 -0.056407 0.030966 -0.330399 -0.064471 0.033784 -0.344822 -0.07639 0.0447 -0.34469 -0.072056 0.038791 -0.352654 -0.079377 0.042965 -0.360043 --0.322854 0.106248 -0.077181 --0.313158 0.092123 -0.066962 --0.320502 0.095611 -0.078823 --0.333369 0.102499 -0.098747 --0.346526 0.104262 -0.113642 --0.337765 0.107296 -0.110064 --0.338259 0.116265 -0.110842 -0.017746 0.040951 0.812066 -0.009216 0.030475 0.814915 -0.020663 0.028746 0.813476 --0.00773 0.030427 0.814869 -0.000519 0.020385 0.816414 --0.005597 0.011172 0.816161 -0.006185 0.011219 0.816208 -0.009217 -0.000749 0.81448 -0.015179 0.018604 0.815181 -0.027084 0.022222 0.812192 -0.028174 0.010026 0.810997 -0.035581 0.02961 0.807508 -0.044336 0.035043 0.802024 -0.037243 0.045818 0.804088 -0.03363 0.059166 0.801916 -0.025828 0.050636 0.807364 -0.009273 0.050657 0.810793 -0.000687 0.061143 0.808539 --0.007787 0.05061 0.810747 --0.024567 0.050517 0.807247 --0.016261 0.040856 0.811973 --0.019401 0.028627 0.813359 -0.18904 0.05325 -0.939661 -0.182748 0.035737 -0.933876 -0.195966 0.039643 -0.94641 -0.164345 0.034493 -0.924958 -0.174357 0.016942 -0.928091 -0.167372 -0.001747 -0.921102 -0.179159 0.00241 -0.937492 -0.183653 -0.011515 -0.948821 -0.192301 0.007064 -0.952195 -0.200541 0.015069 -0.963765 -0.200717 0.0255 -0.955328 -0.206609 0.045938 -0.955087 -0.212793 0.056229 -0.96256 -0.209337 0.06592 -0.955087 -0.201419 0.088055 -0.954943 -0.201847 0.076324 -0.949784 -0.193309 0.085722 -0.948339 -0.192192 0.06995 -0.943036 -0.174661 0.068452 -0.937492 -0.162798 0.083759 -0.940143 -0.150956 0.069695 -0.933635 -0.12904 0.072825 -0.934358 -0.146932 0.05325 -0.926886 -0.14579 0.034745 -0.918932 --0.022539 -0.100064 0.091452 --0.034476 -0.101114 0.108583 --0.037401 -0.114645 0.094311 --0.030398 -0.087436 0.126216 --0.046951 -0.094812 0.127269 --0.056899 -0.086745 0.145514 --0.0631 -0.10197 0.128565 --0.079022 -0.103607 0.1285 --0.070143 -0.11564 0.112648 --0.079179 -0.122321 0.098194 --0.056918 -0.122012 0.097079 --0.043539 -0.124662 0.081103 --0.055252 -0.131201 0.065892 --0.03184 -0.122629 0.064184 --0.021241 -0.119156 0.047819 --0.014697 -0.106949 0.062432 --0.005273 -0.087201 0.062205 --0.011532 -0.094401 0.076493 --0.011787 -0.086838 0.089827 --0.004127 -0.078303 0.089556 --0.007918 -0.081657 0.10567 -0.000538 -0.082118 0.117488 --0.014592 -0.081206 0.124929 --0.022503 -0.076678 0.144759 -0.131016 0.030007 -0.921102 -0.143786 0.015279 -0.910738 -0.14456 -0.003872 -0.904471 -0.155358 -0.003662 -0.909773 -0.1614 -0.021081 -0.914594 -0.119759 0.090982 -0.939902 -0.114652 0.074994 -0.938938 -0.10756 0.073056 -0.946893 -0.113518 0.057939 -0.939902 -0.111983 0.043341 -0.944241 -0.119362 0.03847 -0.932912 -0.123592 0.019284 -0.927128 -0.122701 0.016798 -0.434688 -0.119816 0.015003 -0.450125 -0.123458 0.007119 -0.467249 -0.115291 0.010284 -0.473285 -0.110572 0.006901 -0.501334 -0.123614 0.037812 -0.375614 -0.126764 0.02868 -0.397726 -0.136835 0.018556 -0.394055 -0.127983 0.018983 -0.419896 -0.128589 0.010047 -0.432236 -0.13734 0.003116 -0.416256 -0.130069 -6.1e-05 -0.434212 -0.133967 -0.011808 -0.420916 -0.128438 -0.005142 -0.442843 -0.130463 -0.011981 -0.449293 -0.127148 -0.001314 -0.460421 -0.131875 0.000305 -0.482078 -0.07376 -0.060361 -0.446447 -0.081422 -0.070317 -0.446155 -0.074366 -0.059211 -0.42425 -0.083033 -0.080999 -0.460094 -0.092136 -0.079874 -0.450203 -0.101872 -0.09105 -0.460544 -0.104184 -0.079577 -0.445754 -0.101264 -0.071654 -0.425042 -0.095197 -0.066519 -0.397707 -0.081907 -0.063519 -0.3956 -0.065807 -0.059207 -0.358372 -0.070643 -0.057006 -0.394146 -0.063182 -0.044838 -0.390651 -0.049602 -0.032254 -0.350668 -0.058213 -0.033789 -0.389715 -0.060478 -0.025451 -0.402212 -0.064594 -0.038002 -0.423824 -0.068956 -0.050201 -0.446956 -0.067741 -0.059835 -0.460462 -0.063104 -0.056832 -0.477018 -0.067905 -0.071221 -0.476469 -0.068524 -0.082087 -0.496373 -0.075535 -0.082429 -0.476394 -0.086245 -0.092693 -0.476471 --0.13985 -0.032976 0.544864 --0.121856 -0.015987 0.544631 --0.157599 -0.047413 0.542014 --0.138462 -0.044574 0.546762 --0.118631 -0.049816 0.550605 --0.118026 -0.037195 0.550138 --0.093064 -0.030237 0.555935 --0.109946 -0.022646 0.549189 --0.095244 -0.014451 0.549912 --0.07089 -0.019631 0.558345 --0.070176 -0.006256 0.552081 --0.052827 -0.000471 0.559068 --0.061982 0.00917 0.546538 --0.052401 0.02565 0.53756 --0.071502 0.020137 0.538825 --0.097721 0.006941 0.540286 --0.115382 0.000856 0.538372 --0.119433 0.007059 0.530845 --0.111344 0.022729 0.520377 --0.132721 0.00573 0.523074 --0.1349 0.020916 0.511005 --0.151237 -0.002315 0.520796 --0.162466 0.000964 0.509945 --0.165064 -0.01331 0.526696 --0.178461 -0.01867 0.527382 --0.171972 -0.028404 0.534845 --0.183187 -0.037055 0.533602 --0.17008 -0.044257 0.538687 --0.169844 -0.059164 0.536704 --0.011435 -0.071785 0.162323 --0.033151 -0.071885 0.162021 --0.043355 -0.069196 0.184369 --0.050995 -0.074575 0.161725 --0.067752 -0.080186 0.156535 --0.079645 -0.075422 0.161214 --0.081894 -0.09013 0.145433 --0.096184 -0.097408 0.127305 -0.155193 -0.042267 -0.328511 -0.144044 -0.056138 -0.331802 -0.146565 -0.046701 -0.362179 -0.14187 -0.067816 -0.300288 -0.129697 -0.065918 -0.33509 -0.112584 -0.069761 -0.334921 -0.145724 -0.04062 -0.397722 -0.14768 -0.037354 -0.429624 -0.14385 -0.031198 -0.410293 -0.13465 -0.019295 -0.431087 -0.139795 -0.022598 -0.40794 -0.144662 -0.015466 -0.390082 -0.147078 -0.005504 -0.387421 -0.156843 -0.015484 -0.353528 -0.16598 -0.010938 -0.323817 -0.162478 -0.027604 -0.325149 -0.165205 -0.038736 -0.295989 -0.17237 -0.033081 -0.260025 -0.16446 -0.050074 -0.260846 -0.161644 -0.06222 -0.2196 -0.152275 -0.065284 -0.261854 -0.137313 -0.078934 -0.26276 --0.035914 0.029887 0.535065 --0.035627 0.023916 0.550875 --0.045107 -0.011677 0.572079 --0.044876 0.004108 0.5716 --0.044648 0.008032 0.590438 --0.040072 0.016353 0.569512 --0.037008 0.012413 0.596277 --0.030016 0.020549 0.569024 --0.022786 0.024164 0.555526 --0.027801 0.026911 0.545729 --0.027459 0.037248 0.523334 --0.15181 -0.045272 0.070061 --0.143513 -0.057344 0.089619 --0.147568 -0.033066 0.086242 --0.133135 -0.081212 0.091882 --0.134142 -0.064362 0.105115 --0.121507 -0.066958 0.122248 --0.130141 -0.046087 0.118848 --0.125061 -0.026671 0.131923 --0.135215 -0.024117 0.114596 --0.135435 -0.002784 0.106996 --0.143273 -0.018113 0.097486 --0.150184 -0.01141 0.074829 --0.152458 0.008475 0.053013 --0.157561 -0.009098 0.049444 --0.166125 -0.015147 0.017548 --0.15867 -0.030222 0.04676 --0.154641 -0.056404 0.046788 --0.159489 -0.064526 0.015852 --0.149614 -0.077279 0.04327 --0.14235 -0.093183 0.038038 --0.138852 -0.091657 0.060299 --0.123816 -0.106646 0.066798 --0.129778 -0.095184 0.080177 --0.118404 -0.100227 0.094179 --0.170441 -0.04876 -0.016736 --0.162724 -0.069586 -0.017523 --0.163057 -0.075771 -0.054285 --0.151345 -0.088426 -0.018273 --0.136028 -0.10162 -0.018561 --0.140846 -0.09927 0.00987 --0.129984 -0.108136 0.032549 --0.170585 -0.002107 -0.013763 --0.173047 -0.025564 -0.015013 --0.178563 -0.035749 -0.052958 --0.181919 -0.023271 -0.091209 --0.179103 -0.044763 -0.092663 --0.17597 -0.052477 -0.134767 --0.171315 -0.06396 -0.093206 --0.1577 -0.080727 -0.092798 -0.131039 0.13736 0.411877 -0.140198 0.119042 0.411468 -0.135377 0.131816 0.400307 -0.144295 0.099758 0.424146 -0.146946 0.09759 0.408961 -0.148633 0.077584 0.402356 -0.146465 0.09759 0.394257 -0.141644 0.100482 0.37859 -0.139715 0.118559 0.38881 -0.130556 0.136395 0.392112 -0.125229 0.136629 0.384559 -0.123446 0.146097 0.401633 -0.113217 0.154187 0.409708 -0.122121 0.147965 0.414047 -0.127664 0.139047 0.421037 -0.123473 0.137444 0.430974 -0.132243 0.124585 0.432438 -0.132484 0.110605 0.445597 -0.139715 0.103616 0.435956 -0.1455 0.079994 0.435619 -0.105464 -0.079538 -0.965452 -0.108877 -0.059028 -0.964005 -0.113441 -0.073235 -0.973405 -0.112164 -0.048839 -0.948579 -0.114524 -0.041362 -0.959667 -0.118654 -0.025019 -0.956534 -0.124001 -0.034779 -0.963282 -0.13177 -0.046317 -0.96979 -0.14364 -0.059065 -0.97437 -0.126855 -0.067107 -0.975575 -0.120626 -0.088659 -0.978708 -0.12822 -0.105502 -0.979672 -0.115311 -0.104016 -0.975093 -0.110603 -0.112693 -0.967861 -0.106386 -0.09863 -0.966175 -0.102106 -0.0885 -0.953641 -0.104124 -0.095288 -0.938938 -0.103842 -0.077671 -0.938697 -0.107859 -0.071404 -0.923031 -0.110488 -0.058521 -0.935082 -0.11693 -0.040685 -0.934599 --0.074463 -0.077453 0.307102 --0.078401 -0.059789 0.264934 --0.060105 -0.068898 0.271716 --0.094806 -0.048793 0.261802 --0.092646 -0.040849 0.223745 --0.098855 -0.019207 0.193097 --0.093596 -0.035382 0.193387 --0.097932 -0.038457 0.172604 --0.082951 -0.049343 0.190749 --0.078645 -0.064003 0.179784 --0.067216 -0.061508 0.206435 --0.051503 -0.065799 0.210524 --0.056095 -0.066328 0.240663 --0.042784 -0.069414 0.257042 --0.030736 -0.06768 0.230518 --0.025676 -0.071542 0.257965 --0.009122 -0.069386 0.246128 --0.02034 -0.08079 0.311141 --0.015722 -0.091002 0.38749 --0.038388 -0.094059 0.388783 --0.063779 -0.102674 0.454682 --0.069124 -0.099458 0.402922 --0.099713 -0.101902 0.417736 --0.082542 -0.092616 0.359743 --0.092785 -0.07983 0.333374 --0.110242 -0.06956 0.339931 --0.102354 -0.060438 0.304457 --0.103764 -0.036518 0.268755 --0.167933 0.08371 -0.940143 --0.179796 0.068401 -0.937492 --0.156091 0.069644 -0.933635 --0.197328 0.069899 -0.943036 --0.194175 0.053199 -0.939661 --0.201101 0.039592 -0.94641 --0.188074 0.035687 -0.933876 --0.179874 0.016891 -0.928091 --0.169862 0.034443 -0.924958 --0.151499 0.034695 -0.918932 --0.152259 0.053199 -0.926886 --0.134175 0.072774 -0.934358 --0.119787 0.074944 -0.938938 --0.124894 0.090931 -0.939902 --0.123458 0.104295 -0.947616 --0.140525 0.105246 -0.944 --0.164355 0.112976 -0.95099 --0.161647 0.101154 -0.944964 --0.1825 0.094357 -0.947134 --0.198444 0.085672 -0.948339 --0.206982 0.076274 -0.949784 --0.26955 -0.027566 0.148414 --0.269784 -0.031868 0.174756 --0.283577 -0.021435 0.15516 --0.254123 -0.038395 0.194776 --0.268151 -0.032264 0.201521 --0.266811 -0.032233 0.222687 --0.27862 -0.020605 0.208538 --0.283485 -0.005696 0.214455 --0.290721 -0.008551 0.188788 --0.298912 0.005133 0.168092 --0.294046 -0.009776 0.162176 --0.295288 -0.009656 0.136587 --0.302695 0.003478 0.118243 --0.294271 -0.005903 0.112597 --0.294476 0.000933 0.081539 --0.281762 -0.011371 0.106581 --0.268831 -0.011324 0.101957 --0.268751 -0.021254 0.123824 --0.253482 -0.027507 0.142668 --0.238511 -0.021268 0.139045 --0.237648 -0.031749 0.163265 --0.223083 -0.032097 0.185407 --0.238055 -0.038335 0.18903 --0.240273 -0.043833 0.209926 --0.092235 -0.053103 0.557851 --0.065838 -0.057596 0.565579 --0.068238 -0.048313 0.5663 --0.06415 -0.06796 0.563892 --0.052196 -0.056957 0.57459 --0.039091 -0.057596 0.588712 --0.044625 -0.051689 0.58389 --0.046306 -0.037344 0.587499 --0.054252 -0.04554 0.57714 --0.069674 -0.038068 0.56485 --0.070156 -0.028909 0.562199 --0.110436 -0.062109 0.551569 --0.113088 -0.075607 0.547471 --0.091271 -0.07118 0.55544 --0.061976 -0.078684 0.556998 --0.307034 0.016774 0.123519 --0.308559 0.021113 0.09218 --0.309478 0.03837 0.071997 --0.310004 0.030432 0.052442 --0.31328 0.040791 0.018598 --0.304347 0.019634 0.047448 --0.29708 0.01208 0.043322 --0.301366 0.02206 0.010747 --0.286625 0.011456 0.039936 --0.278617 0.015978 0.038748 --0.271092 0.000358 0.073531 --0.255479 -0.00576 0.098726 --0.10819 0.051484 0.346852 --0.091984 0.065889 0.328944 --0.101225 0.039468 0.317848 --0.099468 0.030274 0.292639 --0.11085 0.013589 0.310555 --0.119912 -0.008526 0.319179 --0.125871 0.000417 0.343807 --0.129094 -0.019956 0.347048 --0.14803 0.06051 0.427228 --0.148512 0.060269 0.40915 --0.148753 0.077624 0.402329 --0.144415 0.068705 0.390109 --0.139835 0.083407 0.375333 --0.131157 0.07232 0.373959 --0.113804 0.08148 0.361907 --0.165551 0.020785 -0.013373 --0.173778 0.01109 -0.05048 --0.172771 0.023123 -0.088693 --0.179595 0.000111 -0.089983 --0.182354 -0.010619 -0.132109 --0.255119 -0.04115 0.231815 --0.262239 -0.034719 0.239786 --0.256245 -0.039171 0.250488 --0.263742 -0.029584 0.251808 --0.261051 -0.028069 0.269964 --0.267756 -0.016443 0.253074 --0.267568 -0.003742 0.255398 --0.275969 -0.003485 0.237115 --0.282221 0.010851 0.218632 --0.229679 -0.046571 0.221176 --0.24261 -0.046618 0.225799 --0.245496 -0.048147 0.236884 --0.347738 0.040042 -0.142918 --0.353667 0.042125 -0.127058 --0.356317 0.046678 -0.138321 --0.35461 0.040786 -0.15294 -0.005749 0.069597 0.154621 --0.016153 0.071343 0.154745 --0.022133 0.07466 0.135914 --0.030434 0.070256 0.160134 --0.048125 0.065048 0.164629 --0.033519 0.068932 0.193502 --0.035154 0.071296 0.236562 --0.016637 0.072116 0.217562 -0.001976 0.076208 0.235974 --0.001783 0.071803 0.195631 -0.007337 0.068129 0.165708 -0.029494 0.021295 0.609461 -0.015004 0.021967 0.592485 -0.015432 0.027967 0.60197 -0.000534 0.033823 0.598879 -0.022632 0.119945 0.709601 -0.01864 0.122279 0.72591 -0.030456 0.116728 0.725309 -0.007049 0.124805 0.725863 -0.014014 0.119953 0.745819 -0.007809 0.113608 0.764353 -0.022299 0.110654 0.76278 -0.030941 0.098067 0.775004 -0.035492 0.104674 0.76056 -0.047384 0.096111 0.756079 -0.039022 0.10842 0.743 -0.041374 0.10915 0.725558 -0.051577 0.098428 0.724437 -0.043447 0.106507 0.711471 -0.043852 0.103614 0.701769 -0.035145 0.109797 0.700616 -0.026544 0.112935 0.696855 -0.016846 0.119048 0.696408 -0.003831 0.121368 0.693726 -0.001843 0.12455 0.707691 --0.005359 0.124757 0.725816 --0.047895 -0.033112 0.632439 --0.039997 -0.050565 0.611532 --0.039458 -0.048356 0.630012 --0.033538 -0.067356 0.584232 --0.041964 -0.080732 0.563843 --0.027989 -0.076875 0.581195 --0.014495 -0.083384 0.576854 --0.031229 -0.063274 0.607769 --0.035367 -0.055128 0.641799 --0.027961 -0.062423 0.652942 --0.044647 -0.052561 0.666889 --0.050951 -0.056102 0.687794 --0.06074 -0.040712 0.687247 --0.070191 -0.034184 0.705635 --0.068844 -0.025178 0.6872 --0.071198 -0.011523 0.684393 --0.066181 -0.011895 0.672771 --0.064748 0.000376 0.663475 --0.05987 -0.005735 0.657821 --0.057325 0.002532 0.645455 --0.054149 -0.014155 0.644219 --0.049165 -0.019113 0.623133 --0.047295 -0.006621 0.608268 --0.046321 -0.021741 0.599809 --0.045823 -0.025293 0.583643 -0.034705 -0.078761 0.707976 -0.038589 -0.07787 0.725791 -0.025665 -0.084619 0.720957 -0.049905 -0.068353 0.725275 -0.040027 -0.073785 0.742972 -0.04014 -0.066351 0.760543 -0.027533 -0.077494 0.753043 -0.013803 -0.079631 0.757756 -0.013991 -0.085154 0.741814 -0.000149 -0.088699 0.731704 -0.013562 -0.088193 0.726908 -0.012945 -0.087998 0.713803 -0.000278 -0.088283 0.705279 -0.014016 -0.084427 0.698771 -0.017632 -0.076473 0.686237 -0.02777 -0.078283 0.694074 -0.04279 -0.069767 0.694988 -0.052085 -0.05596 0.687934 -0.053071 -0.063014 0.706322 -0.060455 -0.056201 0.725898 --0.124358 0.015972 0.116484 --0.130583 0.019448 0.097922 --0.122774 0.038074 0.084373 --0.134711 0.030922 0.074441 --0.136144 0.044633 0.047276 --0.145615 0.026361 0.05254 --0.155987 0.025318 0.021507 --0.344947 0.098323 -0.101601 --0.354748 0.089292 -0.091377 --0.351936 0.084312 -0.081462 --0.345374 0.094633 -0.072209 --0.352176 0.096955 -0.090405 --0.353859 0.107433 -0.10818 --0.330378 0.041738 -0.108605 --0.330965 0.052203 -0.106372 --0.321655 0.064517 -0.089461 --0.31604 0.055047 -0.082705 --0.306991 0.054927 -0.068061 --0.311581 0.043207 -0.073344 --0.309078 0.0326 -0.057447 --0.320561 0.033007 -0.078988 --0.339723 0.035357 -0.097872 --0.338386 0.036283 -0.110522 --0.343031 0.037898 -0.120054 --0.340279 0.050512 -0.128731 --0.335034 0.046233 -0.119727 -0.177365 0.094407 -0.947134 -0.156512 0.101205 -0.944964 -0.159221 0.113026 -0.95099 -0.13539 0.105296 -0.944 -0.118323 0.104346 -0.947616 -0.119172 0.111799 -0.956052 -0.107758 0.098325 -0.956293 -0.100776 0.089941 -0.964247 -0.102101 0.083022 -0.956534 -0.102727 0.067415 -0.955328 -0.204447 0.088714 -0.970754 -0.211592 0.068093 -0.96979 -0.199394 0.079435 -0.975334 -0.210757 0.047042 -0.970995 -0.204398 0.026319 -0.97196 -0.198123 0.038895 -0.975816 -0.178143 0.02451 -0.975575 -0.183176 0.049987 -0.977263 -0.158815 0.059781 -0.977745 -0.183612 0.069476 -0.977986 -0.181941 0.088311 -0.978226 -0.159791 0.099559 -0.977263 -0.178263 0.103536 -0.976057 -0.172326 0.114202 -0.9722 -0.19131 0.103879 -0.971959 -0.20086 0.096689 -0.964969 -0.005181 -0.087214 0.062214 -0.000179 -0.071911 0.07624 --5.2e-05 -0.073248 0.062226 -0.000429 -0.076374 0.04766 --0.004377 -0.088818 0.033195 --4.6e-05 -0.066889 0.02081 --4e-05 -0.073279 0.033488 -0.004541 -0.088818 0.033436 -0.010327 -0.099053 0.017474 -0.014908 -0.11014 0.033623 -0.033467 -0.126531 0.033382 -0.021891 -0.119156 0.04806 -0.032008 -0.122629 0.064184 -0.014856 -0.106955 0.062437 -0.011436 -0.094421 0.076506 -0.021981 -0.100084 0.091465 -0.012149 -0.086864 0.089845 -0.00887 -0.081677 0.105684 -0.004955 -0.078317 0.089565 -0.105559 0.049912 -0.953401 -0.110176 0.031911 -0.950026 -0.101499 0.059253 -0.963042 -0.100973 0.06804 -0.970754 -0.104367 0.051356 -0.969549 -0.117059 0.042578 -0.973164 -0.110071 0.033648 -0.965693 -0.116072 0.013433 -0.960631 -0.111821 0.023545 -0.95581 -0.114597 0.013327 -0.946169 -0.117434 -0.004796 -0.942795 -0.120635 0.005571 -0.934599 -0.126374 0.000526 -0.92062 -0.115697 0.052927 0.064604 -0.12412 0.057123 0.041569 -0.107076 0.06003 0.039268 -0.135992 0.0446 0.047299 -0.135003 0.05898 0.0145 -0.145382 0.056535 -0.015276 -0.128658 0.066975 -0.016655 -0.119967 0.074178 -0.051056 -0.108637 0.070634 -0.018096 -0.085949 0.067766 -0.01774 -0.097938 0.065546 0.010758 -0.089167 0.05798 0.035697 -0.071956 0.050993 0.033388 -0.082586 0.052298 0.053811 -0.080983 0.052559 0.072149 -0.094446 0.048808 0.075378 -0.10232 0.045278 0.09406 -0.109649 0.047386 0.082601 -0.122678 0.038054 0.084387 -0.130502 0.019442 0.097927 -0.134594 0.030903 0.074455 -0.14572 0.026341 0.052554 -0.156332 0.025299 0.02152 -0.152839 0.008468 0.053018 -0.124276 0.015966 0.116488 -0.135361 -0.002784 0.106996 -0.135135 -0.024123 0.114601 -0.143199 -0.018113 0.097486 -0.147489 -0.03303 0.086306 -0.150351 -0.01141 0.074829 -0.158212 -0.009098 0.049444 -0.159315 -0.030186 0.046824 -0.167008 -0.015154 0.017552 -0.173674 -0.025584 -0.015001 -0.171212 -0.002127 -0.013749 -0.173907 0.011057 -0.050457 -0.165909 0.020759 -0.013355 -0.156984 0.041113 -0.013512 -0.154395 0.051967 -0.049846 --0.124767 -0.083039 0.539078 --0.094399 -0.086967 0.543967 --0.074263 -0.095434 0.538294 --0.063175 -0.088685 0.5489 --0.181475 -0.068678 0.527955 --0.167436 -0.07399 0.532045 --0.164075 -0.086491 0.522695 --0.147763 -0.082629 0.53208 --0.124752 -0.090866 0.530217 --0.114596 -0.099145 0.514863 --0.094865 -0.097686 0.529563 --0.062976 -0.100331 0.523284 --0.148871 -0.086093 -0.13452 --0.140218 -0.093475 -0.092171 --0.118388 -0.101384 -0.091998 --0.130259 -0.101712 -0.053949 --0.119931 -0.109546 -0.023769 -0.08764 -0.093707 -0.216992 -0.081115 -0.082263 -0.256671 -0.100588 -0.085991 -0.260314 -0.063332 -0.074299 -0.253294 -0.072724 -0.072541 -0.293332 -0.067366 -0.063606 -0.326429 -0.079865 -0.068112 -0.330731 -0.091596 -0.066713 -0.364781 -0.095734 -0.069876 -0.333607 -0.108066 -0.078032 -0.299852 -0.120622 -0.085307 -0.262494 -0.130847 -0.089998 -0.220525 -0.139655 -0.088782 -0.177295 -0.118948 -0.097977 -0.176964 -0.105677 -0.103228 -0.133752 -0.095775 -0.101002 -0.17561 -0.073108 -0.096493 -0.173495 -0.058404 -0.094484 -0.130672 -0.055271 -0.085402 -0.17082 -0.04176 -0.069767 -0.168502 -0.052019 -0.074652 -0.210941 -0.049896 -0.062168 -0.250626 -0.000105 -0.093426 0.416664 -0.016088 -0.091022 0.387503 -0.014568 -0.097048 0.458884 -0.020875 -0.080817 0.311158 -0.038653 -0.094125 0.389 -0.06913 -0.099564 0.403338 -0.063146 -0.102773 0.454611 -0.092277 -0.103655 0.496372 -0.061235 -0.104008 0.501116 -0.062183 -0.100099 0.522373 -0.035222 -0.101386 0.506885 -0.010127 -0.099013 0.5109 --0.001957 -0.093507 0.542871 --0.009467 -0.099126 0.510991 --0.035298 -0.101612 0.507551 --0.01426 -0.097154 0.458971 --0.004296 -0.064566 0.210009 -0.009517 -0.069399 0.246137 -0.026069 -0.071576 0.257988 -0.174655 0.01451 -0.214808 -0.173821 0.004595 -0.257441 -0.168303 0.024016 -0.256211 -0.175462 -0.015423 -0.258951 -0.1718 -0.00441 -0.293582 -0.163634 0.005286 -0.322165 -0.152804 0.016459 -0.33883 -0.157001 0.021904 -0.321052 -0.147233 0.035817 -0.319925 -0.159648 0.031627 -0.291239 -0.159558 0.041268 -0.255456 -0.146861 0.054797 -0.254858 -0.155792 0.05037 -0.212704 -0.148643 0.058914 -0.169526 -0.162318 0.04321 -0.170186 -0.16849 0.034677 -0.128877 -0.172439 0.024603 -0.171537 -0.179366 0.002286 -0.173456 -0.182242 -0.010652 -0.132087 -0.181041 -0.019273 -0.175061 -0.177474 -0.03964 -0.176264 -0.177971 -0.027068 -0.217924 -0.134343 0.058148 -0.289554 -0.134626 0.04881 -0.318837 -0.119812 0.057273 -0.317494 -0.133323 0.040997 -0.33629 -0.109918 0.05439 -0.334077 -0.10214 0.060962 -0.315465 -0.086957 0.059268 -0.313516 -0.095394 0.067287 -0.28474 -0.085052 0.069002 -0.247709 -0.107071 0.071003 -0.250465 -0.119164 0.072706 -0.21002 -0.128348 0.065555 -0.253251 -0.028509 -0.062365 0.653021 -0.036108 -0.055071 0.641878 -0.04558 -0.05245 0.66701 -0.032152 -0.063137 0.60769 -0.040205 -0.048321 0.630067 -0.0408 -0.050443 0.611472 -0.048401 -0.033136 0.632478 -0.049234 -0.019221 0.623613 -0.054612 -0.014209 0.644259 -0.057802 0.002485 0.64549 -0.060721 -0.005676 0.65792 -0.065591 0.000503 0.663586 -0.067219 -0.011753 0.672911 -0.072041 -0.011396 0.684503 -0.069883 -0.025037 0.68734 -0.071229 -0.034042 0.705776 -0.061825 -0.04057 0.687388 -0.007749 -0.071412 0.681176 -0.017824 -0.070295 0.667247 -0.012474 -0.06725 0.654764 -0.000418 -0.068214 0.64512 --0.011874 -0.069195 0.644369 -0.003307 -0.075928 0.612096 -0.003083 -0.085682 0.577241 -0.018014 -0.079302 0.588959 -0.029115 -0.076531 0.580916 -0.041843 0.031531 -0.054264 -0.046956 0.047724 -0.084097 -0.035804 0.02614 -0.082837 -0.06273 0.065099 -0.084751 -0.052552 0.058425 -0.123083 -0.06228 0.064521 -0.163763 -0.045416 0.048726 -0.162482 -0.040365 0.03884 -0.202978 -0.032626 0.029131 -0.160851 -0.024746 0.009647 -0.160336 -0.028611 0.017247 -0.120191 -0.028682 0.006079 -0.082532 -0.024102 -0.012962 -0.083182 -0.028796 -0.00423 -0.056545 -0.028981 -0.018724 -0.042869 -0.032428 -0.000686 -0.037014 -0.038354 0.015013 -0.034427 -0.044616 0.030651 -0.02668 -0.042996 0.03104 -0.012194 -0.053191 0.043575 -0.011379 -0.061717 0.050002 0.009207 -0.066937 0.059209 -0.016982 -0.073686 0.068823 -0.050037 -0.084616 0.075252 -0.085947 --0.206772 0.026449 0.219402 --0.207281 0.026054 0.196015 --0.217749 0.037713 0.203031 --0.202416 0.011146 0.190098 --0.210607 0.024829 0.169402 --0.217843 0.021974 0.143736 --0.222708 0.036883 0.149652 --0.235334 0.046607 0.132096 --0.233176 0.048541 0.156668 --0.247204 0.054673 0.163415 --0.231543 0.048146 0.183434 --0.231777 0.043844 0.209776 --0.247845 0.043785 0.215521 --0.232266 0.037564 0.231569 --0.231348 0.029005 0.24833 --0.219678 0.028227 0.243814 --0.208003 0.020542 0.256459 --0.208212 0.023241 0.238393 --0.200402 0.013902 0.233699 --0.193367 0.002578 0.248041 --0.195636 0.000164 0.228781 --0.195384 -0.01338 0.226177 --0.197141 -0.002198 0.208566 --0.203681 -0.0054 0.185921 --0.124502 -0.087365 0.436587 --0.11767 -0.024869 0.314524 --0.110453 -0.028499 0.285352 --0.115015 -0.048167 0.327124 -0.13688 -0.018398 -0.453705 -0.145146 -0.023604 -0.458171 -0.14324 -0.013696 -0.469193 -0.152096 -0.012738 -0.486574 -0.141551 -0.005659 -0.486438 -0.139321 1.7e-05 -0.507051 --0.107862 0.067365 -0.955328 --0.110694 0.049862 -0.953401 --0.106635 0.059203 -0.963042 --0.117119 0.043291 -0.944241 --0.115311 0.031862 -0.950026 --0.119732 0.013277 -0.946169 --0.116956 0.023495 -0.95581 --0.121207 0.013384 -0.960631 --0.115206 0.033599 -0.965693 --0.122194 0.042528 -0.973164 --0.109501 0.051305 -0.969549 --0.106108 0.067989 -0.970754 --0.117075 0.077062 -0.975093 --0.10601 0.083071 -0.970754 --0.108153 0.096173 -0.970272 --0.105911 0.089891 -0.964247 --0.112893 0.098274 -0.956293 --0.107236 0.082972 -0.956534 --0.112695 0.073005 -0.946893 --0.118653 0.05789 -0.939902 --0.124689 0.03842 -0.932912 --0.054251 -0.024329 0.570632 --0.111988 -0.029166 0.149335 --0.102827 -0.050105 0.155093 --0.116449 -0.007149 0.142486 --0.109537 -0.085282 0.124806 --0.107266 -0.068478 0.141081 --0.091664 -0.065896 0.159362 --0.101448 -0.006364 0.205763 --0.100792 0.002775 0.176828 --0.095502 0.023304 0.165658 --0.106851 0.009749 0.155037 --0.115526 0.014161 0.134647 --0.109753 0.047412 0.082583 --0.115809 0.052959 0.064582 --0.107196 0.060068 0.03924 --0.12426 0.057162 0.041541 --0.135163 0.05902 0.014473 --0.110525 0.02955 0.124647 --0.097403 0.04055 0.131958 --0.102391 0.040374 0.113425 --0.093323 0.048946 0.102827 --0.102411 0.045252 0.094047 --0.094558 0.04884 0.075356 -0.092157 0.048065 -0.362567 -0.080423 0.052717 -0.331143 -0.090473 0.050157 -0.344858 -0.103947 0.049278 -0.349382 -0.115333 0.043908 -0.360673 -0.102967 0.047707 -0.360461 -0.154261 -0.028668 -0.468845 -0.162366 -0.034723 -0.485764 -0.158569 -0.022198 -0.486355 -0.169657 -0.042323 -0.506075 -0.174619 -0.052305 -0.527479 -0.173675 -0.036642 -0.528507 -0.176606 -0.031142 -0.553545 -0.169928 -0.022748 -0.529261 -0.161807 -0.010156 -0.52955 -0.162113 -0.015904 -0.507419 -0.149061 -0.034016 -0.453482 -0.043372 0.087032 0.607996 -0.050796 0.077545 0.612581 -0.057733 0.0781 0.627504 -0.05621 0.065484 0.618657 -0.060708 0.050856 0.625212 --0.096239 0.027188 0.263362 --0.090507 0.027446 0.23157 --0.099124 0.006927 0.234635 --0.079112 0.04604 0.233137 --0.082624 0.036539 0.199058 --0.073807 0.044645 0.17829 --0.086095 0.035676 0.172444 --0.091055 0.038716 0.152578 --0.104198 -0.015731 0.243364 --0.087677 0.048747 0.283971 --0.076685 0.070177 0.293928 --0.076986 0.059499 0.267173 --0.066782 0.058222 0.233865 --0.219107 0.005427 0.139559 --0.227208 0.019759 0.120406 --0.239844 0.01887 0.100013 --0.238051 0.031753 0.104238 --0.251669 0.043873 0.081306 --0.241312 0.043693 0.110012 --0.249499 0.05209 0.11651 --0.266 0.059433 0.093409 --0.26285 0.057929 0.122881 --0.276557 0.058314 0.126641 --0.262711 0.058578 0.145212 --0.263273 0.054613 0.16916 --0.062151 -0.104074 0.501967 --0.09365 -0.103528 0.496735 --0.120818 -0.100624 0.499053 --0.140169 -0.09861 0.499327 --0.121535 -0.102212 0.471815 --0.120483 -0.100043 0.445758 -0.032502 0.105532 0.635204 -0.040234 0.101373 0.627981 -0.029099 0.103641 0.625923 --0.159727 -0.11605 -0.943517 --0.154575 -0.122851 -0.960149 --0.145729 -0.122779 -0.948098 --0.159244 -0.119119 -0.973887 --0.14056 -0.118146 -0.97678 --0.136031 -0.125444 -0.971237 --0.124119 -0.121526 -0.966898 --0.134307 -0.124716 -0.957739 --0.13451 -0.12082 -0.938938 --0.125296 -0.116442 -0.93725 --0.132554 -0.117879 -0.901824 --0.12834 -0.118523 -0.851712 --0.137255 -0.119545 -0.85154 --0.139205 -0.123264 -0.795694 --0.147709 -0.116859 -0.829962 --0.1577 -0.113684 -0.792993 --0.156937 -0.108369 -0.848223 --0.16437 -0.097422 -0.845784 --0.162056 -0.10353 -0.890525 --0.168228 -0.09362 -0.914112 --0.165287 -0.106934 -0.925923 --0.172012 -0.105366 -0.947133 --0.177922 -0.090558 -0.947133 --0.176295 -0.100282 -0.965211 --0.17577 -0.091291 -0.975334 --0.168679 -0.107233 -0.975334 --0.159577 -0.109299 -0.978226 -0.19361 0.100996 -0.957016 --0.346552 0.051592 -0.138698 --0.05331 0.043614 -0.011406 --0.043116 0.031079 -0.012221 --0.044736 0.030689 -0.026708 --0.036458 0.035374 -0.005666 --0.034593 0.02256 -0.016369 --0.026002 0.020196 -0.027749 --0.032548 -0.000647 -0.037041 --0.038473 0.015053 -0.034454 --0.042106 0.031463 -0.054643 --0.036354 0.025857 -0.083922 --0.047462 0.04744 -0.085184 --0.053157 0.058033 -0.124522 --0.063386 0.064816 -0.085837 --0.085277 0.075076 -0.086681 --0.074292 0.068754 -0.050417 --0.086456 0.067805 -0.017768 --0.067251 0.059248 -0.01701 --0.061836 0.050042 0.009179 --0.072076 0.051032 0.033361 --0.063817 0.04906 0.040277 --0.070173 0.052519 0.067033 --0.056287 0.052694 0.047223 --0.043826 0.062622 0.049801 --0.041395 0.049328 0.017863 --0.026846 0.045526 -0.009819 -0.026111 -0.038364 -0.058814 -0.02288 -0.033403 -0.084919 -0.027925 -0.055292 -0.087379 -0.02033 -0.022247 -0.121654 -0.021046 -0.010807 -0.16115 -0.022959 -0.030884 -0.1634 -0.029038 -0.037487 -0.206713 -0.03026 -0.050994 -0.166212 -0.032675 -0.064246 -0.126926 -0.035877 -0.074808 -0.088427 -0.048094 -0.089411 -0.089855 -0.040517 -0.087569 -0.059881 -0.051296 -0.100298 -0.037443 -0.035125 -0.08673 -0.04151 -0.028149 -0.086287 -0.034555 -0.020779 -0.046423 -0.035166 -0.025827 -0.040847 -0.041556 --5.2e-05 -0.062188 0.00548 -0.006463 -0.067002 -0.005033 -0.012015 -0.073985 -0.017958 -0.011292 -0.086761 -0.001085 -0.019728 -0.107513 0.001326 --0.351987 0.047534 -0.115422 --0.14288 0.119413 -0.96321 --0.164423 0.119848 -0.961836 --0.124308 0.111749 -0.956052 --0.124703 0.115167 -0.964728 --0.131241 0.115405 -0.971237 --0.146413 0.109641 -0.974611 --0.154631 0.117995 -0.971237 --0.177461 0.114153 -0.9722 --0.183398 0.103486 -0.976057 --0.196445 0.10383 -0.971959 --0.209582 0.088665 -0.970754 --0.205996 0.096639 -0.964969 --0.206554 0.088005 -0.954943 --0.198745 0.100946 -0.957016 --0.186213 0.111705 -0.961681 --0.101613 0.044489 0.508518 --0.091777 0.062148 0.497244 --0.115934 0.045627 0.502282 --0.065327 0.068468 0.494147 --0.080758 0.081006 0.485764 --0.072828 0.096906 0.474952 --0.094039 0.092566 0.477121 --0.105515 0.105185 0.467462 --0.114767 0.087024 0.475675 --0.128024 0.080515 0.470854 --0.129064 0.06286 0.483867 --0.141192 0.04135 0.481454 --0.131264 0.042973 0.496583 --0.086956 0.0366 0.517973 --0.077142 0.031594 0.526258 --0.062917 0.040509 0.517528 --0.042954 0.041937 0.516578 --0.051125 0.05472 0.504975 --0.037893 0.067004 0.494885 -0.093484 -0.097607 0.529183 -0.073719 -0.095196 0.538101 -0.094082 -0.086854 0.544358 -0.063594 -0.088335 0.548857 -0.062627 -0.07834 0.556719 -0.043104 -0.080268 0.563468 -0.034656 -0.067018 0.583958 --0.159365 0.009212 -0.972683 --0.138816 0.025941 -0.973647 --0.141228 -0.000439 -0.968586 --0.138367 0.050982 -0.977021 --0.124538 -0.006661 -0.956534 --0.130607 -0.004067 -0.963041 --0.129136 -0.034829 -0.963282 --0.136905 -0.046366 -0.96979 --0.13199 -0.067156 -0.975575 --0.148774 -0.059115 -0.97437 --0.161709 -0.072079 -0.977504 --0.16425 -0.051076 -0.974611 --0.175649 -0.042919 -0.972683 --0.186474 -0.009328 -0.973165 --0.175349 -0.002784 -0.973887 --0.183278 0.02446 -0.975575 --0.203258 0.038845 -0.975816 --0.188311 0.049937 -0.977263 --0.188748 0.069427 -0.977986 --0.16395 0.05973 -0.977745 --0.136642 0.068833 -0.977745 -0.020641 0.106222 0.68288 -0.010145 0.114881 0.68217 --0.029 -0.004162 0.808506 --0.018674 -0.009285 0.810771 --0.027318 0.00999 0.810749 --0.009358 -0.027252 0.807329 --0.009189 -0.013674 0.811803 -0.000182 -0.009914 0.812958 --0.008739 -0.00082 0.81441 --0.026137 0.022133 0.811998 --0.034725 0.029576 0.807259 --0.036296 0.04573 0.803895 --0.043479 0.035008 0.801776 --0.050999 0.040011 0.794549 --0.046173 0.020569 0.800655 --0.049765 0.005415 0.797041 --0.039102 0.000983 0.803895 --0.030572 -0.017744 0.803895 --0.033041 -0.031611 0.797041 --0.021032 -0.035161 0.800678 --0.010705 -0.050268 0.794618 --0.010144 -0.039546 0.801847 -0.000295 -0.037069 0.804011 --0.129301 0.086215 -0.97678 --0.133015 -0.094692 0.454036 --0.160025 -0.094814 0.509594 --0.154706 -0.098454 0.492353 --0.167264 -0.097792 0.475314 --0.148605 -0.096707 0.473117 --0.143549 -0.091627 0.451185 --0.072193 0.039441 0.677409 --0.069623 0.032875 0.660902 --0.072019 0.033374 0.670516 --0.073593 0.030982 0.680927 --0.074948 0.036613 0.688621 --0.071575 0.047963 0.690054 --0.070167 0.058917 0.70771 --0.068208 0.061066 0.690866 --0.063995 0.074404 0.689656 --0.065714 0.069965 0.676427 --0.063559 0.081997 0.666975 --0.065384 0.069769 0.659252 --0.062969 0.074671 0.643834 --0.066039 0.05651 0.649292 --0.065441 0.044988 0.639524 --0.067935 0.038771 0.651999 --0.067598 0.025852 0.654161 -0.006898 0.113222 0.666528 -0.019509 0.109191 0.657762 -0.012014 0.113397 0.650796 --0.346918 0.12142 -0.117927 --0.354088 0.121599 -0.124121 --0.344419 0.115564 -0.103046 --0.34972 0.117279 -0.110488 -0.127701 -0.017526 -0.914112 -0.134572 -0.007901 -0.908327 -0.143269 -0.019543 -0.89748 -0.143442 -0.032239 -0.887839 -0.151011 -0.035966 -0.894347 -0.156411 -0.049333 -0.89507 -0.158344 -0.037714 -0.906158 -0.165572 -0.036508 -0.92544 --0.02225 0.059766 0.501427 --0.023436 0.07472 0.489154 --0.011365 0.079551 0.484593 --0.024381 0.086541 0.474952 --0.015704 0.092085 0.464828 --0.029443 0.098834 0.459284 --0.029254 0.111213 0.441758 --0.039324 0.10751 0.457116 --0.050653 0.119321 0.4515 --0.054991 0.107029 0.465552 --0.0709 0.112091 0.464033 -0.020423 0.102461 0.608305 -0.035992 0.09389 0.606934 -0.025793 0.095655 0.598077 -0.051857 0.097225 0.633387 -0.04315 0.095805 0.617906 --0.181442 -0.053912 -0.970754 --0.185342 -0.0355 -0.968585 --0.188407 -0.024348 -0.958462 --0.192056 -0.015446 -0.969067 --0.200678 0.00534 -0.970995 --0.205676 0.015018 -0.963765 --0.209533 0.02627 -0.97196 --0.215892 0.046993 -0.970995 --0.020731 -0.081957 -0.028567 --0.015762 -0.05829 -0.029114 --0.012329 -0.073985 -0.017958 --0.020393 -0.046404 -0.035179 --0.012687 -0.019453 -0.035064 --0.011094 -0.036464 -0.029315 --5.2e-05 -0.043146 -0.02248 --0.006793 -0.067002 -0.005033 --0.011606 -0.086761 -0.001326 --0.01016 -0.099053 0.016992 --0.019801 -0.107513 0.000844 --0.03519 -0.122715 0.002285 --0.031337 -0.113581 -0.011936 --0.048416 -0.114586 -0.018931 --0.031544 -0.100799 -0.024715 --0.028404 -0.086268 -0.034568 --0.035628 -0.086704 -0.041528 --0.02362 -0.066445 -0.03714 --0.025699 -0.040813 -0.041578 --0.217927 0.056178 -0.96256 --0.216728 0.068043 -0.96979 --0.204529 0.079384 -0.975334 --0.187076 0.088261 -0.978226 --0.164926 0.099509 -0.977263 --0.140782 0.098259 -0.976539 --0.12071 0.098647 -0.974129 --0.015554 0.023029 0.566772 --0.019233 0.031936 0.534362 --0.006303 0.039781 0.519301 --0.017401 0.043994 0.515556 --0.043958 0.004853 0.609394 --0.051897 0.010736 0.631519 --0.043078 0.012732 0.61846 --0.045175 0.024826 0.613791 --0.029703 0.021295 0.609428 --0.015003 0.027943 0.601931 --0.015397 0.021991 0.592477 --0.005867 0.023916 0.567498 --0.097654 0.15403 0.414501 --0.106021 0.1509 0.422436 --0.114099 0.14311 0.431965 --0.105181 0.145039 0.431965 --0.09795 0.134674 0.443848 --0.094669 0.144955 0.431429 --0.080782 0.140532 0.434579 --0.089942 0.14945 0.421973 --0.084901 0.150248 0.411978 --0.068533 0.142851 0.413384 --0.079138 0.149118 0.400054 --0.068103 0.145349 0.392723 --0.08706 0.150934 0.389497 --0.094948 0.154109 0.402241 --0.104885 0.155597 0.405945 --0.123565 0.146136 0.401605 --0.113336 0.154226 0.409681 --0.122239 0.148005 0.414019 --0.081095 0.052591 0.072127 --0.082705 0.052338 0.053784 --0.089286 0.058018 0.03567 --0.098251 0.065585 0.010731 -0.28925 0.045602 -0.009567 -0.292529 0.034588 -0.011436 -0.284719 0.030641 0.007874 -0.298173 0.032736 -0.023181 -0.302759 0.030528 -0.010877 -0.312972 0.037178 -0.005232 -0.301493 0.022318 0.010571 -0.296961 0.01204 0.043347 -0.286506 0.011417 0.039962 -0.270972 0.000318 0.073557 -0.278498 0.015939 0.038776 -0.271804 0.025941 0.039055 -0.254717 0.020693 0.073311 -0.269021 0.037206 0.041245 -0.267648 0.047465 0.045042 -0.280242 0.053479 0.012775 -0.288229 0.065783 -0.000674 -0.287556 0.057176 -0.006856 -0.289871 0.057957 -0.019069 -0.295948 0.074193 -0.034474 -0.293578 0.055729 -0.035571 -0.299659 0.054995 -0.052561 -0.298705 0.042921 -0.044444 -0.310441 0.034345 -0.058644 -0.305132 0.033182 -0.038279 -0.235805 -0.050145 0.243698 -0.245376 -0.048187 0.236911 -0.246224 -0.046905 0.24834 -0.242491 -0.046658 0.225826 -0.255 -0.04119 0.231842 -0.266692 -0.032272 0.222715 -0.26212 -0.034758 0.239813 -0.263623 -0.029623 0.251835 -0.256126 -0.03921 0.250514 -0.246606 -0.046318 0.264945 -0.245716 -0.046049 0.285837 -0.236221 -0.052131 0.281348 -0.22488 -0.058924 0.302204 -0.225541 -0.054724 0.276795 -0.214081 -0.053728 0.27235 -0.225508 -0.052151 0.25575 -0.224536 -0.049797 0.239557 -0.214015 -0.045365 0.236305 -0.221176 -0.04779 0.228147 -0.216208 -0.041047 0.217972 -0.229561 -0.046609 0.221203 -0.240155 -0.043871 0.209954 --0.059417 0.009418 0.787695 --0.053243 -0.009739 0.790062 --0.05706 -0.024749 0.778846 --0.045051 -0.028037 0.790062 --0.035286 -0.044335 0.787695 --0.180683 -0.041219 -0.578831 --0.175537 -0.031146 -0.553571 --0.176602 -0.025983 -0.580031 --0.172605 -0.036647 -0.528534 --0.168859 -0.022753 -0.529288 --0.161242 -0.015909 -0.507445 --0.160737 -0.010161 -0.529577 --0.14849 -0.00131 -0.529345 --0.159038 -0.005362 -0.554497 --0.154699 -0.003267 -0.581093 --0.168224 -0.012792 -0.580668 --0.175483 -0.022229 -0.612273 --0.169777 -0.021693 -0.649493 --0.17872 -0.03387 -0.646851 --0.179299 -0.047129 -0.689553 --0.183473 -0.048939 -0.644727 --0.185221 -0.064888 -0.643884 --0.184318 -0.052534 -0.60895 --0.181709 -0.057628 -0.577707 --0.179005 -0.07687 -0.577528 --0.177507 -0.063217 -0.551419 --0.170991 -0.068748 -0.52619 --0.173551 -0.05231 -0.527506 --0.168785 -0.042328 -0.506102 -0.092154 -0.05311 0.557856 -0.110348 -0.062122 0.551578 -0.118544 -0.049829 0.550614 -0.091429 -0.071075 0.555353 -0.112759 -0.07562 0.547722 -0.124087 -0.083092 0.539767 -0.123488 -0.090952 0.530619 -0.147208 -0.082727 0.532732 -0.163751 -0.086596 0.523111 -0.167215 -0.074061 0.532267 -0.181355 -0.068716 0.527983 -0.169725 -0.059203 0.536731 -0.16996 -0.044296 0.538715 -0.157488 -0.047446 0.542037 -0.139754 -0.03396 0.544878 -0.138367 -0.044594 0.546776 -0.117945 -0.037202 0.550143 -0.109874 -0.02361 0.549189 -0.093007 -0.030242 0.555939 -0.070108 -0.028687 0.562213 -0.069634 -0.03808 0.564859 -0.054196 -0.045559 0.577154 -0.068181 -0.04832 0.566305 -0.066004 -0.057483 0.565488 -0.064556 -0.067734 0.563709 -0.140035 -0.093514 -0.092145 -0.148605 -0.086131 -0.134493 -0.157508 -0.080765 -0.09277 -0.155489 -0.074654 -0.177276 -0.169056 -0.0578 -0.17712 -0.175859 -0.05251 -0.134745 -0.178984 -0.044802 -0.092636 -0.171196 -0.063998 -0.093178 -0.162918 -0.075809 -0.054259 -0.162813 -0.069618 -0.0175 -0.15164 -0.088465 -0.018245 -0.141603 -0.099259 0.009952 -0.13688 -0.101658 -0.018535 -0.1209 -0.109584 -0.023743 -0.130686 -0.101752 -0.053921 -0.117814 -0.101416 -0.091734 -0.091184 -0.103428 -0.091451 -0.019782 0.089341 0.584488 --0.056232 0.093284 0.679412 --0.072636 0.057242 0.724416 --0.066049 0.072554 0.719436 --0.059213 0.086421 0.724422 --0.059279 0.085457 0.712483 --0.051306 0.093238 0.699401 --0.058462 0.085324 0.699207 --0.047663 0.097322 0.687755 -0.005499 0.057805 -0.010951 -0.012745 0.065075 0.010335 -0.015692 0.053492 -0.012164 -0.007199 0.074492 0.042059 -0.026725 0.070387 0.044149 -0.042585 0.072901 0.084773 -0.044231 0.062609 0.049328 -0.056432 0.052674 0.046995 -0.041781 0.04931 0.017394 -0.036587 0.035342 -0.005884 -0.027221 0.045504 -0.010286 -0.018781 0.04154 -0.022529 -0.019554 0.029015 -0.029508 -0.012607 0.034629 -0.03064 --6.1e-05 0.037879 -0.031299 --6.3e-05 0.049097 -0.021972 --0.005372 0.057808 -0.010711 --0.015317 0.053505 -0.011692 --0.01235 0.065084 0.010811 --0.026311 0.0704 0.044621 --0.007026 0.074499 0.042295 --3e-05 0.078582 0.077556 -0.034475 0.02252 -0.016341 -0.027855 0.006828 -0.029477 -0.025882 0.020158 -0.027721 -0.070069 0.052493 0.067051 -0.063705 0.049027 0.040299 -0.1708 -0.048786 -0.016719 -0.178692 -0.035781 -0.052935 -0.15983 -0.064509 0.01593 -0.181799 -0.02331 -0.091181 -0.179477 7.1e-05 -0.089955 -0.172651 0.023084 -0.088665 -0.151966 -0.0452 0.070189 -0.15501 -0.056338 0.04692 -0.149717 -0.077178 0.043467 --0.060997 0.014244 0.640455 -0.029437 0.020503 0.56901 -0.036427 0.012307 0.596246 -0.027474 0.026888 0.545481 -0.022206 0.024118 0.555512 -0.014975 0.022983 0.566758 -0.005541 0.023894 0.567491 -0.051751 0.010618 0.631502 -0.042587 0.012608 0.618431 -0.04362 0.004713 0.609833 -0.047187 -0.006745 0.609202 -0.044487 0.007977 0.591049 -0.04478 0.004342 0.572568 -0.039869 0.016341 0.56975 -0.035477 0.023909 0.55068 -0.231228 0.028966 0.248357 -0.232147 0.037525 0.231595 -0.219558 0.028188 0.243842 -0.247727 0.043746 0.215549 -0.231658 0.043805 0.209803 -0.231425 0.048107 0.183461 -0.217631 0.037675 0.203057 -0.207163 0.026016 0.196042 -0.206654 0.026409 0.219429 -0.200283 0.013862 0.233726 -0.208092 0.023203 0.238421 -0.207884 0.020504 0.256487 -0.197155 0.012116 0.273302 -0.206211 0.017357 0.277512 -0.203911 0.01663 0.302668 -0.21645 0.019107 0.281786 -0.22791 0.018111 0.286231 -0.229794 0.023032 0.265276 -0.244364 0.024711 0.252074 -0.25506 0.017995 0.254749 -0.260255 0.027033 0.238936 -0.274955 0.025766 0.220281 -0.262699 0.037508 0.219172 -0.263562 0.047989 0.194952 --0.055273 -0.045936 -0.519443 --0.057572 -0.052139 -0.497425 --0.056619 -0.062531 -0.518394 --0.061513 -0.042844 -0.478046 --0.062629 -0.056837 -0.477045 --0.067662 -0.05984 -0.460489 --0.06743 -0.071226 -0.476496 --0.075061 -0.082434 -0.476421 --0.067654 -0.082092 -0.4964 --0.071384 -0.093884 -0.51898 --0.0619 -0.078097 -0.517919 --0.057834 -0.072877 -0.544067 --0.062274 -0.085678 -0.573733 --0.056388 -0.067092 -0.573757 --0.059578 -0.062913 -0.61318 --0.054796 -0.050683 -0.574783 --0.057039 -0.034769 -0.576001 --0.054895 -0.039873 -0.546142 --0.057761 -0.030459 -0.520797 --0.064086 -0.016652 -0.522259 --0.0623 -0.022607 -0.50001 --0.068868 -0.018314 -0.480453 --0.063753 -0.02879 -0.479277 --0.065459 -0.034827 -0.462233 --0.109923 0.097139 0.355728 --0.090865 0.091656 0.35474 --0.071542 0.095326 0.351671 --0.056188 0.093387 0.339369 --0.077145 0.084673 0.334731 --0.079236 0.078124 0.313693 --0.100936 -0.085951 -0.260342 --0.120891 -0.085269 -0.26252 --0.108267 -0.077994 -0.29988 --0.131186 -0.08996 -0.220553 --0.137402 -0.07889 -0.262806 --0.152178 -0.065241 -0.261914 --0.141676 -0.067763 -0.300371 --0.143641 -0.056073 -0.33192 --0.129503 -0.065865 -0.335172 --0.121419 -0.06514 -0.367938 --0.112603 -0.069717 -0.334966 --0.095862 -0.069837 -0.333635 --0.091719 -0.066681 -0.364809 --0.079744 -0.06832 -0.330753 --0.066743 -0.06431 -0.326443 --0.072427 -0.072997 -0.293351 --0.062856 -0.075002 -0.253308 --0.081215 -0.082472 -0.256694 --0.088531 -0.093675 -0.217257 --0.074539 -0.096574 -0.174349 --0.097136 -0.100976 -0.176111 --0.106882 -0.103202 -0.134252 --0.119835 -0.097944 -0.177229 --0.139993 -0.088744 -0.177323 -0.018239 0.107256 0.671512 --0.063667 -0.030882 -0.613891 --0.063363 -0.02034 -0.57735 --0.07406 -0.008564 -0.578235 --0.067545 -0.011638 -0.548953 --0.073747 -0.006272 -0.523751 --0.08742 0.000865 -0.525118 --0.080661 -0.003753 -0.502829 --0.085059 -0.001355 -0.479605 --0.077569 -0.009841 -0.481781 --0.076309 -0.016679 -0.464386 -0.000655 0.093766 0.458566 --0.002688 0.085095 0.474469 -0.01032 0.079544 0.484598 -0.022375 0.074701 0.489167 -0.023779 0.086522 0.474965 -0.029284 0.098801 0.459307 -0.016284 0.094475 0.458816 -0.003999 0.097856 0.43977 -0.008865 0.104204 0.421525 --0.001514 0.098828 0.420243 --0.002447 0.094977 0.395652 --0.010454 0.103501 0.414762 --0.019698 0.112569 0.404978 --0.021112 0.113229 0.423026 --0.034872 0.124633 0.422118 --0.108977 -0.07772 -0.938697 --0.109259 -0.095338 -0.938938 --0.107241 -0.088549 -0.953641 --0.113633 -0.098839 -0.923753 --0.116069 -0.109289 -0.937974 --0.115738 -0.112744 -0.967861 --0.120446 -0.104066 -0.975093 --0.111521 -0.09868 -0.966175 --0.110599 -0.079589 -0.965452 --0.118576 -0.073285 -0.973405 --0.114012 -0.059077 -0.964005 --0.119658 -0.041412 -0.959667 --0.117299 -0.048889 -0.948579 --0.122066 -0.040734 -0.934599 --0.115623 -0.05857 -0.935082 --0.113042 -0.071006 -0.923031 --0.116876 -0.070528 -0.907364 --0.114063 -0.084261 -0.914594 --0.114361 -0.096332 -0.891874 -0.092611 -0.019001 0.551748 -0.121772 -0.017925 0.544638 -0.115293 -0.000121 0.538382 -0.097639 0.005006 0.540291 -0.071429 0.018691 0.538825 -0.0619 0.007477 0.546542 -0.052729 -0.000966 0.55956 -0.070088 -0.007715 0.552091 -0.070817 -0.020132 0.558359 -0.054188 -0.023873 0.571133 --0.138607 0.02768 -0.35295 --0.145511 0.020286 -0.352691 --0.152296 0.016528 -0.338967 --0.143987 0.017902 -0.368736 --0.136642 0.01861 -0.394138 --0.143563 0.00789 -0.390521 --0.137355 0.00316 -0.416302 --0.146884 -0.00545 -0.387504 --0.144468 -0.015412 -0.390165 --0.15644 -0.015421 -0.353648 --0.161971 -0.027536 -0.325287 --0.165474 -0.010869 -0.323954 --0.17139 -0.004353 -0.293696 --0.163127 0.005355 -0.322302 --0.156493 0.021972 -0.321189 --0.159237 0.031684 -0.291354 --0.14683 0.03588 -0.320043 --0.134195 0.048864 -0.318919 --0.133129 0.041051 -0.336372 --0.119072 0.0442 -0.348832 --0.130775 0.036182 -0.351647 --0.126896 0.038675 -0.36124 --0.077792 -0.020144 -0.448556 --0.069431 -0.035049 -0.448115 --0.326873 0.070892 -0.095212 --0.323667 0.076354 -0.088293 --0.325844 0.086822 -0.08913 --0.303517 0.08504 -0.050481 --0.305062 0.071913 -0.060507 --0.298297 0.05325 -0.051365 --0.335226 0.071435 -0.105031 --0.336521 0.091323 -0.101414 -0.095477 0.114936 0.354458 -0.074906 0.108128 0.353034 -0.083691 0.118559 0.354297 -0.055065 0.107161 0.357513 -0.036546 0.108676 0.369588 -0.052728 0.116812 0.361744 -0.062169 0.129533 0.366115 -0.070881 0.123564 0.35888 -0.088753 0.131816 0.363311 -0.090854 0.143024 0.374301 -0.100805 0.141939 0.376978 -0.109457 0.13928 0.37762 -0.118765 0.132885 0.374621 -0.109192 0.125534 0.364114 -0.109846 0.110711 0.356521 -0.12183 0.107697 0.360592 -0.109803 0.0971 0.355756 -0.113684 0.08144 0.361935 -0.090746 0.091617 0.354768 -0.071427 0.095289 0.351696 -0.139244 -0.091472 0.060615 -0.142666 -0.093081 0.038234 -0.130994 -0.108076 0.032687 --0.01243 0.098552 0.594381 --0.025239 0.095565 0.597988 --0.075025 0.108167 0.353006 --0.055184 0.107199 0.357485 --0.052847 0.11685 0.361717 --0.036657 0.108709 0.369566 --0.025423 0.111393 0.38572 --0.024164 0.101644 0.37025 --0.014121 0.093803 0.35882 --0.031916 0.096268 0.349002 --0.04652 0.092123 0.322665 --0.040688 0.086532 0.306737 --0.061795 0.083175 0.305975 --0.000933 0.105174 0.600462 --0.035943 -0.066474 0.205451 --0.0186 -0.064312 0.200394 --0.08577 -0.092697 -0.476498 --0.097479 -0.099416 -0.476688 --0.090447 -0.106019 -0.497618 --0.101793 -0.091054 -0.460571 --0.110883 -0.10191 -0.477474 --0.123514 -0.099711 -0.478696 --0.118719 -0.11206 -0.49993 --0.128373 -0.119869 -0.525079 --0.112883 -0.122273 -0.52364 --0.106856 -0.129589 -0.550984 --0.098014 -0.118725 -0.522114 --0.083467 -0.107549 -0.52012 --0.077161 -0.107657 -0.546619 --0.082954 -0.081004 -0.460122 --0.081541 -0.070322 -0.446183 --0.092255 -0.079879 -0.45023 --0.104304 -0.079581 -0.445782 --0.155674 -0.074622 -0.177299 --0.16916 -0.057773 -0.177139 --0.161643 -0.062189 -0.219637 --0.16425 -0.050033 -0.260919 -0.127133 0.121437 0.370812 -0.139715 0.083369 0.375361 -0.134388 0.103125 0.368146 --0.068844 0.012849 0.774859 --0.063682 0.02886 0.78246 --0.050887 0.056595 0.78944 --0.058855 0.0443 0.785701 --0.064074 0.048032 0.775737 --0.060763 0.065345 0.769511 --0.069069 0.051193 0.763654 --0.071972 0.053638 0.751696 --0.076084 0.035166 0.755922 --0.080451 0.020839 0.742337 --0.076253 0.015994 0.760403 --0.07311 -0.002734 0.762646 --0.075242 -0.016457 0.745698 --0.067498 -0.021175 0.764266 --0.059978 -0.038044 0.764266 --0.009606 0.112575 0.67871 --0.015597 0.118906 0.696268 --0.00595 0.117643 0.685904 -0.226135 -0.009565 0.137964 -0.240308 -0.009492 0.116998 -0.238392 -0.021308 0.139073 -0.245799 0.004574 0.098227 -0.25536 -0.0058 0.098753 -0.268712 -0.011363 0.101984 -0.281644 -0.011411 0.106607 -0.268632 -0.021294 0.123851 -0.269432 -0.027605 0.148441 -0.253363 -0.027546 0.142696 -0.237528 -0.031788 0.163293 -0.237937 -0.038374 0.189058 -0.222965 -0.032136 0.185436 -0.211831 -0.032069 0.2031 -0.210709 -0.020394 0.184326 -0.203562 -0.00544 0.185949 -0.213017 -0.008303 0.161075 -0.218989 0.005389 0.139586 -0.217724 0.021935 0.143763 -0.227089 0.01972 0.120432 -0.237932 0.031715 0.104265 -0.239727 0.018832 0.100041 -0.130115 0.072367 -0.087024 -0.108208 0.07774 -0.08661 -0.095777 0.07914 -0.125732 --0.345718 0.120033 -0.130569 --0.3545 0.113702 -0.130478 -0.162066 0.04364 -0.087898 -0.14782 0.060596 -0.087193 -0.014973 -0.081226 0.124942 -0.1861 -0.077551 0.515917 -0.193564 -0.061189 0.519585 -0.202622 -0.050908 0.506478 -0.194808 -0.044467 0.52252 -0.189091 -0.027347 0.524711 -0.182835 -0.037088 0.532901 -0.171853 -0.028443 0.534873 -0.159701 -0.094919 0.51001 -0.175026 -0.088818 0.512794 -0.18688 -0.085619 0.498698 -0.112989 -0.099264 0.514738 -0.119816 -0.10079 0.499372 -0.12111 -0.102352 0.472425 -0.139744 -0.098748 0.499938 -0.154484 -0.098526 0.492574 --0.057988 0.086278 0.737506 --0.05054 0.098287 0.724297 --0.040337 0.109009 0.725419 --0.04241 0.106365 0.71133 --0.034774 0.10754 0.698319 --0.043208 0.101822 0.699208 --0.038916 0.101242 0.690966 --0.037643 -0.056629 0.774859 --0.024399 -0.058749 0.782484 --0.012425 -0.070703 0.772788 --0.011827 -0.061132 0.78577 -0.000295 -0.058512 0.789555 --0.060763 -0.048909 0.745698 --0.050326 -0.053627 0.762646 --0.039102 -0.066493 0.760403 -0.059026 0.08642 0.737646 -0.06025 0.086563 0.724562 -0.067086 0.072696 0.719576 -0.060317 0.085599 0.712624 -0.040503 0.101825 0.693342 -0.032757 0.103531 0.684267 --0.0291 -0.018684 -0.042895 --0.02906 -0.004299 -0.056925 --0.026374 -0.038433 -0.059194 --0.028475 -0.055575 -0.088465 --0.02343 -0.033688 -0.086005 --0.020775 -0.022888 -0.123089 --0.024652 -0.013246 -0.084268 --0.029232 0.005796 -0.083617 --0.029056 0.016608 -0.121626 --0.015647 0.006899 -0.037608 --0.017185 -0.003649 -0.036607 --0.008532 -0.002853 -0.039356 --0.058424 0.097628 0.648 --0.054499 0.096907 0.633642 --0.059248 0.088844 0.638419 --0.05795 0.078018 0.627512 --0.007443 -0.071435 0.681152 --0.116739 -0.136397 -0.580844 --0.100306 -0.131872 -0.579544 --0.097219 -0.130442 -0.616268 --0.085158 -0.121117 -0.577172 --0.072117 -0.104671 -0.575223 --0.342027 0.053983 -0.116465 --0.062924 0.055834 0.198102 --0.06222 0.057004 0.16879 --0.062482 0.064178 0.147523 --0.056597 0.070898 0.121654 --0.074757 0.060879 0.130228 --0.084062 0.055142 0.11443 --0.086826 0.049633 0.133312 --0.050465 0.066241 0.237 --0.036428 0.078589 0.278784 -0.130528 0.070492 -0.168451 -0.140569 0.067337 -0.127511 -0.107643 0.076923 -0.167086 -0.083619 0.075084 -0.165215 -0.073121 0.068867 -0.205393 -0.105061 0.144999 0.431993 -0.113979 0.143071 0.431993 -0.118559 0.131743 0.442671 --0.010837 0.077735 0.109421 -0.01077 0.077721 0.10943 -0.022527 0.074634 0.135932 -0.03386 0.076146 0.114557 -0.056505 0.070872 0.121672 --0.107441 -0.101702 0.109968 --0.099315 -0.114582 0.096708 --0.089676 -0.125385 0.083379 --0.069075 -0.050206 -0.446984 --0.073878 -0.060366 -0.446474 --0.074237 -0.059456 -0.424272 --0.03394 0.076172 0.114539 --0.042417 0.072921 0.085 --0.014258 -0.11014 0.033141 --0.157132 -0.090422 0.430384 --0.140529 -0.081659 0.429758 --0.145454 0.048158 0.463138 --0.017373 -0.070336 0.667195 --0.127703 -0.087862 -0.461567 --0.13624 -0.094361 -0.479914 --0.146737 -0.087653 -0.48091 --0.145564 -0.100101 -0.501844 --0.155102 -0.101739 -0.525344 --0.142492 -0.11326 -0.525774 --0.138801 -0.12618 -0.553256 --0.148871 -0.128621 -0.581038 --0.133194 -0.135391 -0.581677 --0.127663 -0.138176 -0.616978 --0.150984 -0.068803 0.390182 --0.078496 0.031985 0.709579 --0.080563 0.025413 0.7244 --0.077425 0.041899 0.723785 --0.073202 0.055797 0.737495 --0.06539 0.070966 0.745601 --0.056146 0.084691 0.751708 --0.018935 0.105176 0.619158 --0.007126 0.109562 0.629151 --0.051168 0.076338 0.775737 --0.054422 0.081515 0.76366 --0.046346 0.095969 0.755939 --0.034454 0.104531 0.76042 --0.037984 0.108279 0.742859 --0.029419 0.116585 0.725169 --0.047298 0.024671 -0.310259 --0.037849 0.017816 -0.27972 --0.040346 0.008987 -0.311486 --0.038052 0.027864 -0.243939 --0.030119 0.00961 -0.244461 --0.02382 4.2e-05 -0.203348 --0.027026 -0.008049 -0.245536 --0.029351 -0.026807 -0.247007 --0.031831 -0.016508 -0.282364 --0.038859 -0.023333 -0.315602 --0.037421 -0.00768 -0.313056 --0.044946 0.004605 -0.332387 --0.053967 0.004087 -0.358133 --0.054656 0.017964 -0.342715 --0.065584 0.033024 -0.344384 --0.056152 0.029739 -0.330176 --0.058364 0.04014 -0.310503 --0.070722 0.05147 -0.311389 --0.060798 0.050331 -0.280418 --0.06604 0.059635 -0.245537 --0.049933 0.044911 -0.244394 --0.039882 0.038028 -0.203344 --0.038989 -0.073927 0.742832 --0.026679 -0.077612 0.752926 --0.013508 -0.085225 0.741744 --0.013247 -0.079702 0.757686 -0.000222 -0.077653 0.76359 --0.123761 -0.022861 -0.937492 --0.128272 -0.030546 -0.92279 --0.132998 -0.017576 -0.914112 --0.133664 -0.033246 -0.905194 --0.132777 -0.045326 -0.894106 --0.124311 -0.054024 -0.87316 --0.124777 -0.059983 -0.896517 --0.11425 -0.070799 -0.880902 --0.105875 -0.070266 -0.84386 --0.105368 -0.07996 -0.844737 --0.09669 -0.083735 -0.796708 --0.108292 -0.09176 -0.848477 --0.114245 -0.103894 -0.85013 --0.075698 -0.022685 -0.438931 --0.066752 -0.020972 -0.416209 --0.093239 0.007916 -0.466902 --0.086006 0.003262 -0.460468 --0.079904 0.004909 -0.444729 --0.07522 -0.005856 -0.439366 --0.066779 0.011585 -0.400071 --0.061683 0.002995 -0.386192 --0.058576 -0.009739 -0.386374 --0.060099 -0.025929 -0.40223 --0.214472 0.06587 -0.955087 --0.211744 0.045888 -0.955087 --0.205851 0.025451 -0.955328 -0.210488 0.024791 0.16943 -0.202298 0.011107 0.190126 -0.197022 -0.002238 0.208593 -0.195264 -0.013418 0.226204 -0.195517 0.000125 0.228806 -0.193247 0.002539 0.248068 -0.185983 -0.008188 0.266481 -0.190326 0.003492 0.269578 -0.184997 0.005073 0.293798 -0.06669 0.059844 -0.245514 -0.0619 0.051035 -0.280406 -0.050798 0.045614 -0.24438 -0.039176 0.029063 -0.243934 --0.00915 0.088447 0.337107 --0.005564 0.08412 0.313582 --0.021113 0.08532 0.31136 -0.355453 0.089966 -0.127158 -0.350899 0.086456 -0.117554 -0.350983 0.097265 -0.137094 -0.072647 0.03954 0.677462 -0.072418 0.04809 0.690165 -0.075207 0.036698 0.688642 -0.073504 0.031041 0.680896 --0.178961 -0.090729 -0.609801 --0.172446 -0.097083 -0.578269 --0.162075 -0.115543 -0.579879 --0.164614 -0.10158 -0.551666 --0.164655 -0.086899 -0.525697 --0.163951 -0.073973 -0.503455 --0.155571 -0.076365 -0.482167 --0.160899 -0.062998 -0.48358 --0.157142 -0.052747 -0.467235 --0.163173 -0.047792 -0.48491 --0.161891 -0.034728 -0.485791 --0.06518 -0.059918 -0.358386 --0.054486 -0.054171 -0.322075 --0.045494 -0.039634 -0.317862 --0.046159 -0.051222 -0.286305 --0.037043 -0.045221 -0.248688 --0.048844 -0.063367 -0.25063 --0.051682 -0.075463 -0.211308 --0.041886 -0.070545 -0.169579 --0.056049 -0.085832 -0.171786 --0.060066 -0.09468 -0.131873 -0.254005 -0.038433 0.194803 -0.269666 -0.031907 0.174783 -0.268033 -0.032302 0.201549 -0.278501 -0.020645 0.208564 --0.073666 0.038526 -0.352224 --0.076882 0.044216 -0.344481 --0.090345 0.049949 -0.34488 --0.079808 0.052262 -0.33116 --0.086114 0.059059 -0.313538 --0.101546 0.061002 -0.315492 --0.094518 0.067326 -0.284767 --0.106388 0.071042 -0.250493 --0.084369 0.069042 -0.247735 --0.073014 0.068797 -0.205774 --0.065091 0.019151 0.646697 --0.068711 0.018789 0.660834 --0.022373 0.108586 0.644445 --0.013195 0.113326 0.640083 -0.000977 0.116141 0.634141 -0.004099 0.080202 0.281578 -0.005222 0.084113 0.313587 --1.8e-05 0.091348 0.36624 -0.014112 0.093784 0.358834 -0.009048 0.088435 0.337116 -0.020807 0.0853 0.31137 -0.040382 0.086506 0.306746 -0.036479 0.078562 0.278794 -0.051479 0.066209 0.237014 -0.036242 0.07127 0.236575 -0.035281 0.068899 0.193525 -0.019448 0.074097 0.237143 -0.05561 -0.052972 -0.32207 -0.06282 0.040489 0.517542 -0.051013 0.054687 0.504998 -0.042858 0.041918 0.516592 -0.065208 0.06843 0.494175 -0.037299 0.066972 0.494908 -0.02119 0.059746 0.501441 -0.02674 0.044809 0.514452 -0.038298 0.035426 0.525313 -0.071308 0.027007 0.533041 -0.077062 0.031587 0.526262 -0.086859 0.03658 0.517987 -0.11124 0.022703 0.520396 -0.101501 0.044457 0.508542 -0.115815 0.045589 0.502309 -0.091657 0.06211 0.497272 -0.080638 0.080966 0.485791 -0.07637 0.070151 0.293696 -0.087457 0.048722 0.283502 -0.077093 0.059473 0.2667 -0.099334 0.030242 0.29242 -0.092998 0.032041 0.26222 -0.090592 0.023644 0.230841 -0.082636 0.042793 0.245419 -0.067806 0.058189 0.233638 -0.064536 0.055795 0.198126 -0.04641 0.092096 0.322679 -0.061535 0.083149 0.305984 -0.079014 0.078099 0.313706 -0.07703 0.084647 0.334748 -0.091849 0.065857 0.328967 -0.108059 0.051446 0.346879 -0.101082 0.039429 0.317875 -0.110719 0.01355 0.310582 -0.047532 -0.049776 -0.286305 -0.046867 -0.038187 -0.317862 -0.040231 -0.021887 -0.315602 --0.039663 0.064887 0.794549 --0.04651 0.07132 0.785701 --0.038541 0.084186 0.78246 --0.032592 0.059025 0.801776 --0.024175 0.069175 0.800655 --0.007675 0.0712 0.803918 --0.015757 0.080755 0.797041 --0.00745 0.092215 0.790085 --0.023277 0.089332 0.787695 --0.029903 0.097925 0.774864 --0.13845 3.5e-05 -0.507078 --0.133365 0.003981 -0.52854 --0.118126 0.005381 -0.527499 --0.127763 0.005296 -0.553194 --0.121695 0.003929 -0.580238 --0.13855 0.0021 -0.580595 --0.148539 -0.004026 -0.614203 --0.141288 -0.008128 -0.65282 --0.156359 -0.013014 -0.650997 -0.066578 -0.099193 -0.090488 --0.020935 0.110536 0.762662 --0.074793 -0.027179 0.725758 --0.078767 -0.010184 0.725264 --0.080834 0.008385 0.724145 --0.078249 -0.000118 0.707505 --0.076155 0.000473 0.689176 --0.067835 -0.042762 0.725758 --0.059417 -0.056343 0.725758 --0.000277 0.032619 0.537537 -0.005267 0.039781 0.519301 --0.051985 -0.063156 0.706182 --0.048867 -0.068495 0.725135 --0.037495 -0.077989 0.725432 -0.024103 0.101619 0.370269 -0.0319 0.096241 0.34902 -0.056128 0.09336 0.339387 --0.109701 0.006933 -0.501361 --0.102506 0.004261 -0.526311 --0.095928 0.003411 -0.551193 --0.088769 -0.000652 -0.579106 --0.105482 0.003165 -0.579427 --0.115801 0 -0.614731 --0.111182 -0.006827 -0.655254 --0.125404 -0.005933 -0.653705 --0.179507 -0.072641 -0.973405 --0.179736 -0.06291 -0.959185 --0.177626 -0.071836 -0.943035 --0.176403 -0.051299 -0.941831 --0.170692 -0.036558 -0.92544 --0.181173 -0.031149 -0.943277 --0.188787 -0.011566 -0.948821 --0.184485 0.00236 -0.937492 --0.197436 0.007013 -0.952195 -0.22727 0.014598 0.31173 -0.238361 0.013681 0.289874 -0.247408 0.005987 0.292578 -0.250942 0.011887 0.271593 -0.260531 0.00639 0.26041 -0.061291 0.014473 0.640484 -0.064319 0.036648 0.631863 -0.064492 0.023752 0.636896 -0.065379 0.01969 0.646737 -0.067876 0.0264 0.654196 -0.046216 -0.02182 0.600769 --0.048478 -0.033459 -0.350673 --0.028058 -0.038793 -0.20707 --0.022763 -0.03191 -0.164472 --0.030063 -0.052021 -0.167285 --0.03312 -0.064886 -0.128361 --0.078161 0.023262 0.691248 --0.080978 0.011701 0.692981 --0.076344 0.010431 0.681702 --0.064465 -0.038248 -0.423846 -0.025319 0.111368 0.385738 -0.303574 0.01443 0.063373 -0.310434 0.028986 0.052327 -0.307717 0.015772 0.090519 -0.314679 0.044944 0.019966 -0.310561 0.042088 0.055983 -0.303049 0.051605 0.07469 -0.306827 0.04219 0.097537 -0.299525 0.043386 0.128354 -0.305899 0.030051 0.126908 -0.304373 0.01851 0.147806 -0.306915 0.016735 0.123546 -0.302576 0.003439 0.11827 -0.295169 -0.009694 0.136612 -0.294152 -0.005943 0.112623 -0.294359 0.000894 0.081565 -0.323461 0.051886 -0.012258 -0.31762 0.048353 -0.001508 -0.314347 0.060279 0.004129 -0.049496 0.065008 0.164657 -0.031795 0.070223 0.160157 -0.017269 0.071324 0.154758 --0.146941 -0.076726 -0.464747 --0.158396 -0.12559 -0.614064 -0.015532 0.006863 -0.037583 -0.015801 0.017143 -0.036113 -0.006115 0.011659 -0.039677 -0.008433 -0.002873 -0.039343 --4.7e-05 0.003617 -0.040655 --0.006218 0.011679 -0.039691 --0.012729 0.034655 -0.030658 --0.12596 0.005521 -0.934599 --0.122568 -0.004845 -0.942795 --0.123125 -0.014672 -0.948338 --0.123788 -0.025068 -0.956534 --0.341135 0.114455 -0.093223 -0.283327 0.067105 0.019638 -0.293591 0.074868 0.000607 -0.303696 0.077585 0.000797 -0.301369 0.085086 -0.0127 -0.302858 0.089175 -0.033159 -0.304878 0.086785 -0.051677 --0.141076 -0.005643 -0.486465 --0.1314 0.000337 -0.482105 --0.127069 -0.001274 -0.460449 --0.123182 0.007158 -0.467277 --0.119739 0.015043 -0.450152 --0.114817 0.010324 -0.473312 --0.10375 0.010983 -0.473422 --0.012278 -0.088021 0.713298 --0.013227 -0.084451 0.698266 --0.026636 -0.078353 0.693522 --0.016851 -0.07652 0.685949 --0.006472 0.093335 0.584256 --0.008884 0.08669 0.576962 --0.018959 0.089208 0.584357 --0.026719 0.077269 0.582716 --0.035274 0.075271 0.589531 --0.043146 0.064831 0.596409 --0.043718 0.076945 0.600527 --0.051105 0.077523 0.612557 --0.046133 0.086744 0.60819 --0.043245 0.096606 0.616926 --0.037863 0.093977 0.607513 -0.075393 0.14152 0.374137 -0.086941 0.150895 0.389525 -0.104766 0.155557 0.405971 -0.055472 0.135163 0.378477 -0.099553 -0.102008 0.418153 -0.12026 -0.100115 0.445981 -0.124401 -0.087157 0.436601 -0.132895 -0.094732 0.454064 -0.14343 -0.091666 0.451211 -0.148486 -0.096747 0.473145 -0.167144 -0.097831 0.475341 -0.082814 -0.092688 0.359965 --0.134686 -0.019262 -0.431128 --0.130583 -0.011949 -0.44932 --0.137 -0.01838 -0.453731 --0.128557 -0.005102 -0.442871 --0.151621 -0.012736 -0.486601 --0.143161 -0.013686 -0.46922 --0.145265 -0.023601 -0.458198 --0.154182 -0.028671 -0.468872 --0.14918 -0.034021 -0.45351 --0.150857 -0.045031 -0.452753 --0.147695 -0.037346 -0.42967 --0.14553 -0.040588 -0.397805 --0.143761 -0.031171 -0.410357 --0.139706 -0.022557 -0.408004 --0.133982 -0.011764 -0.420962 --0.130189 -2.3e-05 -0.434238 --0.130731 -0.059971 -0.402214 --0.121147 -0.064038 -0.400681 --0.125873 -0.068746 -0.427178 --0.108189 -0.06546 -0.398883 --0.095316 -0.066502 -0.397735 --0.083395 -0.008128 -0.615211 --0.083465 -0.018747 -0.656748 --0.09549 -0.01114 -0.656054 --0.10805 -0.017318 -0.701964 --0.345052 0.050757 -0.075733 --0.340852 0.040872 -0.077866 --0.336268 0.045862 -0.051459 --0.332708 0.03217 -0.080015 --0.320226 0.028715 -0.057207 --0.315513 0.029836 -0.034959 --0.309902 0.030384 -0.019142 --0.323412 0.038281 -0.031359 --0.328846 0.05104 -0.026909 --0.322347 0.05044 -0.011265 --0.328773 0.070373 -0.02519 --0.324824 0.085885 -0.025156 --0.335409 0.090632 -0.048177 --0.340413 0.105961 -0.07299 --0.355039 0.079322 -0.09346 --0.354729 0.070641 -0.094084 --0.352009 0.069524 -0.083348 --0.351945 0.060515 -0.095032 --0.349183 0.058431 -0.083911 --0.349528 0.052311 -0.095002 --0.013355 0.039852 0.594693 --0.024763 0.044374 0.593548 --0.054537 0.031464 0.616147 --0.041883 0.035996 0.605149 --0.033948 0.049288 0.593243 --0.0564 0.065484 0.618657 --0.047072 0.09933 0.625654 --0.038556 0.102139 0.627143 -0.038416 -0.043774 -0.248688 --0.148015 -0.107669 -0.979672 --0.133355 -0.105551 -0.979672 --0.125761 -0.088709 -0.978708 --0.158094 -0.022202 -0.486381 --0.172153 -0.033047 -0.260095 --0.164795 -0.038678 -0.296103 --0.154686 -0.042198 -0.328648 --0.146163 -0.046645 -0.362297 --0.175247 -0.088746 0.512573 --0.115347 0.043952 -0.360718 --0.123524 0.03786 -0.375678 --0.117734 0.034503 -0.397764 --0.126675 0.028729 -0.39779 --0.127998 0.019027 -0.419943 --0.128992 0.067014 -0.016683 --0.145541 0.056575 -0.015304 --0.154535 0.052006 -0.049873 --0.156895 0.041146 -0.013534 -0.030724 -0.025361 -0.247007 -0.361949 0.095031 -0.136579 -0.017074 -0.003681 -0.036584 -0.01259 -0.019473 -0.035051 -0.011123 -0.036474 -0.029308 -0.348279 0.123165 -0.119123 -0.351082 0.119023 -0.111683 -0.355221 0.109178 -0.109376 -0.355448 0.123344 -0.125317 -0.354377 0.127636 -0.135881 --0.090973 0.143064 0.374274 --0.075512 0.14156 0.374109 --0.050079 0.136278 0.403542 --0.051536 0.13747 0.389704 --0.034916 0.125166 0.390231 --0.055592 0.135202 0.378449 --0.062288 0.129571 0.366088 --0.071 0.123602 0.358853 --0.083811 0.118598 0.35427 --0.088873 0.131855 0.363284 --0.100924 0.141979 0.37695 --0.109577 0.13932 0.377592 -0.082637 0.018265 0.705134 --0.039964 0.056346 0.594023 --0.007824 0.077675 0.574045 --0.052266 -0.100272 -0.03746 --0.041446 -0.087651 -0.060252 --0.061914 -0.113622 -0.024715 --0.073924 -0.108272 -0.035007 --0.097503 -0.11114 -0.028186 --0.081836 -0.105332 -0.058315 --0.092393 -0.103408 -0.091947 --0.068174 -0.099288 -0.091332 --0.049442 -0.089607 -0.090816 --0.036659 -0.075099 -0.089509 --0.065044 -0.123263 -0.010253 --0.009836 0.115743 0.648737 -0.327237 0.060264 -0.098716 -0.317402 0.056792 -0.083903 -0.323016 0.066262 -0.090659 -0.325027 0.078099 -0.08949 -0.328238 0.072616 -0.096421 -0.336604 0.073106 -0.106272 -0.341772 0.073187 -0.117031 -0.345532 0.067716 -0.130501 -0.344925 0.062626 -0.120462 -0.341867 0.06099 -0.11061 -0.364758 0.09914 -0.149249 --0.109966 0.110749 0.356492 --0.109311 0.125573 0.364086 --0.095597 0.114976 0.35443 --0.118885 0.132924 0.374594 --0.12195 0.107737 0.360565 --0.134508 0.103165 0.368117 --0.127253 0.121475 0.370784 --0.125349 0.136668 0.384532 --0.056701 0.132894 0.430754 --0.05099 0.134502 0.41673 -0.039707 -0.057384 0.588539 -0.044777 -0.051595 0.583812 -0.046226 -0.037371 0.588 -0.045744 -0.025078 0.584625 --0.353014 0.125885 -0.134685 --0.171172 -0.082649 -0.978226 --0.160534 -0.093928 -0.979672 --0.174519 -0.08255 -0.925923 --0.108082 -0.108063 -0.798143 --0.11853 -0.1142 -0.831174 --0.124871 -0.122406 -0.796678 --0.149434 -0.05543 -0.451883 -0.104513 -0.119856 -0.006538 -0.0978 -0.111172 -0.028164 -0.073252 -0.108298 -0.034989 -0.080957 -0.10535 -0.058061 -0.131654 -0.0198 0.347079 -0.125752 0.000379 0.343833 -0.135014 0.009127 0.377787 -0.126958 0.034315 0.373538 -0.142655 0.022913 0.401574 -0.148499 0.040696 0.417274 -0.149856 0.024669 0.463786 -0.145439 0.003689 0.46021 -0.124828 -0.071875 0.412865 -0.122421 -0.065139 0.370851 -0.111641 -0.069488 0.340433 -0.117589 -0.047997 0.327629 -0.111689 -0.028426 0.285855 -0.120236 -0.024706 0.314792 -0.121132 -0.008466 0.319208 --0.172876 -0.001797 -0.921102 --0.166697 -0.021132 -0.914594 --0.161038 -0.003711 -0.909773 --0.140046 -0.007952 -0.908327 --0.150225 -0.003921 -0.904471 --0.149673 0.015229 -0.910738 --0.136725 0.029957 -0.921102 -0.020953 0.113196 0.423049 -0.019571 0.112543 0.404996 -0.029089 0.111175 0.441785 -0.03473 0.124593 0.422144 -0.050871 0.134464 0.416757 -0.049959 0.136238 0.403569 -0.067984 0.14531 0.39275 -0.051416 0.137432 0.389731 -0.034804 0.125134 0.390255 -0.039183 0.107472 0.457142 -0.015921 -0.058296 -0.02911 -0.020451 -0.081964 -0.028563 --0.338809 0.059569 -0.107587 --0.343537 0.061005 -0.11919 --0.340383 0.071566 -0.115761 --0.130676 0.136435 0.392085 --0.139835 0.118599 0.388783 --0.135497 0.131855 0.40028 --0.140317 0.11908 0.41144 --0.131158 0.137399 0.411849 --0.127783 0.139086 0.421009 -0.075004 -0.077493 0.30713 -0.093165 -0.079862 0.333638 -0.102578 -0.060463 0.304957 -0.078029 0.023319 0.691211 --0.057586 -0.034515 -0.389729 --0.132363 0.124624 0.43241 --0.123592 0.137482 0.430947 --0.118679 0.131781 0.442643 -0.002832 0.090679 0.579852 --0.141763 0.100521 0.378563 --0.355368 0.062627 -0.114814 --0.349396 0.078605 -0.105548 --0.288757 0.056509 -0.018076 --0.286935 0.056323 -0.006271 --0.288629 0.044749 -0.008982 --0.287855 0.065227 -0.000294 --0.280113 0.053221 0.012952 --0.267767 0.047503 0.045014 --0.269139 0.037245 0.041218 --0.254836 0.020732 0.073284 --0.271923 0.02598 0.039028 --0.284591 0.030382 0.008051 --0.295296 0.031881 -0.01158 --0.30377 0.031437 -0.037083 --0.297344 0.041176 -0.043246 --0.292216 0.053984 -0.034375 --0.294587 0.072448 -0.033277 --0.301496 0.08743 -0.031962 --0.300253 0.083639 -0.011707 --0.302827 0.076435 0.001586 --0.292969 0.074014 0.001193 --0.281513 0.06279 0.019897 -0.086165 0.000232 0.696923 -0.008936 0.071248 0.803965 -0.000872 0.107344 0.613001 --0.351867 0.045028 -0.160324 --0.184334 -0.090987 0.478655 --0.180866 -0.093797 0.455579 --0.192861 -0.084703 0.435922 --0.175791 -0.091508 0.432581 --0.17016 -0.086396 0.41059 --0.101383 -0.071651 -0.425071 --0.117583 -0.078589 -0.445556 --0.129201 -0.076726 -0.446516 --0.062555 -0.045563 -0.390664 --0.070265 -0.057484 -0.394165 --0.081778 -0.063749 -0.395622 --0.018647 0.109092 0.657665 --0.01261 0.109655 0.669207 -0.0612 -0.113649 -0.024697 -0.064575 -0.12329 -0.010235 -0.047695 -0.114606 -0.018917 -0.030815 -0.113587 -0.011691 -0.030815 -0.100812 -0.024706 -0.045019 -0.011214 0.573057 --0.137942 -0.072298 -0.448699 --0.144563 -0.066044 -0.450807 --0.025506 0.112792 0.696714 --0.017276 0.122161 0.725792 --0.021595 0.119803 0.709461 --0.142078 -0.058424 -0.430818 --0.056606 -0.128518 0.003003 --0.08336 -0.12659 0.001076 --0.097366 -0.127499 0.015291 --0.103809 -0.119817 -0.006566 -0.039376 0.10617 0.663733 -0.028632 0.106834 0.66425 -0.033957 0.107383 0.64946 -0.016359 0.043991 0.515559 -0.044878 0.100847 0.681489 -0.045223 0.103968 0.670256 -0.052914 0.104231 0.65826 -0.044621 0.10681 0.652899 -0.076213 0.010488 0.681667 -0.071646 0.023293 0.67267 -0.362776 0.097661 -0.123083 -0.359574 0.088524 -0.112757 -0.312943 0.044951 -0.07454 -0.308352 0.056671 -0.069257 -0.306424 0.073658 -0.061704 -0.314519 0.093868 -0.068159 -0.324215 0.107993 -0.078378 -0.321862 0.097355 -0.08002 -0.329791 0.096935 -0.09254 -0.327202 0.088566 -0.090317 -0.321923 0.034752 -0.080185 -0.024801 0.001349 -0.202991 -0.031492 0.011056 -0.244461 -0.028399 -0.006603 -0.245536 -0.033205 -0.015061 -0.282364 --0.028547 0.103573 0.625856 --0.032026 0.105467 0.63514 --0.081198 0.054688 0.090468 -0.058176 0.097731 0.647868 -0.060032 0.093784 0.669735 -0.063961 0.082176 0.666919 -0.06654 0.070166 0.676537 -0.065813 0.070004 0.659307 -0.066309 0.056881 0.649331 -0.062987 0.07486 0.643841 -0.059015 0.089006 0.6384 --0.03838 0.035432 0.525308 --0.071382 0.027007 0.533041 --0.009674 0.106762 0.611441 --0.019029 0.103085 0.608624 -0.039222 0.019263 -0.27972 -0.362573 0.106105 -0.142558 -0.357631 0.10375 -0.131248 -0.353224 0.101317 -0.116185 -0.296483 0.061471 0.059544 -0.291315 0.057051 0.098559 -0.276438 0.058275 0.126668 -0.289726 0.052774 0.12868 -0.290853 0.046799 0.150876 -0.310791 0.072738 0.004206 -0.303663 0.06729 0.025 -0.284757 0.064199 0.058181 --0.139835 0.103654 0.435928 --0.132604 0.110644 0.44557 --0.130916 0.094977 0.458971 --0.124074 0.116031 0.45141 --0.111929 0.11973 0.45508 --0.100119 0.120694 0.456286 --0.086715 0.123262 0.455026 --0.360632 0.093308 -0.135502 --0.349583 0.084734 -0.116476 --0.358255 0.086801 -0.111672 --0.131878 0.000476 -0.92062 --0.12911 0.019234 -0.927128 -0.198889 -0.026778 0.227738 -0.192185 -0.023207 0.243713 -0.188352 -0.031844 0.264659 -0.185173 -0.020391 0.264729 -0.176599 -0.017861 0.287774 --0.013024 -0.088241 0.72662 --0.0247 -0.08469 0.720404 --0.033508 -0.078855 0.707401 --0.0416 -0.069884 0.69463 --0.027312 0.044822 0.514443 -0.018951 0.105409 0.618947 -0.057516 0.093339 0.67944 -0.278125 0.048336 0.17281 -0.263154 0.054575 0.169187 -0.262592 0.058539 0.14524 -0.247085 0.054634 0.163441 -0.233057 0.048502 0.156696 -0.282102 0.010812 0.218659 -0.288074 0.024504 0.19717 -0.297528 0.02164 0.172297 -0.290382 0.036594 0.173918 -0.065553 0.045494 0.639548 -0.068224 0.03931 0.65204 -0.069891 0.033191 0.660931 --0.083804 0.074907 -0.165948 --0.062697 0.064238 -0.16485 --0.045673 0.048194 -0.163564 --0.03268 0.028352 -0.161928 --0.127843 0.065599 -0.253296 --0.118913 0.072745 -0.210048 --0.130514 0.070531 -0.168479 --0.107684 0.076855 -0.167466 --0.096156 0.078963 -0.126465 --0.083653 -0.133012 0.031918 --0.10907 -0.123137 0.031923 --0.118079 -0.11643 0.049941 --0.159349 0.041309 -0.255529 --0.155791 0.0504 -0.21274 --0.146525 0.05484 -0.254917 --0.162423 0.043235 -0.170205 --0.148711 0.058947 -0.169549 --0.1406 0.067376 -0.127538 --0.133779 0.058198 -0.289618 --0.11935 0.057318 -0.31754 --0.168086 0.02405 -0.256279 --0.173604 0.00463 -0.257509 --0.17464 0.014528 -0.214836 --0.179462 0.002306 -0.17347 --0.172535 0.024622 -0.171551 --0.168602 0.03471 -0.128899 --0.06391 0.131373 0.441954 -0.095027 -0.048825 0.262066 -0.103824 -0.036545 0.269255 -0.104098 -0.015764 0.243629 --0.0991 0.017944 -0.450663 -0.262731 0.05789 0.122908 -0.265882 0.059393 0.093437 -0.249381 0.05205 0.116538 -0.241193 0.043654 0.110039 -0.235215 0.046569 0.132123 -0.222589 0.036845 0.14968 -0.146018 0.020217 -0.352553 -0.143756 0.007836 -0.390439 -0.14439 0.017838 -0.368618 -0.139017 0.027615 -0.35283 -0.12709 0.038621 -0.361158 -0.131022 0.036125 -0.351555 -0.355028 0.04387 -0.128252 -0.353348 0.049278 -0.116616 -0.349146 0.056385 -0.106308 -0.35089 0.054056 -0.096199 -0.350544 0.060176 -0.085108 -0.346414 0.052502 -0.07693 -0.337629 0.047607 -0.052655 -0.342214 0.042617 -0.079063 -0.33407 0.033915 -0.081211 -0.341084 0.037101 -0.099069 -0.344392 0.039642 -0.121249 --0.141336 -0.051267 -0.399824 -0.298792 0.005093 0.16812 -0.290602 -0.00859 0.188816 -0.293927 -0.009816 0.162202 -0.283459 -0.021475 0.155186 -0.27585 -0.003524 0.237141 -0.283366 -0.005735 0.214482 --0.351907 0.099595 -0.115108 --0.34249 0.092899 -0.112093 --0.345768 0.096557 -0.122526 --0.349666 0.095542 -0.136017 --0.356314 0.102028 -0.130171 --0.312351 0.036325 -0.004647 --0.305342 0.029027 -0.008073 -0.191614 0.011738 0.323365 -0.203097 0.014525 0.328262 -0.202498 0.011696 0.352268 -0.214997 0.013491 0.332878 -0.226236 0.008727 0.336797 -0.351124 0.080663 -0.106541 -0.356378 0.081055 -0.094596 -0.353367 0.071267 -0.084535 -0.356095 0.072365 -0.095294 -0.35739 0.073907 -0.111625 -0.337536 0.088233 -0.102831 -0.343807 0.094623 -0.113171 --0.02455 0.00862 -0.161409 --0.02085 -0.011834 -0.162222 --0.012323 0.119858 0.745726 -0.154903 -0.108342 -0.84821 -0.157955 -0.103488 -0.89052 -0.162337 -0.097395 -0.84577 -0.160152 -0.106884 -0.925923 -0.163093 -0.093571 -0.914112 -0.169384 -0.0825 -0.925923 -0.163187 -0.078345 -0.907845 -0.158667 -0.064508 -0.89748 -0.162831 -0.075833 -0.88042 -0.167599 -0.073578 -0.841037 -0.167119 -0.084966 -0.841937 -0.172359 -0.091098 -0.788958 -0.179314 -0.084468 -0.735822 -0.174054 -0.098491 -0.737941 -0.172653 -0.108092 -0.692848 -0.166863 -0.110119 -0.739513 -0.156392 -0.119247 -0.743145 -0.157734 -0.113673 -0.792971 -0.146709 -0.116839 -0.829945 -0.139238 -0.123252 -0.795671 -0.135221 -0.119519 -0.851526 -0.126098 -0.118567 -0.85216 -0.128453 -0.117836 -0.90182 -0.120161 -0.116392 -0.93725 -0.129375 -0.12077 -0.938938 -0.129172 -0.124665 -0.957739 -0.140594 -0.122729 -0.948098 -0.14944 -0.122801 -0.960149 -0.154592 -0.115999 -0.943517 -0.166877 -0.105317 -0.947133 -0.065331 0.074522 0.689774 -0.069246 0.061208 0.691007 -0.071205 0.059059 0.707851 -0.35675 0.064282 -0.116065 -0.353311 0.062239 -0.096241 -0.355051 0.062522 -0.127898 -0.361289 0.071473 -0.127738 -0.35018 0.077184 -0.123737 -0.20152 -0.056893 0.293143 -0.203629 -0.049299 0.268706 -0.194583 -0.041604 0.266002 -0.204535 -0.04329 0.248854 -0.205009 -0.037103 0.231537 -0.140183 -0.036419 -0.8667 -0.134024 -0.037348 -0.836944 -0.143458 -0.038358 -0.836309 -0.122109 -0.041502 -0.8244 -0.125832 -0.031999 -0.795256 -0.117287 -0.025912 -0.751044 -0.128883 -0.023115 -0.748801 -0.134097 -0.01391 -0.699996 -0.139656 -0.024143 -0.746943 -0.152452 -0.029692 -0.74438 -0.146039 -0.034038 -0.792764 -0.153367 -0.04399 -0.821778 -0.163786 -0.047167 -0.789462 -0.160572 -0.053136 -0.836354 -0.165379 -0.062623 -0.837463 -0.159322 -0.056693 -0.872748 -0.135524 -0.036067 -0.889768 -0.128481 -0.032994 -0.905194 -0.127864 -0.044391 -0.893624 -0.12024 -0.059215 -0.895553 -0.120564 -0.052687 -0.872655 -0.109133 -0.059543 -0.840626 -0.116183 -0.050045 -0.839212 -0.109217 -0.043154 -0.796386 --0.019678 0.02905 -0.029532 --0.015918 0.01718 -0.036137 --0.027782 0.104204 0.685235 --0.018875 0.106096 0.678748 --0.044037 0.103878 0.670194 --0.038982 0.102331 0.681333 --0.128709 0.010087 -0.432263 --0.12282 0.016837 -0.434716 --0.115201 0.022097 -0.434582 --0.111437 0.030062 -0.419783 --0.105629 0.024339 -0.434984 --0.093734 0.023146 -0.434976 --0.181137 -0.019254 -0.175074 --0.162184 0.043679 -0.087926 --0.175245 -0.015389 -0.25902 --0.177955 -0.02705 -0.217951 --0.177569 -0.03962 -0.176278 -0.035154 -0.122722 0.002771 --0.052378 0.104259 0.65844 --0.043756 0.106801 0.652945 --0.03289 0.107281 0.649359 -0.093246 0.049027 0.102835 -0.102315 0.040455 0.113433 -0.081108 0.054715 0.090481 -0.083986 0.055223 0.114438 -0.074667 0.060907 0.130241 -0.086741 0.049708 0.133324 -0.090962 0.038731 0.152601 -0.097319 0.040624 0.13197 -0.110442 0.029584 0.124656 -0.11543 0.014141 0.134661 --0.146583 0.097629 0.39423 --0.147066 0.097629 0.408934 --0.144415 0.099798 0.424119 --0.075479 0.127276 0.449668 --0.137907 0.071356 0.459767 --0.145378 0.063402 0.446028 -0.002666 0.099924 0.594166 --0.208136 -0.06588 0.463823 --0.213936 -0.055034 0.444634 --0.206495 -0.070744 0.440044 --0.214464 -0.036885 0.448758 --0.221066 -0.043897 0.422246 --0.2282 -0.032688 0.395547 --0.227826 -0.044995 0.392802 --0.236249 -0.044725 0.363377 --0.221761 -0.057822 0.388709 --0.212382 -0.068579 0.384699 --0.20756 -0.072435 0.413562 --0.187 -0.085581 0.498671 --0.197969 -0.077029 0.482777 --0.206481 -0.058685 0.487615 --0.203208 -0.050881 0.507905 --0.20808 -0.037902 0.491988 --0.201669 -0.018703 0.494444 --0.210263 -0.026948 0.47232 --0.208053 -0.017687 0.451214 -0.16996 -0.027323 0.310191 -0.170357 -0.015106 0.311774 -0.166048 -0.012376 0.331959 -0.17441 -0.003851 0.314699 -0.181753 0.005421 0.318704 --0.1098 0.054429 -0.334104 --0.14562 0.080033 0.435592 -0.060488 -0.068938 0.271743 -0.078781 -0.059829 0.264962 -0.092697 -0.040887 0.223772 -0.093348 -0.035422 0.193415 -0.098751 -0.019246 0.193125 -0.100697 0.002736 0.176856 -0.101353 -0.006403 0.205791 -0.095902 0.006341 0.221401 -0.038794 -0.006233 -0.313056 -0.041719 0.010433 -0.311486 -0.066881 -0.020749 -0.416186 -0.029509 -0.087462 0.126235 -0.022217 -0.076703 0.144776 -0.032796 -0.071917 0.162044 -0.011425 -0.071799 0.162332 -0.002712 -0.068146 0.176001 -0.070781 0.112051 0.464061 -0.054872 0.10699 0.465579 -0.072708 0.096866 0.474979 -0.050534 0.119282 0.451526 -0.093919 0.092528 0.477149 -0.114648 0.086984 0.475703 -0.105395 0.105146 0.46749 -0.111811 0.119691 0.455108 -0.1 0.120655 0.456313 -0.097831 0.134636 0.443876 -0.086595 0.123223 0.455053 -0.07536 0.127236 0.449695 -0.080663 0.140493 0.434606 -0.06379 0.131334 0.441982 -0.056581 0.132855 0.430781 -0.179852 0.005927 0.340541 -0.048671 0.026118 -0.310259 -0.059727 0.041338 -0.310499 -0.027022 0.037246 0.523155 --0.080366 0.042976 -0.359844 --0.072915 0.03363 -0.369455 --0.070227 0.013891 -0.415307 --0.069241 0.022839 -0.390911 --0.076875 0.030483 -0.393719 --0.085119 0.027745 -0.418972 --0.086507 0.036825 -0.396545 --0.096641 0.042094 -0.387468 --0.087837 0.044531 -0.374711 --0.092277 0.048103 -0.362594 --0.103086 0.047746 -0.360488 --0.104066 0.049317 -0.349409 -0.275432 0.062709 0.0551 -0.269288 0.055547 0.049494 -0.25155 0.043835 0.081332 -0.312765 0.085286 -0.010822 --0.006119 0.113561 0.764306 -0.000738 0.103834 0.778899 -0.056372 -0.128537 0.003258 -0.044313 -0.130652 0.018679 -0.05757 -0.134243 0.033141 -0.083339 -0.126623 0.001097 --0.155492 -0.028329 0.356208 --0.160749 -0.032802 0.345004 --0.161044 -0.021111 0.346178 --0.166167 -0.012336 0.331933 --0.164113 -0.010625 0.349268 --0.170574 -0.001596 0.353845 --0.16219 -0.006465 0.362443 --0.164373 -0.002133 0.375173 --0.154414 -0.01074 0.370753 --0.163482 -0.053438 0.350923 --0.160466 -0.043558 0.34741 --0.165475 -0.036245 0.329176 --0.18622 -0.077512 0.51589 --0.193917 -0.061157 0.520285 --0.195394 -0.04444 0.523948 --0.176073 0.003874 0.375161 --0.167484 0.004652 0.390015 --0.172233 0.009446 0.407131 -0.075831 -0.027037 0.725898 -0.068873 -0.04262 0.725898 -0.061801 -0.048767 0.745837 --0.27555 0.062748 0.055073 --0.269406 0.055586 0.049467 -0.103573 0.004244 -0.526283 -0.096997 0.003408 -0.551167 -0.088488 0.000855 -0.525091 -0.106551 0.00317 -0.5794 -0.089838 -0.000647 -0.579079 -0.084256 -0.008196 -0.615646 -0.075129 -0.008559 -0.578207 -0.064432 -0.020335 -0.577323 -0.068614 -0.011633 -0.548927 -0.065155 -0.016647 -0.522231 -0.074816 -0.006275 -0.523723 -0.081531 -0.003771 -0.502802 -0.078044 -0.009858 -0.481754 -0.119195 0.005364 -0.527473 -0.134434 0.003972 -0.528513 -0.128831 0.005293 -0.553166 -0.139619 0.002105 -0.580567 -0.122764 0.003934 -0.580212 -0.116662 -6.7e-05 -0.615168 -0.349613 0.067725 -0.140039 -0.356089 0.091025 -0.092513 -0.353287 0.086051 -0.082628 -0.068413 0.142812 0.41341 -0.206081 -0.033746 0.219483 -0.078094 -0.064043 0.179812 -0.082596 -0.049383 0.190776 -0.067026 -0.061547 0.206463 -0.097546 -0.038496 0.172632 -0.043016 -0.069454 0.25707 -0.056181 -0.066368 0.240689 -0.051476 -0.065838 0.21055 -0.035925 -0.066514 0.205479 -0.04313 -0.069236 0.184395 -0.050298 -0.074615 0.161752 -0.055473 -0.086776 0.145536 -0.066807 -0.080225 0.156563 -0.080701 -0.090169 0.145461 -0.078786 -0.075462 0.161242 -0.090862 -0.065936 0.159389 -0.106573 -0.068518 0.141109 -0.102218 -0.050143 0.15512 -0.111741 -0.029199 0.149358 -0.112008 -0.070891 -0.906881 -0.11057 -0.07076 -0.880396 -0.102978 -0.080562 -0.84563 -0.103886 -0.06994 -0.844271 -0.096618 -0.061591 -0.797183 -0.0882 -0.060651 -0.752721 -0.091109 -0.04912 -0.75201 -0.08615 -0.033149 -0.705572 -0.09666 -0.040182 -0.752628 -0.106557 -0.032314 -0.752003 -0.14956 -0.001313 -0.529318 -0.144855 -0.124841 -0.745393 -0.149778 -0.127288 -0.700318 -0.177309 -0.101695 -0.647878 -0.167115 -0.118095 -0.650531 -0.159465 -0.125585 -0.614036 -0.154711 -0.128161 -0.654369 -0.139654 -0.133976 -0.656646 -0.128731 -0.138171 -0.616951 -0.126036 -0.135417 -0.659802 -0.111818 -0.132281 -0.660855 -0.124126 -0.130883 -0.706812 -0.122834 -0.126243 -0.750746 -0.132821 -0.126997 -0.748729 -0.058829 -0.030454 -0.520769 -0.06317 -0.022602 -0.499983 -0.064227 -0.028785 -0.47925 -0.069343 -0.018317 -0.480426 -0.076388 -0.016697 -0.464359 -0.077672 -0.020169 -0.448528 -0.353537 0.0987 -0.091601 -0.346735 0.096378 -0.073406 -0.341774 0.107706 -0.074186 -0.336771 0.092377 -0.049375 -0.326186 0.08763 -0.026353 -0.330134 0.072118 -0.026387 -0.330207 0.052784 -0.028106 -0.324773 0.040026 -0.032555 -0.124965 -0.02669 0.131936 -0.116345 -0.007176 0.142504 -0.106752 0.009716 0.15506 -0.095406 0.023264 0.165686 -0.09808 -0.130509 -0.616704 -0.099527 -0.124822 -0.662285 -0.087129 -0.110283 -0.661788 -0.102193 -0.117634 -0.710458 -0.104738 -0.111843 -0.754197 -0.113192 -0.120451 -0.752962 -0.120129 -0.121547 -0.798324 -0.116907 -0.114396 -0.832544 -0.108758 -0.085321 0.124834 -0.121176 -0.06699 0.122271 -0.13404 -0.064346 0.105192 -0.130046 -0.046107 0.118861 -0.339622 0.118009 -0.112037 -0.334976 0.116204 -0.088929 -0.342497 0.1162 -0.094419 -0.073351 0.008257 0.671288 -0.10554 -0.092843 -0.849852 -0.095638 -0.08453 -0.799001 -0.090968 -0.087454 -0.754633 -0.088128 -0.07297 -0.753181 -0.078976 -0.058512 -0.707107 --0.075424 0.009123 -0.435344 -0.316876 0.03158 -0.036155 -0.086249 0.027853 0.199251 -0.08641 0.035636 0.172472 -0.075024 0.044607 0.178318 -0.063516 0.056965 0.168818 --0.106758 -0.031887 -0.749253 --0.096861 -0.039755 -0.749878 --0.110329 -0.043009 -0.794095 --0.118654 -0.050742 -0.838319 --0.111159 -0.06052 -0.840215 -0.162173 -0.024273 -0.695147 -0.163692 -0.037602 -0.74226 -0.172937 -0.048276 -0.73928 -0.180361 -0.047124 -0.689526 -0.178379 -0.05903 -0.736715 -0.180512 -0.072165 -0.735769 -0.174036 -0.067408 -0.788005 --0.01866 0.041565 -0.022306 -0.160106 -0.005357 -0.554471 --0.33064 0.11508 -0.074983 --0.311601 0.083779 -0.009789 --0.314357 0.096724 -0.02922 --0.32229 0.110319 -0.053009 -0.184241 -0.076208 -0.687737 --0.083623 0.018781 -0.434761 -0.183078 -0.041209 0.287631 -0.077348 -0.10364 0.128523 -0.094965 -0.097446 0.127334 -0.10717 -0.101699 0.110056 -0.05856 0.088006 0.689861 -0.051062 0.096194 0.687617 -0.348306 0.101164 -0.105268 -0.347888 0.106007 -0.114837 -0.355884 0.113699 -0.120724 -0.009858 0.094499 0.586661 -0.108833 -0.085105 -0.914594 -0.10845 -0.099236 -0.923753 -0.109957 -0.097255 -0.892333 -0.111542 -0.104529 -0.851505 -0.097953 -0.099412 -0.47666 -0.111357 -0.101905 -0.477447 -0.119589 -0.112055 -0.499903 -0.059946 0.08543 0.699312 --0.170476 -0.015066 0.311747 --0.174529 -0.003813 0.314673 --0.185116 0.005111 0.293771 --0.181873 0.00546 0.318677 --0.191732 0.011776 0.323338 --0.179971 0.005966 0.340514 --0.179228 0.004671 0.361014 --0.19039 0.007652 0.367844 -0.072639 -0.099523 -0.614258 -0.0775 -0.093825 -0.66106 -0.071185 -0.075868 -0.660395 -0.084233 -0.088981 -0.709949 -0.096406 -0.099648 -0.754415 -0.107078 -0.108411 -0.800435 -0.26745 -0.00378 0.255424 -0.267637 -0.016481 0.253101 -0.260933 -0.028107 0.26999 -0.261114 -0.004045 0.273784 -0.25364 -0.003772 0.29392 -0.256818 -0.015226 0.29385 -0.252192 -0.024433 0.317098 -0.256449 -0.026587 0.292379 -0.252545 -0.037425 0.289561 --0.148709 -0.127292 -0.700346 --0.138585 -0.133981 -0.656673 --0.153642 -0.128166 -0.654397 --0.125176 -0.13535 -0.659367 --0.166046 -0.1181 -0.650557 --0.176239 -0.101699 -0.647905 --0.171584 -0.108097 -0.692875 --0.172985 -0.098496 -0.737968 --0.165794 -0.110124 -0.73954 --0.155324 -0.119252 -0.743172 --0.143786 -0.124846 -0.74542 --0.131961 -0.12693 -0.748294 --0.12239 -0.126032 -0.749386 --0.123473 -0.130744 -0.705913 --0.111165 -0.132142 -0.659956 -0.160924 -0.021151 0.346205 -0.163994 -0.010665 0.349294 -0.16207 -0.006504 0.36247 -0.170454 -0.001635 0.353872 -0.179108 0.004632 0.361041 -0.175953 0.003835 0.375187 -0.19027 0.007613 0.367871 -0.198506 0.009604 0.379501 -0.060439 -0.06298 -0.613617 -0.068768 -0.059883 -0.659006 -0.070615 -0.043766 -0.658497 -0.353396 0.053089 -0.127413 -0.241181 -0.04512 0.336466 -0.245234 -0.033866 0.339391 -0.23613 -0.044764 0.363403 -0.245631 -0.02165 0.340974 -0.242331 -0.009756 0.341047 -0.2369 -0.020241 0.36773 -0.225764 -0.019446 0.394917 -0.228079 -0.032727 0.395573 -0.220946 -0.043937 0.422273 -0.227706 -0.045034 0.392829 -0.221642 -0.057862 0.388736 -0.207441 -0.072475 0.413589 -0.212263 -0.068618 0.384727 -0.201974 -0.072895 0.375283 -0.219409 -0.064794 0.355389 -0.223978 -0.060709 0.3278 -0.233838 -0.054393 0.332461 -0.244235 -0.046525 0.311355 --0.246344 -0.046865 0.248314 --0.246726 -0.046279 0.264919 --0.23634 -0.052093 0.281321 --0.245836 -0.04601 0.28581 --0.244354 -0.046486 0.311327 --0.252664 -0.037387 0.289534 --0.256568 -0.026548 0.292352 --0.235924 -0.050107 0.243671 --0.224655 -0.049757 0.239529 --0.225627 -0.052112 0.255722 --0.2142 -0.053688 0.272322 --0.22566 -0.054685 0.276767 --0.224999 -0.058884 0.302177 -0.172787 -0.090507 -0.947133 -0.172491 -0.071786 -0.943035 -0.174601 -0.062859 -0.959185 -0.171268 -0.051249 -0.941831 -0.176039 -0.031099 -0.943277 -0.183272 -0.024297 -0.958462 --0.182568 -0.083839 -0.644914 --0.183173 -0.076213 -0.687764 --0.179451 -0.07217 -0.735796 --0.178245 -0.084473 -0.73585 --0.172324 -0.091111 -0.788981 --0.165041 -0.06852 0.372711 --0.165792 -0.076618 0.390739 --0.182487 -0.078599 0.387542 --0.202092 -0.072856 0.375256 --0.192214 -0.07212 0.365583 --0.198164 -0.066806 0.340792 --0.181422 -0.0669 0.357366 --0.169688 -0.061476 0.354624 --0.175191 -0.05662 0.334131 --0.190446 0.003531 0.269551 --0.197274 0.012155 0.273275 --0.20633 0.017395 0.277484 --0.216569 0.019145 0.281759 --0.204031 0.016668 0.302641 --0.203217 0.014564 0.328234 --0.170079 -0.027283 0.310164 --0.176718 -0.017823 0.287748 --0.185292 -0.020352 0.264702 --0.186102 -0.008149 0.266454 -0.363363 0.07473 -0.150603 --0.107552 0.038514 -0.397668 --0.177325 -0.059035 -0.736742 --0.173896 -0.068383 -0.788751 -0.360636 0.075305 -0.160142 --0.189058 0.010659 0.403907 --0.198626 0.009643 0.379475 --0.202387 0.007865 0.396803 --0.212762 0.002657 0.389941 --0.206169 -0.001395 0.41268 --0.213588 -0.012012 0.413809 --0.205328 -0.008308 0.434608 --0.195397 0.000483 0.451738 --0.18388 0.006758 0.473118 --0.187941 -0.003168 0.494719 -0.062872 0.064146 0.147546 -0.159288 0.119897 -0.961836 -0.119568 0.115217 -0.964728 -0.343388 0.055728 -0.117661 -0.347914 0.053336 -0.139892 -0.341641 0.052257 -0.129927 -0.336395 0.047978 -0.120921 -0.321588 0.03046 -0.058404 --0.03278 -0.126524 0.032896 -0.123153 -0.030496 -0.92279 --0.219528 -0.064755 0.355362 -0.17326 -0.039216 0.310118 -0.165356 -0.036284 0.329203 -0.160346 -0.043597 0.347437 -0.160629 -0.032842 0.345031 -0.155372 -0.028368 0.356235 -0.150337 -0.035624 0.365792 -0.150665 -0.022157 0.367637 -0.154294 -0.010779 0.370781 -0.164253 -0.002173 0.375198 -0.035841 0.029887 0.534583 -0.079019 0.149079 0.400081 -0.349098 0.041786 -0.144112 -0.356004 0.042531 -0.154123 --0.099083 -0.124611 -0.660924 --0.113162 -0.120096 -0.750675 --0.104916 -0.111415 -0.751447 --0.102164 -0.117278 -0.708171 --0.086684 -0.110071 -0.660427 -0.084782 0.150209 0.412004 -0.089822 0.149412 0.422 -0.097534 0.153991 0.414529 -0.212494 -0.063496 0.322903 -0.108072 -0.017674 -0.704252 -0.085284 0.006734 0.70766 -0.013054 -0.070632 0.772858 --0.359252 0.073551 -0.158881 -0.35586 0.115447 -0.131675 -0.347078 0.121777 -0.131765 -0.11799 -0.014622 -0.948338 -0.118626 -0.022811 -0.937492 -0.077762 0.003004 0.692549 -0.149496 0.118045 -0.971237 -0.353272 0.046773 -0.161503 --0.169398 -0.075532 -0.842498 --0.166691 -0.077802 -0.88187 --0.169032 -0.085956 -0.842673 -0.17116 -0.100231 -0.965211 -0.078337 0.029774 0.697426 -0.078949 0.032083 0.70963 -0.078269 0.042027 0.723896 -0.081211 0.025526 0.724481 -0.081294 0.020966 0.742447 -0.081482 0.008499 0.724226 -0.079611 -0.010056 0.725374 -0.078703 -2e-05 0.707557 --0.212613 -0.063458 0.322876 --0.200714 -0.062423 0.31826 --0.201639 -0.056854 0.293115 --0.189474 -0.05766 0.314342 --0.18008 -0.049671 0.311534 -0.009779 -0.013603 0.811873 --0.348227 0.066029 -0.138773 --0.188471 -0.031806 0.264632 --0.192304 -0.023168 0.243687 --0.199008 -0.02674 0.227711 -0.195543 0.005391 -0.970995 --0.001704 -0.064566 0.188111 -0.123955 0.115992 0.451438 -0.130797 0.094938 0.458999 --0.244484 0.024749 0.252048 --0.229914 0.023071 0.265249 --0.22803 0.018149 0.286204 -0.181303 -0.06694 0.357393 -0.198046 -0.066846 0.34082 -0.192095 -0.072159 0.365611 -0.200594 -0.062462 0.318287 -0.182369 -0.078638 0.387568 -0.170041 -0.086435 0.410617 -0.165672 -0.076658 0.390767 -0.150865 -0.068843 0.390208 -0.164921 -0.06856 0.372738 -0.169569 -0.061516 0.35465 -0.163363 -0.053478 0.350949 -0.175072 -0.056658 0.334159 -0.179961 -0.04971 0.311561 -0.189355 -0.0577 0.314369 -0.135647 0.09831 -0.976539 -0.141277 0.10969 -0.974611 -0.126106 0.115455 -0.971237 -0.109014 0.105489 -0.967742 -0.224509 0.002044 0.362953 -0.23563 0.000737 0.339605 -0.245713 -0.001086 0.317242 --0.233957 -0.054354 0.332434 --0.224097 -0.06067 0.327773 -0.144342 -0.12144 -0.974756 -0.154109 -0.119068 -0.973887 -0.154443 -0.109248 -0.978226 -0.163545 -0.107182 -0.975334 -0.170636 -0.091241 -0.975334 -0.119337 0.007039 0.530859 -0.178109 -0.018702 0.526682 -0.164945 -0.013349 0.526722 -0.162346 0.000925 0.509971 -0.151118 -0.002354 0.520824 -0.134781 0.020876 0.511031 -0.132609 0.005697 0.523097 --0.306945 0.042229 0.09751 --0.303168 0.051644 0.074662 --0.291433 0.05709 0.098532 --0.296602 0.06151 0.059517 --0.284876 0.064238 0.058154 --0.303535 0.067031 0.025178 --0.310169 0.071885 0.004791 --0.313972 0.059722 0.00451 --0.316999 0.047501 -0.000922 --0.304492 0.018549 0.147779 --0.306017 0.030089 0.12688 --0.299644 0.043425 0.128327 --0.290972 0.046839 0.150848 --0.289846 0.052813 0.128654 --0.288193 0.024543 0.197143 --0.290501 0.036632 0.173892 --0.297647 0.021679 0.172269 --0.278244 0.048375 0.172783 --0.275074 0.025804 0.220254 --0.260374 0.027071 0.23891 --0.262818 0.037546 0.219144 --0.263681 0.048027 0.194925 -0.212654 0.002962 0.390454 -0.220072 -0.007654 0.391584 -0.213481 -0.011706 0.414323 -0.119387 -0.116328 0.050137 -0.110117 -0.123127 0.032005 -0.084289 -0.133045 0.031941 -0.097998 -0.127538 0.015318 -0.09455 0.144916 0.431457 --0.055893 0.052902 0.614078 --0.050027 0.052827 0.603861 -0.188938 0.01062 0.403933 -0.202273 0.007998 0.397072 -0.206061 -0.00109 0.413194 --0.085415 0.006678 0.707696 --0.086296 0.000176 0.696959 --0.183198 -0.04117 0.287604 --0.173379 -0.039177 0.310091 -0.205214 -0.008175 0.434878 -0.195277 0.000444 0.451765 -0.207934 -0.017726 0.451241 -0.209911 -0.026981 0.471619 -0.214345 -0.036924 0.448785 -0.213816 -0.055074 0.444661 -0.025324 -0.058631 0.7826 -0.01253 -0.061062 0.78584 -0.011407 -0.050197 0.794688 -0.206377 -0.070783 0.44007 -0.192742 -0.084742 0.435948 -0.180747 -0.093837 0.455606 -0.175672 -0.091547 0.432608 -0.157013 -0.090462 0.43041 -0.355823 0.129013 -0.15251 -0.167365 0.004612 0.390041 -0.154985 0.004237 0.403646 -0.172113 0.009406 0.407158 -0.174958 0.013221 0.428688 --0.202617 0.011735 0.352242 --0.359857 0.065634 -0.137145 --0.358469 0.06381 -0.148531 -0.339127 0.109041 -0.111258 --0.355676 0.07573 -0.135227 --0.361979 0.072975 -0.149342 -0.100875 0.083121 -0.970754 -0.103018 0.096223 -0.970272 -0.115575 0.098697 -0.974129 -0.124166 0.086266 -0.97678 -0.131507 0.068883 -0.977745 -0.111941 0.077113 -0.975093 -0.208017 -0.065919 0.46385 --0.078302 0.029706 0.697437 -0.058903 -0.072872 -0.54404 -0.057457 -0.067087 -0.57373 -0.063343 -0.085673 -0.573707 -0.055864 -0.050678 -0.574756 -0.073186 -0.104666 -0.575196 -0.086227 -0.121112 -0.577144 -0.078229 -0.107652 -0.546591 -0.084535 -0.107544 -0.520092 -0.072452 -0.093879 -0.518952 -0.062968 -0.078092 -0.517891 -0.057687 -0.062526 -0.518367 -0.058442 -0.052134 -0.497397 -0.056341 -0.045931 -0.519415 -0.055964 -0.039868 -0.546115 -0.058107 -0.034764 -0.575973 --0.245354 -0.033827 0.339365 --0.2413 -0.045081 0.336438 -0.341625 0.09724 -0.100812 -0.129441 -0.119864 -0.525052 -0.139869 -0.126175 -0.553229 -0.143561 -0.113256 -0.525748 -0.134261 -0.135387 -0.581649 -0.149939 -0.128617 -0.58101 -0.163143 -0.115538 -0.579851 -0.173515 -0.097078 -0.578243 -0.165684 -0.101575 -0.551639 -0.165725 -0.086894 -0.52567 -0.156171 -0.101734 -0.525316 -0.113951 -0.122268 -0.523613 -0.099083 -0.11872 -0.522086 -0.107925 -0.129584 -0.550956 -0.101374 -0.131867 -0.579516 -0.117808 -0.136392 -0.580816 -0.141849 0.001235 0.469012 -0.15723 0.009821 0.447299 -0.17877 0.011883 0.450648 -0.18376 0.006719 0.473145 --0.06438 0.023247 0.636874 --0.063112 0.03358 0.628538 --0.205128 -0.037064 0.23151 --0.204654 -0.043252 0.248828 --0.214134 -0.045326 0.236279 --0.194701 -0.041565 0.265974 --0.203749 -0.049259 0.268678 --0.221296 -0.04775 0.22812 --0.216327 -0.041007 0.217945 --0.211949 -0.032031 0.203072 --0.2062 -0.033706 0.219456 -0.176307 -0.053861 -0.970754 -0.174371 -0.07259 -0.973405 -0.166037 -0.0826 -0.978226 -0.1554 -0.093877 -0.979672 -0.156574 -0.072029 -0.977504 -0.159115 -0.051027 -0.974611 -0.173162 -0.027178 -0.973164 -0.170514 -0.042869 -0.972683 -0.180207 -0.035449 -0.968585 -0.186921 -0.015395 -0.969067 --0.074434 -0.029724 -0.656313 --0.086129 -0.032795 -0.703283 --0.091303 -0.048692 -0.74926 --0.088386 -0.060223 -0.749972 --0.097276 -0.061724 -0.795373 --0.19108 -0.014562 0.512553 -0.091317 -0.106014 -0.49759 -0.172606 0.006961 0.492825 -0.153428 0.00626 0.488919 -0.145335 0.048119 0.463165 -0.141073 0.041311 0.48148 -0.128945 0.062822 0.483894 -0.131145 0.042934 0.49661 -0.190494 -0.014589 0.511124 -0.201084 -0.01873 0.493017 -0.187589 -0.0032 0.494019 -0.181753 -0.041215 -0.578804 -0.177671 -0.025978 -0.580003 -0.182778 -0.057623 -0.577679 -0.185387 -0.052529 -0.608923 -0.18629 -0.064883 -0.643856 -0.184542 -0.048934 -0.6447 -0.179789 -0.033866 -0.646825 -0.170847 -0.021688 -0.649466 -0.176552 -0.022224 -0.612247 -0.169293 -0.012787 -0.580641 -0.155768 -0.003262 -0.581065 -0.172059 -0.068744 -0.526163 -0.178575 -0.063213 -0.551391 -0.180073 -0.076866 -0.577502 --0.360641 0.10223 -0.157401 --0.362883 0.096396 -0.150932 -0.183637 -0.083834 -0.644886 -0.18003 -0.090725 -0.609774 -0.149608 -0.004021 -0.614175 -0.33473 0.104244 -0.099942 -0.064528 -0.030949 -0.614326 -0.074879 -0.029935 -0.657674 -0.061988 -0.042839 -0.478018 -0.065538 -0.034823 -0.462206 -0.095934 -0.011351 -0.657415 -0.08391 -0.018958 -0.65811 --0.362464 0.103486 -0.146037 -0.362126 0.105398 -0.155069 --0.096585 -0.099221 -0.751664 --0.091146 -0.087026 -0.751883 --0.084203 -0.088625 -0.70766 --0.07074 -0.075656 -0.659033 --0.077055 -0.093614 -0.659699 --0.071779 -0.099456 -0.613822 --0.088308 -0.072542 -0.75043 -0.008712 0.092262 0.790131 -0.025323 0.069293 0.800772 -0.017018 0.080849 0.797134 -0.024425 0.08945 0.787811 -0.039578 0.084327 0.7826 -0.0407 0.065028 0.794688 -0.181339 -0.009278 -0.973165 -0.170214 -0.002734 -0.973887 -0.15423 0.009263 -0.972683 -0.010846 -0.039475 0.801916 -0.010059 -0.027181 0.807399 -0.019509 -0.009221 0.810943 --0.189676 -0.02732 0.526138 -0.157428 -0.013009 -0.65097 -0.142149 -0.008196 -0.653256 -0.126057 -0.006072 -0.654604 -0.111627 -0.007039 -0.656616 --0.168201 -0.079358 -0.908568 -0.153661 -0.045747 0.361072 -0.021957 -0.035043 0.800794 --0.245751 -0.02161 0.340947 --0.252311 -0.024394 0.317072 --0.256938 -0.015188 0.293823 -0.315719 0.098469 -0.030418 -0.323652 0.112064 -0.054205 -0.069311 -0.035047 -0.448088 --0.225878 -0.01958 0.394647 --0.23702 -0.020202 0.367703 --0.242451 -0.009717 0.34102 --0.06082 0.050615 0.625212 --0.238481 0.01372 0.289848 --0.22739 0.014637 0.311703 --0.226356 0.008767 0.336769 --0.215117 0.013529 0.332851 --0.23575 0.000777 0.339577 --0.245832 -0.001047 0.317215 --0.247528 0.006025 0.292552 --0.253759 -0.003734 0.293895 --0.261233 -0.004006 0.273759 --0.22018 -0.00796 0.391069 --0.224623 0.001911 0.362682 -0.010481 0.086772 0.577044 -0.143699 -0.044555 0.377004 --0.251061 0.011926 0.271567 --0.070169 -0.043554 -0.657136 --0.068323 -0.059671 -0.657643 --0.078948 -0.058156 -0.70482 -0.138631 -0.069856 0.409434 -0.140603 -0.056375 0.391026 -0.144295 0.068666 0.390136 -0.131039 0.072281 0.373987 -0.119545 0.061258 0.365376 -0.148392 0.060229 0.409178 -0.14791 0.06047 0.427255 -0.061194 -0.101996 0.128584 -0.068223 -0.115659 0.112662 -0.055213 -0.122019 0.097083 -0.078208 -0.122298 0.098267 -0.090109 -0.125325 0.083517 -0.099539 -0.114531 0.09685 -0.119083 -0.100139 0.094385 -0.130689 -0.095004 0.080497 -0.133536 -0.081117 0.092084 -0.14342 -0.057279 0.089751 -0.079745 -0.131592 0.067127 -0.103922 -0.122391 0.067669 -0.055395 -0.131208 0.065896 -0.069381 -0.135449 0.049532 -0.124957 -0.10646 0.067114 -0.036158 -0.114652 0.094315 -0.042764 -0.124662 0.081103 -0.033238 -0.101133 0.108597 -0.045264 -0.094838 0.127286 -0.206129 -0.058717 0.486915 -0.197849 -0.077068 0.482803 -0.184214 -0.091027 0.478681 -0.140409 -0.081699 0.429785 -0.127904 0.080476 0.470882 -0.137787 0.071317 0.459794 -0.145259 0.063363 0.446056 --0.26065 0.006429 0.260385 -0.119403 -0.006611 -0.956534 --0.25518 0.018034 0.254722 -0.01688 -0.065543 0.196797 -0.03084 -0.067719 0.230546 -0.110934 -0.109238 -0.937974 -0.116602 -0.118716 -0.954292 -0.038681 -0.056487 0.774999 -0.036323 -0.044192 0.787834 -0.046088 -0.027895 0.790202 -0.034078 -0.03147 0.797181 -0.031518 -0.017656 0.804089 -0.142881 -0.10762 -0.979672 -0.073673 0.057384 0.724556 -0.074239 0.055939 0.737636 -0.133232 0.051032 -0.977021 -0.332002 0.116825 -0.076179 -0.059894 0.044442 0.78584 -0.051924 0.056737 0.789578 -0.051946 0.0401 0.794742 -0.047547 0.071461 0.78584 -0.047029 0.020604 0.800903 -0.039958 0.001019 0.804143 -0.050712 0.005504 0.797234 -0.054282 -0.009597 0.790202 -0.060455 0.00956 0.787834 -0.069883 0.012992 0.774999 -0.06472 0.029003 0.7826 -0.065112 0.048174 0.775876 -0.070107 0.051334 0.763795 -0.061801 0.065488 0.769652 -0.05546 0.081657 0.7638 -0.052205 0.076479 0.775876 -0.057184 0.084832 0.751847 -0.029857 -0.004128 0.808754 --0.210827 -0.020354 0.184299 --0.213135 -0.008264 0.161048 -0.125472 -0.004017 -0.963041 -0.077122 0.035307 0.756062 -0.07301 0.053779 0.751837 -0.066428 0.071108 0.745741 -0.074148 -0.002592 0.762786 -0.077291 0.016137 0.760543 --0.240427 -0.009453 0.11697 --0.226254 -0.009526 0.137936 -0.07628 -0.016315 0.745837 --0.108726 0.077671 -0.08699 --0.147896 0.060634 -0.08722 --0.130339 0.072406 -0.087051 --0.120429 0.074217 -0.051083 --0.245918 0.004612 0.0982 -0.118984 -0.121476 -0.966898 -0.130897 -0.125393 -0.971237 -0.135425 -0.118096 -0.97678 -0.13368 0.025991 -0.973647 -0.136093 -0.000389 -0.968586 -0.207495 -0.037927 0.490559 -0.051363 -0.053485 0.762786 -0.071824 0.052174 -0.311375 --0.109144 0.070674 -0.018124 --0.068987 -0.135429 0.049518 --0.079129 -0.131615 0.067054 --0.102803 -0.122492 0.067471 --0.057114 -0.134223 0.032887 --0.044072 -0.130639 0.018188 -0.058097 -0.024607 0.778987 -0.068536 -0.021033 0.764406 -0.061016 -0.037903 0.764406 diff --git a/src/cython/example/ex_clustering.py b/src/cython/example/ex_clustering.py deleted file mode 100644 index 44b3d610..00000000 --- a/src/cython/example/ex_clustering.py +++ /dev/null @@ -1,64 +0,0 @@ -import numpy as np -from sklearn.metrics import pairwise_distances -import os -import gudhi as gd -import sys -sys.path.append("../sktda/") -from clustering import * - -X = np.loadtxt("../../../data/points/human") - -print("Mapper computation with point cloud") -mapper = MapperComplex(inp="point cloud", - filters=X[:,[2,0]], - filter_bnds=np.array([[np.nan,np.nan],[np.nan,np.nan]]), - resolutions=np.array([np.nan,np.nan]), gains=np.array([0.33,0.33]), - colors=X[:,2:3], - ).fit(X) - -f = open("mapper_pc", "w") -f.write("%s\n%s\n%s\n%f %f\n%d %d\n" % ("human", "coord2-0", "coord2", 10, 0.33, len(mapper.mapper_.get_skeleton(0)), len([edge for (edge,f) in mapper.mapper_.get_skeleton(1) if len(edge)==2]))) -for (vertex,_) in mapper.mapper_.get_skeleton(0): - f.write(str(vertex[0]) + " " + str(mapper.node_info_[vertex[0]]["colors"][0]) + " " + str(mapper.node_info_[vertex[0]]["size"]) + "\n") -for (edge,_) in mapper.mapper_.get_skeleton(1): - if len(edge) == 2: - f.write(str(edge[0]) + " " + str(edge[1]) + "\n") -f.close() - -os.system("python3 ~/Git/gudhi-devel/src/Nerve_GIC/utilities/KeplerMapperVisuFromTxtFile.py -f ~/Git/gudhi-devel/src/cython/example/mapper_pc") -os.system("rm ~/Git/gudhi-devel/src/cython/example/mapper_pc") - -dgms = mapper.compute_persistence_diagrams() -plot = gd.plot_persistence_diagram(dgms[0]) -plot.show() - -distrib = mapper.compute_distribution(X, N=10) -print("Distance threshold associated to confidence 90 percent is " + str(distrib[int(np.floor(0.9 * len(distrib)))])) - -print("Mapper computation with pairwise distances only") -X = pairwise_distances(X) -mapper = MapperComplex(inp="distance matrix", - filters=X[:,[2,0]], - filter_bnds=np.array([[np.nan,np.nan],[np.nan,np.nan]]), - resolutions=np.array([np.nan,np.nan]), gains=np.array([0.33,0.33]), - colors=np.max(X, axis=1)[:,np.newaxis], - ).fit(X) - -f = open("mapper_dm", "w") -f.write("%s\n%s\n%s\n%f %f\n%d %d\n" % ("human", "coord2-0", "coord2", 10, 0.33, len(mapper.mapper_.get_skeleton(0)), len([edge for (edge,f) in mapper.mapper_.get_skeleton(1) if len(edge)==2]))) -for (vertex,_) in mapper.mapper_.get_skeleton(0): - f.write(str(vertex[0]) + " " + str(mapper.node_info_[vertex[0]]["colors"][0]) + " " + str(mapper.node_info_[vertex[0]]["size"]) + "\n") -for (edge,_) in mapper.mapper_.get_skeleton(1): - if len(edge) == 2: - f.write(str(edge[0]) + " " + str(edge[1]) + "\n") -f.close() - -os.system("python3 ~/Git/gudhi-devel/src/Nerve_GIC/utilities/KeplerMapperVisuFromTxtFile.py -f ~/Git/gudhi-devel/src/cython/example/mapper_dm") -os.system("rm ~/Git/gudhi-devel/src/cython/example/mapper_dm") - -dgms = mapper.compute_persistence_diagrams() -plot = gd.plot_persistence_diagram(dgms[0]) -plot.show() - -distrib = mapper.compute_distribution(X, N=10) -print("Distance threshold associated to confidence 90 percent is " + str(distrib[int(np.floor(0.9 * len(distrib)))])) diff --git a/src/cython/sktda/clustering.py b/src/cython/sktda/clustering.py deleted file mode 100644 index d3dd531b..00000000 --- a/src/cython/sktda/clustering.py +++ /dev/null @@ -1,269 +0,0 @@ -""" -@author: Mathieu Carriere -All rights reserved -""" - -import numpy as np -import itertools - -from metrics import BottleneckDistance -from sklearn.base import BaseEstimator, TransformerMixin -from sklearn.cluster import DBSCAN, AgglomerativeClustering -from sklearn.metrics import pairwise_distances -from sklearn.neighbors import radius_neighbors_graph, kneighbors_graph -from scipy.spatial.distance import directed_hausdorff -from scipy.sparse import csgraph - -try: - import gudhi as gd - USE_GUDHI = True - -except ImportError: - USE_GUDHI = False - print("Gudhi not found: MapperComplex not available") - -############################################# -# Clustering ################################ -############################################# - -class MapperComplex(BaseEstimator, TransformerMixin): - """ - This is a class for computing Mapper simplicial complexes on point clouds or distance matrices. - """ - def __init__(self, filters, filter_bnds, colors, resolutions, gains, inp="point cloud", clustering=DBSCAN(), mask=0): - """ - Constructor for the MapperComplex class. - - Attributes: - inp (string): either "point cloud" or "distance matrix". Specifies the type of input data. - filters (numpy array of shape (num_points) x (num_filters)): filters (sometimes called lenses) used to compute the Mapper. Each column of the numpy array defines a scalar function defined on the input points. - filter_bnds (numpy array of shape (num_filters) x 2): limits of each filter, of the form [[f_1^min, f_1^max], ..., [f_n^min, f_n^max]]. If one of the values is numpy.nan, it can be computed from the points with the fit() method. - colors (numpy array of shape (num_points) x (num_colors)): functions used to color the nodes of the output Mapper simplicial complex. More specifically, coloring is done by computing the means of these functions on the subpopulations corresponding to each node. It can be the same as filters. - resolutions (numpy array of shape num_filters containing integers): resolution of each filter, ie number of intervals required to cover each filter image. - gains (numpy array of shape num_filters containing doubles in [0,1]): gain of each filter, ie overlap percentage of the intervals covering each filter image. - clustering (class): clustering class (default sklearn.cluster.DBSCAN()). Common clustering classes can be found in the scikit-learn library (such as AgglomerativeClustering for instance). - mask (int): threshold on the size of the Mapper nodes (default 0). Any node associated to a subpopulation with less than **mask** points will be removed. - - mapper_ (gudhi SimplexTree): Mapper simplicial complex computed after calling the fit() method - node_info_ (dictionary): various information associated to the nodes of the Mapper. - """ - self.filters, self.filter_bnds, self.resolutions, self.gains, self.colors, self.clustering = filters, filter_bnds, resolutions, gains, colors, clustering - self.input, self.mask = inp, mask - - def get_optimal_parameters_for_agglomerative_clustering(self, X, beta=0., C=10., N=100): - """ - Compute optimal scale and resolutions for a point cloud or a distance matrix. - - Parameters: - X (numpy array of shape (num_points) x (num_coordinates) if point cloud and (num_points) x (num_points) if distance matrix): input point cloud or distance matrix. - beta (double): exponent parameter (default 0.). See http://www.jmlr.org/papers/volume19/17-291/17-291.pdf for details. - C (double): constant parameter (default 10.). See http://www.jmlr.org/papers/volume19/17-291/17-291.pdf for details. - N (int): subsampling iterations (default 100). See http://www.jmlr.org/papers/volume19/17-291/17-291.pdf for details. - - Returns: - delta (double): optimal scale that can be used with agglomerative clustering. - resolutions (numpy array of shape (num_filters): optimal resolutions associated to each filter. - """ - num_pts, num_filt, delta = X.shape[0], self.filters.shape[1], 0 - m = int( num_pts / np.exp((1+beta) * np.log(np.log(num_pts)/np.log(C))) ) - for _ in range(N): - subpop = np.random.choice(num_pts, size=m, replace=False) - if self.input == "point cloud": - d, _, _ = directed_hausdorff(X, X[subpop,:]) - if self.input == "distance matrix": - d = np.max(np.min(X[:,subpop], axis=1), axis=0) - delta += d/N - - pairwise = pairwise_distances(X, metric="euclidean") if self.input == "point cloud" else X - pairs = np.argwhere(pairwise <= delta) - num_pairs = pairs.shape[0] - res = [] - for f in range(num_filt): - F = self.filters[:,f] - minf, maxf = np.min(F), np.max(F) - resf = 0 - for p in range(num_pairs): - resf = max(resf, abs(F[pairs[p,0]] - F[pairs[p,1]])) - res.append(int((maxf-minf)/resf)) - - return delta, np.array(res) - - - def fit(self, X, y=None): - """ - Fit the MapperComplex class on a point cloud or a distance matrix: compute the Mapper and store it in a simplex tree called mapper_ - - Parameters: - X (numpy array of shape (num_points) x (num_coordinates) if point cloud and (num_points) x (num_points) if distance matrix): input point cloud or distance matrix. - y (n x 1 array): point labels (unused). - """ - num_pts, num_filters, num_colors = self.filters.shape[0], self.filters.shape[1], self.colors.shape[1] - - # If some resolutions are not specified, automatically compute them - if np.any(np.isnan(self.resolutions)): - delta, resolutions = self.get_optimal_parameters_for_agglomerative_clustering(X=X, beta=0., C=10, N=100) - #self.clustering = NNClustering(radius=delta, inp=self.input) - if self.input == "point cloud": - self.clustering = AgglomerativeClustering(n_clusters=None, linkage="single", distance_threshold=delta, affinity="euclidean") - else: - self.clustering = AgglomerativeClustering(n_clusters=None, linkage="single", distance_threshold=delta, affinity="precomputed") - self.resolutions = np.where(np.isnan(self.resolutions), resolutions, self.resolutions) - - # If some filter limits are unspecified, automatically compute them - self.filter_bnds = np.where(np.isnan(self.filter_bnds), np.hstack([np.min(self.filters, axis=0)[:,np.newaxis], np.max(self.filters, axis=0)[:,np.newaxis]]), self.filter_bnds) - - # Initialize attributes - self.mapper_, self.node_info_ = gd.SimplexTree(), {} - - # Compute which points fall in which patch or patch intersections - interval_inds, intersec_inds = np.empty(self.filters.shape), np.empty(self.filters.shape) - for i in range(num_filters): - f, r, g = self.filters[:,i], self.resolutions[i], self.gains[i] - min_f, max_f = self.filter_bnds[i,0], np.nextafter(self.filter_bnds[i,1], np.inf) - interval_endpoints, l = np.linspace(min_f, max_f, num=r+1, retstep=True) - intersec_endpoints = [] - for j in range(1, len(interval_endpoints)-1): - intersec_endpoints.append(interval_endpoints[j] - g*l / (2 - 2*g)) - intersec_endpoints.append(interval_endpoints[j] + g*l / (2 - 2*g)) - interval_inds[:,i] = np.digitize(f, interval_endpoints) - intersec_inds[:,i] = 0.5 * (np.digitize(f, intersec_endpoints) + 1) - - # Build the binned_data map that takes a patch or a patch intersection and outputs the indices of the points contained in it - binned_data = {} - for i in range(num_pts): - list_preimage = [] - for j in range(num_filters): - a, b = interval_inds[i,j], intersec_inds[i,j] - list_preimage.append([a]) - if b == a: - list_preimage[j].append(a+1) - if b == a-1: - list_preimage[j].append(a-1) - list_preimage = list(itertools.product(*list_preimage)) - for pre_idx in list_preimage: - try: - binned_data[pre_idx].append(i) - except KeyError: - binned_data[pre_idx] = [i] - - # Initialize the cover map, that takes a point and outputs the clusters to which it belongs - cover, clus_base = [[] for _ in range(num_pts)], 0 - - # For each patch - for preimage in binned_data: - - # Apply clustering on the corresponding subpopulation - idxs = np.array(binned_data[preimage]) - if len(idxs) > 1: - clusters = self.clustering.fit_predict(X[idxs,:]) if self.input == "point cloud" else self.clustering.fit_predict(X[idxs,:][:,idxs]) - elif len(idxs) == 1: - clusters = np.array([0]) - else: - continue - - # Collect various information on each cluster - num_clus_pre = np.max(clusters) + 1 - for clus_i in range(num_clus_pre): - node_name = clus_base + clus_i - subpopulation = idxs[clusters == clus_i] - if len(subpopulation) >= self.mask: - self.node_info_[node_name] = {} - self.node_info_[node_name]["indices"] = subpopulation - self.node_info_[node_name]["size"] = len(subpopulation) - self.node_info_[node_name]["colors"] = np.mean(self.colors[subpopulation,:], axis=0) - self.node_info_[node_name]["patch"] = preimage - - # Update the cover map - for pt in range(clusters.shape[0]): - node_name = clus_base + clusters[pt] - if clusters[pt] != -1 and self.node_info_[node_name]["size"] >= self.mask: - cover[idxs[pt]].append(node_name) - - clus_base += np.max(clusters) + 1 - - # Insert the simplices of the Mapper complex - for i in range(num_pts): - self.mapper_.insert(cover[i], filtration=-3) - self.mapper_.initialize_filtration() - - return self - - def compute_persistence_diagrams(self): - """ - Compute the extended persistence diagrams of the Mapper simplicial complex associated to each color function. - - Returns: - list_dgm (list of gudhi persistence diagrams): output extended persistence diagrams. There is one per color function. - """ - num_cols, list_dgm = self.colors.shape[1], [] - - # Compute an extended persistence diagram for each color - for c in range(num_cols): - - # Retrieve all color values - col_vals = {node_name: self.node_info_[node_name]["colors"][c] for node_name in self.node_info_.keys()} - - # Create a new simplicial complex by coning the Mapper with an extra point with name -2 - st = gd.SimplexTree() - list_simplices, list_vertices = self.mapper_.get_skeleton(1), self.mapper_.get_skeleton(0) - for (simplex, f) in list_simplices: - st.insert(simplex + [-2], filtration=-3) - - # Assign ascending filtration values on the original simplices and descending filtration values on the coned simplices - min_val, max_val = min(col_vals), max(col_vals) - for (vertex, f) in list_vertices: - if st.find(vertex): - st.assign_filtration(vertex, filtration = -2 + (col_vals[vertex[0]]-min_val)/(max_val-min_val)) - st.assign_filtration(vertex + [-2], filtration = 2 - (col_vals[vertex[0]]-min_val)/(max_val-min_val)) - - # Compute persistence - st.make_filtration_non_decreasing() - dgm = st.persistence() - - # Output extended persistence diagrams - for point in range(len(dgm)): - b,d = dgm[point][1][0], dgm[point][1][1] - b,d = min_val+(2-abs(b))*(max_val-min_val), min_val+(2-abs(d))*(max_val-min_val) - dgm[point] = tuple([dgm[point][0], tuple([b,d])]) - list_dgm.append(dgm) - - return list_dgm - - def compute_distribution(self, X, N=100): - """ - Compute a bootstrap distribution of bottleneck distances. More specifically, subsample the input point cloud or distance matrix, compute the Mapper with the same parameters on this subsample, and compare its extended persistence diagrams with the original ones. - - Parameters: - X (numpy array of shape (num_points) x (num_coordinates) if point cloud and (num_points) x (num_points) if distance matrix): input point cloud or distance matrix. - N (int): bootstrap iterations (default 100). - - Returns: - distribution: list of bottleneck distance values. - """ - num_pts, distribution = len(X), [] - dgm = self.compute_persistence_diagrams() - - for bootstrap_id in range(N): - - print(str(bootstrap_id) + "th iteration") - - # Randomly select points - idxs = np.random.choice(num_pts, size=num_pts, replace=True) - Xboot = X[idxs,:] if self.input == "point cloud" else X[idxs,:][:,idxs] - f_boot, c_boot = self.filters[idxs,:], self.colors[idxs,:] - Mboot = self.__class__(filters=f_boot, filter_bnds=self.filter_bnds, colors=c_boot, resolutions=self.resolutions, gains=self.gains, inp=self.input, clustering=self.clustering).fit(Xboot) - - # Compute the corresponding persistence diagrams - dgm_boot = Mboot.compute_persistence_diagrams() - - # Compute the bottleneck distances between them and keep the maximum - df = 0. - for i in range(len(dgm)): - npts, npts_boot = len(dgm[i]), len(dgm_boot[i]) - D1 = np.array([[dgm[i][pt][1][0], dgm[i][pt][1][1]] for pt in range(npts) if dgm[i][pt][0] <= 1]) - D2 = np.array([[dgm_boot[i][pt][1][0], dgm_boot[i][pt][1][1]] for pt in range(npts_boot) if dgm_boot[i][pt][0] <= 1]) - bottle = BottleneckDistance().fit([D1]) - df = max(df, float(np.squeeze(bottle.transform([D2])))) - distribution.append(df) - - return np.sort(distribution) -- cgit v1.2.3 From c18017f78779239cf17dc1a9b0d00a9b8f075a2d Mon Sep 17 00:00:00 2001 From: mathieu Date: Mon, 9 Sep 2019 09:50:23 -0400 Subject: fixed a few typos and added entropy --- src/cython/sktda/kernel_methods.py | 2 +- src/cython/sktda/metrics.py | 18 +++++--- src/cython/sktda/preprocessing.py | 16 +++---- src/cython/sktda/vector_methods.py | 93 ++++++++++++++++++++++++++++++++++---- 4 files changed, 106 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/cython/sktda/kernel_methods.py b/src/cython/sktda/kernel_methods.py index 57bfafd7..6e3f6d5e 100644 --- a/src/cython/sktda/kernel_methods.py +++ b/src/cython/sktda/kernel_methods.py @@ -6,7 +6,7 @@ All rights reserved import numpy as np from sklearn.base import BaseEstimator, TransformerMixin from sklearn.metrics import pairwise_distances -from metrics import SlicedWassersteinDistance, PersistenceFisherDistance +from .metrics import SlicedWassersteinDistance, PersistenceFisherDistance ############################################# # Kernel methods ############################ diff --git a/src/cython/sktda/metrics.py b/src/cython/sktda/metrics.py index 05141e8b..5dc219cd 100644 --- a/src/cython/sktda/metrics.py +++ b/src/cython/sktda/metrics.py @@ -192,7 +192,7 @@ class PersistenceFisherDistance(BaseEstimator, TransformerMixin): U, V = np.sum(np.concatenate([self.approx_[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], self.approx_diagonal_[i]], axis=0), axis=0) vectori, vectorj = np.matmul(Z, U.T), np.matmul(Z, V.T) vectori, vectorj = vectori/np.sum(vectori), vectorj/np.sum(vectorj) - Xfit[i,j] = np.arccos(np.dot(np.sqrt(vectori), np.sqrt(vectorj))) + Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) Xfit[j,i] = Xfit[i,j] else: Z = np.concatenate([self.diagrams_[i], self.diagonal_projections_[i], self.diagrams_[j], self.diagonal_projections_[j]], axis=0) @@ -200,7 +200,7 @@ class PersistenceFisherDistance(BaseEstimator, TransformerMixin): vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) vectori, vectorj = vectori/np.sum(vectori), vectorj/np.sum(vectorj) - Xfit[i,j] = np.arccos(np.dot(np.sqrt(vectori), np.sqrt(vectorj))) + Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) Xfit[j,i] = Xfit[i,j] else: projection = (1./2) * np.ones((2,2)) @@ -214,13 +214,19 @@ class PersistenceFisherDistance(BaseEstimator, TransformerMixin): Z = np.concatenate([approx[i], approx_diagonal[i], self.approx_[j], self.approx_diagonal_[j]], axis=0) U, V = np.sum(np.concatenate([approx[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], approx_diagonal[i]], axis=0), axis=0) vectori, vectorj = np.matmul(Z, U.T), np.matmul(Z, V.T) - vectori, vectorj = vectori/np.sum(vectori), vectorj/np.sum(vectorj) - Xfit[i,j] = np.arccos(np.dot(np.sqrt(vectori), np.sqrt(vectorj))) + if np.sum(vectori) != 0: + vectori = vectori/np.sum(vectori) + if np.sum(vectorj) != 0: + vectorj = vectorj/np.sum(vectorj) + Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) else: Z = np.concatenate([X[i], diagonal_projections[i], self.diagrams_[j], self.diagonal_projections_[j]], axis=0) U, V = np.concatenate([X[i], self.diagonal_projections_[j]], axis=0), np.concatenate([self.diagrams_[j], diagonal_projections[i]], axis=0) vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) - vectori, vectorj = vectori/np.sum(vectori), vectorj/np.sum(vectorj) - Xfit[i,j] = np.arccos(np.dot(np.sqrt(vectori), np.sqrt(vectorj))) + if np.sum(vectori) != 0: + vectori = vectori/np.sum(vectori) + if np.sum(vectorj) != 0: + vectorj = vectorj/np.sum(vectorj) + Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) return Xfit diff --git a/src/cython/sktda/preprocessing.py b/src/cython/sktda/preprocessing.py index 2f4a5fe5..b2bc68f6 100644 --- a/src/cython/sktda/preprocessing.py +++ b/src/cython/sktda/preprocessing.py @@ -26,8 +26,8 @@ class BirthPersistenceTransform(BaseEstimator, TransformerMixin): Fit the BirthPersistenceTransform class on a list of persistence diagrams (this function actually does nothing but is useful when BirthPersistenceTransform is included in a scikit-learn Pipeline). Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). + X (n x 2 numpy array): input persistence diagram. + y (n x 1 array): persistence diagram label (unused). """ return self @@ -36,10 +36,10 @@ class BirthPersistenceTransform(BaseEstimator, TransformerMixin): Apply the BirthPersistenceTransform function on the persistence diagrams. Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + X (n x 2 numpy array): input persistence diagram. Returns: - Xfit (list of n x 2 numpy arrays): transformed persistence diagrams. + Xfit (n x 2 numpy array): transformed persistence diagram. """ Xfit = np.matmul(X, np.array([[1., -1.],[0., 1.]])) return Xfit @@ -55,10 +55,10 @@ class DiagramPreprocessor(BaseEstimator, TransformerMixin): Attributes: use (bool): whether to use the class or not (default False). - scalers (list of classes): list of scalers to be fit on the persistence diagrams (default []). Each element of the list is a tuple with two elements: the first one is a list of coordinates, and the second one is a scaler (i.e. a class with fit() and transform() methods) that is going to be applied to these coordinates. Common scalers can be found in the scikit-learn library (such as MinMaxScaler for instance). + scalers (list of classes): list of scalers to be fit on the persistence diagrams (default []). Each element of the list is a tuple with two elements: the first one is a list of coordinates, and the second one is a scaler (i.e. a class with fit() and transform() methods) that is going to be applied to these coordinates. Common scalers can be found in the scikit-learn library (such as MinMaxScaler for instance). """ - self.scalers = scalers - self.use = use + self.scalers = scalers + self.use = use def fit(self, X, y=None): """ @@ -74,7 +74,7 @@ class DiagramPreprocessor(BaseEstimator, TransformerMixin): else: P = np.concatenate(X,0) for (indices, scaler) in self.scalers: - scaler.fit(P[:,indices]) + scaler.fit(np.reshape(P[:,indices], [-1, 1])) return self def transform(self, X): diff --git a/src/cython/sktda/vector_methods.py b/src/cython/sktda/vector_methods.py index 4dd147e7..99c2f420 100644 --- a/src/cython/sktda/vector_methods.py +++ b/src/cython/sktda/vector_methods.py @@ -5,10 +5,10 @@ All rights reserved import numpy as np from sklearn.base import BaseEstimator, TransformerMixin -from sklearn.preprocessing import MinMaxScaler +from sklearn.preprocessing import MinMaxScaler, MaxAbsScaler from sklearn.neighbors import DistanceMetric -from preprocessing import DiagramPreprocessor +from .preprocessing import DiagramPreprocessor, BirthPersistenceTransform ############################################# # Finite Vectorization methods ############## @@ -40,7 +40,8 @@ class PersistenceImage(BaseEstimator, TransformerMixin): y (n x 1 array): persistence diagram labels (unused). """ if np.isnan(np.array(self.im_range)).any(): - pre = DiagramPreprocessor(use=True, scalers=[([0,1], MinMaxScaler())]).fit(X,y) + new_X = DiagramPreprocessor(use=True, scalers=[([0,1], BirthPersistenceTransform())]).fit_transform(X) + pre = DiagramPreprocessor(use=True, scalers=[([0,1], MinMaxScaler())]).fit(new_X,y) [mx,my],[Mx,My] = pre.scalers[0][1].data_min_, pre.scalers[0][1].data_max_ self.im_range = np.where(np.isnan(np.array(self.im_range)), np.array([mx, Mx, my, My]), np.array(self.im_range)) return self @@ -56,9 +57,11 @@ class PersistenceImage(BaseEstimator, TransformerMixin): Xfit (numpy array with shape (number of diagrams) x (number of pixels = **resolution[0]** x **resolution[1]**)): output persistence images. """ num_diag, Xfit = len(X), [] + new_X = DiagramPreprocessor(use=True, scalers=[([0,1], BirthPersistenceTransform())]).fit_transform(X) + for i in range(num_diag): - diagram, num_pts_in_diag = X[i], X[i].shape[0] + diagram, num_pts_in_diag = new_X[i], X[i].shape[0] w = np.ones(num_pts_in_diag) for j in range(num_pts_in_diag): @@ -69,7 +72,8 @@ class PersistenceImage(BaseEstimator, TransformerMixin): image = np.tensordot(w, np.exp((-np.square(Xs)-np.square(Ys))/(2*np.square(self.bandwidth)))/(self.bandwidth*np.sqrt(2*np.pi)), 1) Xfit.append(image.flatten()[np.newaxis,:]) - Xfit = np.concatenate(Xfit,0) + + Xfit = np.concatenate(Xfit,0) return Xfit @@ -150,7 +154,8 @@ class Landscape(BaseEstimator, TransformerMixin): ls[k,j] = events[j][k] Xfit.append(np.sqrt(2)*np.reshape(ls,[1,-1])) - Xfit = np.concatenate(Xfit,0) + + Xfit = np.concatenate(Xfit,0) return Xfit @@ -227,7 +232,8 @@ class Silhouette(BaseEstimator, TransformerMixin): silhouette_value -= step_x Xfit.append(np.reshape(np.sqrt(2) * sh, [1,-1])) - Xfit = np.concatenate(Xfit, 0) + + Xfit = np.concatenate(Xfit, 0) return Xfit @@ -286,7 +292,78 @@ class BettiCurve(BaseEstimator, TransformerMixin): bc[k] += 1 Xfit.append(np.reshape(bc,[1,-1])) - Xfit = np.concatenate(Xfit, 0) + + Xfit = np.concatenate(Xfit, 0) + + return Xfit + +class Entropy(BaseEstimator, TransformerMixin): + """ + This is a class for computing persistence entropy. Persistence entropy is a statistic for persistence diagrams inspired from Shannon entropy. This statistic can also be used to compute a feature vector, called the entropy summary function. See https://arxiv.org/pdf/1803.08304.pdf for more details. + """ + def __init__(self, mode="scalar", normalized=True, resolution=100, ent_range=[np.nan, np.nan]): + """ + Constructor for the Entropy class. + + Attributes: + mode (string): what entropy to compute: either "scalar" for computing the entropy statistics, or "vector" for computing the entropy summary functions (default "scalar"). + normalized (bool): whether to normalize the entropy summary function (default True). Used only if **mode** = "vector". + resolution (int): number of sample for the entropy summary function (default 100). Used only if **mode** = "vector". + ent_range ([double, double]): minimum and maximum of the entropy summary function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. Used only if **mode** = "vector". + """ + self.mode, self.normalized, self.resolution, self.ent_range = mode, normalized, resolution, ent_range + + def fit(self, X, y=None): + """ + Fit the Entropy class on a list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.ent_range)).any(): + pre = DiagramPreprocessor(use=True, scalers=[([0,1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = pre.scalers[0][1].data_min_, pre.scalers[0][1].data_max_ + self.ent_range = np.where(np.isnan(np.array(self.ent_range)), np.array([mx, My]), np.array(self.ent_range)) + return self + + def transform(self, X): + """ + Compute the entropy for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (1 if **mode** = "scalar" else **resolution**)): output entropy. + """ + num_diag, Xfit = len(X), [] + x_values = np.linspace(self.ent_range[0], self.ent_range[1], self.resolution) + step_x = x_values[1] - x_values[0] + new_X = DiagramPreprocessor(use=True, scalers=[([0,1], BirthPersistenceTransform())]).fit_transform(X) + + for i in range(num_diag): + + orig_diagram, diagram, num_pts_in_diag = X[i], new_X[i], X[i].shape[0] + new_diagram = DiagramPreprocessor(use=True, scalers=[([1], MaxAbsScaler())]).fit_transform([diagram])[0] + + if self.mode == "scalar": + ent = - np.sum( np.multiply(new_diagram[:,1], np.log(new_diagram[:,1])) ) + Xfit.append(np.array([[ent]])) + + else: + ent = np.zeros(self.resolution) + for j in range(num_pts_in_diag): + [px,py] = orig_diagram[j,:] + min_idx = np.minimum(np.maximum(np.ceil((px - self.ent_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.ent_range[0]) / step_x).astype(int), 0), self.resolution) + for k in range(min_idx, max_idx): + ent[k] += (-1) * new_diagram[j,1] * np.log(new_diagram[j,1]) + if self.normalized: + ent = ent / np.linalg.norm(ent, ord=1) + Xfit.append(np.reshape(ent,[1,-1])) + + Xfit = np.concatenate(Xfit, 0) return Xfit -- cgit v1.2.3 From 5e8144e8ddb4a39f9f4e60a9fafb5cc57f96acac Mon Sep 17 00:00:00 2001 From: mathieu Date: Mon, 9 Sep 2019 16:17:28 -0400 Subject: reviews --- src/cython/sktda/kernel_methods.py | 2 +- src/cython/sktda/metrics.py | 36 +++++++---- src/cython/sktda/preprocessing.py | 100 ++++++++++++++++++----------- src/cython/sktda/vector_methods.py | 126 ++++++++++++++++++------------------- 4 files changed, 149 insertions(+), 115 deletions(-) (limited to 'src') diff --git a/src/cython/sktda/kernel_methods.py b/src/cython/sktda/kernel_methods.py index 6e3f6d5e..d90bf164 100644 --- a/src/cython/sktda/kernel_methods.py +++ b/src/cython/sktda/kernel_methods.py @@ -101,7 +101,7 @@ class PersistenceWeightedGaussianKernel(BaseEstimator, TransformerMixin): W = np.matmul(self.ws_[i][:,np.newaxis], self.ws_[j][np.newaxis,:]) E = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.exp(-np.square(pairwise_distances(self.diagrams_[i], self.diagrams_[j]))/(2*np.square(self.bandwidth))) Xfit[i,j] = np.sum(np.multiply(W, E)) - Xfit[j,i] = X[i,j] + Xfit[j,i] = Xfit[i,j] else: ws = [ np.array([self.weight(Xp[i][j,:]) for j in range(Xp[i].shape[0])]) for i in range(len(Xp)) ] if self.kernel_approx is not None: diff --git a/src/cython/sktda/metrics.py b/src/cython/sktda/metrics.py index 5dc219cd..18db432a 100644 --- a/src/cython/sktda/metrics.py +++ b/src/cython/sktda/metrics.py @@ -145,7 +145,7 @@ class BottleneckDistance(BaseEstimator, TransformerMixin): class PersistenceFisherDistance(BaseEstimator, TransformerMixin): """ - This is a class for computing the persistence Fisher distance matrix from a list of persistence diagrams. The persistence Fisher distance is obtained by computing the original Fisher distance between the probability distributions associated to the persistence diagrams given by convolving them with a Gaussian kernel. See papers.nips.cc/paper/8205-persistence-fisher-kernel-a-riemannian-manifold-kernel-for-persistence-diagrams for more details. + This is a class for computing the persistence Fisher distance matrix from a list of persistence diagrams. The persistence Fisher distance is obtained by computing the original Fisher distance between the probability distributions associated to the persistence diagrams given by convolving them with a Gaussian kernel. See http://papers.nips.cc/paper/8205-persistence-fisher-kernel-a-riemannian-manifold-kernel-for-persistence-diagrams for more details. """ def __init__(self, bandwidth=1., kernel_approx=None): """ @@ -190,8 +190,12 @@ class PersistenceFisherDistance(BaseEstimator, TransformerMixin): if self.kernel_approx is not None: Z = np.concatenate([self.approx_[i], self.approx_diagonal_[i], self.approx_[j], self.approx_diagonal_[j]], axis=0) U, V = np.sum(np.concatenate([self.approx_[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], self.approx_diagonal_[i]], axis=0), axis=0) - vectori, vectorj = np.matmul(Z, U.T), np.matmul(Z, V.T) - vectori, vectorj = vectori/np.sum(vectori), vectorj/np.sum(vectorj) + vectori, vectorj = np.abs(np.matmul(Z, U.T)), np.abs(np.matmul(Z, V.T)) + vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) + if vectori_sum != 0: + vectori = vectori/vectori_sum + if vectorj_sum != 0: + vectorj = vectorj/vectorj_sum Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) Xfit[j,i] = Xfit[i,j] else: @@ -199,7 +203,11 @@ class PersistenceFisherDistance(BaseEstimator, TransformerMixin): U, V = np.concatenate([self.diagrams_[i], self.diagonal_projections_[j]], axis=0), np.concatenate([self.diagrams_[j], self.diagonal_projections_[i]], axis=0) vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) - vectori, vectorj = vectori/np.sum(vectori), vectorj/np.sum(vectorj) + vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) + if vectori_sum != 0: + vectori = vectori/vectori_sum + if vectorj_sum != 0: + vectorj = vectorj/vectorj_sum Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) Xfit[j,i] = Xfit[i,j] else: @@ -213,20 +221,22 @@ class PersistenceFisherDistance(BaseEstimator, TransformerMixin): if self.kernel_approx is not None: Z = np.concatenate([approx[i], approx_diagonal[i], self.approx_[j], self.approx_diagonal_[j]], axis=0) U, V = np.sum(np.concatenate([approx[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], approx_diagonal[i]], axis=0), axis=0) - vectori, vectorj = np.matmul(Z, U.T), np.matmul(Z, V.T) - if np.sum(vectori) != 0: - vectori = vectori/np.sum(vectori) - if np.sum(vectorj) != 0: - vectorj = vectorj/np.sum(vectorj) + vectori, vectorj = np.abs(np.matmul(Z, U.T)), np.abs(np.matmul(Z, V.T)) + vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) + if vectori_sum != 0: + vectori = vectori/vectori_sum + if vectorj_sum != 0: + vectorj = vectorj/vectorj_sum Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) else: Z = np.concatenate([X[i], diagonal_projections[i], self.diagrams_[j], self.diagonal_projections_[j]], axis=0) U, V = np.concatenate([X[i], self.diagonal_projections_[j]], axis=0), np.concatenate([self.diagrams_[j], diagonal_projections[i]], axis=0) vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) - if np.sum(vectori) != 0: - vectori = vectori/np.sum(vectori) - if np.sum(vectorj) != 0: - vectorj = vectorj/np.sum(vectorj) + vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) + if vectori_sum != 0: + vectori = vectori/vectori_sum + if vectorj_sum != 0: + vectorj = vectorj/vectorj_sum Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) return Xfit diff --git a/src/cython/sktda/preprocessing.py b/src/cython/sktda/preprocessing.py index b2bc68f6..512b02f3 100644 --- a/src/cython/sktda/preprocessing.py +++ b/src/cython/sktda/preprocessing.py @@ -13,7 +13,7 @@ from sklearn.preprocessing import StandardScaler class BirthPersistenceTransform(BaseEstimator, TransformerMixin): """ - This is a class for the affine transformation (x,y) -> (x,y-x) to be applied on persistence diagrams. It is a particular scaler for persistence diagram that can be given as input for the DiagramPreprocessor class. + This is a class for the affine transformation (x,y) -> (x,y-x) to be applied on persistence diagrams. """ def __init__(self): """ @@ -26,8 +26,8 @@ class BirthPersistenceTransform(BaseEstimator, TransformerMixin): Fit the BirthPersistenceTransform class on a list of persistence diagrams (this function actually does nothing but is useful when BirthPersistenceTransform is included in a scikit-learn Pipeline). Parameters: - X (n x 2 numpy array): input persistence diagram. - y (n x 1 array): persistence diagram label (unused). + X (n x 2 numpy array): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). """ return self @@ -36,16 +36,56 @@ class BirthPersistenceTransform(BaseEstimator, TransformerMixin): Apply the BirthPersistenceTransform function on the persistence diagrams. Parameters: - X (n x 2 numpy array): input persistence diagram. + X (list of n x 2 numpy array): input persistence diagrams. Returns: - Xfit (n x 2 numpy array): transformed persistence diagram. + Xfit (list of n x 2 numpy array): transformed persistence diagrams. """ - Xfit = np.matmul(X, np.array([[1., -1.],[0., 1.]])) + Xfit = [] + for diag in X: + new_diag = np.empty(diag.shape) + np.copyto(new_diag, diag) + new_diag[:,1] = new_diag[:,1] - new_diag[:,0] + Xfit.append(new_diag) return Xfit +class Clamping(BaseEstimator, TransformerMixin): + """ + This is a class for clamping values. It can be used as a parameter for the DiagramScaler class, for instance if you want to clamp abscissae or ordinates of persistence diagrams. + """ + def __init__(self, limit=np.inf): + """ + Constructor for the Clamping class. + + Attributes: + limit (double): clamping value (default np.inf). + """ + self.limit = limit + + def fit(self, X, y=None): + """ + Fit the Clamping class on a list of list of values (this function actually does nothing but is useful when Clamping is included in a scikit-learn Pipeline). + + Parameters: + X (list of numpy arrays of size n): input values. + y (n x 1 array): value labels (unused). + """ + return self + + def transform(self, X): + """ + Clamp each list of values individually. -class DiagramPreprocessor(BaseEstimator, TransformerMixin): + Parameters: + X (list of numpy arrays of size n): input list of list of values. + + Returns: + Xfit (list of numpy arrays of size n): output list of list of values. + """ + Xfit = [np.where(L >= self.limit, self.limit * np.ones(L.shape), L) for L in X] + return Xfit + +class DiagramScaler(BaseEstimator, TransformerMixin): """ This is a class for preprocessing persistence diagrams with a given list of scalers, such as those included in scikit-learn. """ @@ -116,6 +156,7 @@ class Padding(BaseEstimator, TransformerMixin): X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. y (n x 1 array): persistence diagram labels (unused). """ + self.max_pts = max([len(diag) for diag in X]) return self def transform(self, X): @@ -130,12 +171,9 @@ class Padding(BaseEstimator, TransformerMixin): """ if self.use: Xfit, num_diag = [], len(X) - max_card = max([len(diag) for diag in X]) for diag in X: - [num_pts, dim] = diag.shape - diag_pad = np.zeros([max_card, dim+1]) - diag_pad[:num_pts,:dim] = diag - diag_pad[:num_pts, dim] = np.ones(num_pts) + diag_pad = np.pad(diag, ((0,max(0, self.max_pts - diag.shape[0])), (0,1)), "constant", constant_values=((0,0),(0,0))) + diag_pad[:diag.shape[0],2] = np.ones(diag.shape[0]) Xfit.append(diag_pad) else: Xfit = X @@ -143,9 +181,9 @@ class Padding(BaseEstimator, TransformerMixin): class ProminentPoints(BaseEstimator, TransformerMixin): """ - This is a class for removing points that are close or far from the diagonal in persistence diagrams. + This is a class for removing points that are close or far from the diagonal in persistence diagrams. If persistence diagrams are n x 2 numpy arrays (i.e. persistence diagrams with ordinary features), points are ordered and thresholded by distance-to-diagonal. If persistence diagrams are n x 1 numpy arrays (i.e. persistence diagrams with essential features), points are not ordered and thresholded by first coordinate. """ - def __init__(self, use=False, num_pts=10, threshold=-1, location="upper", point_type="finite"): + def __init__(self, use=False, num_pts=10, threshold=-1, location="upper"): """ Constructor for the ProminentPoints class. @@ -154,13 +192,11 @@ class ProminentPoints(BaseEstimator, TransformerMixin): location (string): either "upper" or "lower" (default "upper"). Whether to keep the points that are far away ("upper") or close ("lower") to the diagonal. num_pts (int): cardinality threshold (default 10). If location == "upper", keep the top **num_pts** points that are the farthest away from the diagonal. If location == "lower", keep the top **num_pts** points that are the closest to the diagonal. threshold (double): distance-to-diagonal threshold (default -1). If location == "upper", keep the points that are at least at a distance **threshold** from the diagonal. If location == "lower", keep the points that are at most at a distance **threshold** from the diagonal. - point_type (string): either "finite" if persistence diagrams are n x 2 numpy arrays, or "essential" if persistence diagrams are n x 1 numpy arrays (default "finite"). If "finite", points are ordered and thresholded by distance-to-diagonal. If "essential", points are ordered and thresholded by first coordinate. """ self.num_pts = num_pts self.threshold = threshold self.use = use self.location = location - self.point_type = point_type def fit(self, X, y=None): """ @@ -174,7 +210,7 @@ class ProminentPoints(BaseEstimator, TransformerMixin): def transform(self, X): """ - If location == "upper", first select the top **num_pts** points that are the farthest away from the diagonal, then select and return from these points the ones that are at least at distance **threshold** from the diagonal for each persistence diagram individually. If location == "lower", first select the top **num_pts** points that are the closest to the diagonal, then select and return from these points the ones that are at most at distance **threshold** from the diagonal for each persistence diagram individually. If point_type == "essential", do the same with first coordinate instead of distance-to-diagonal. + If location == "upper", first select the top **num_pts** points that are the farthest away from the diagonal, then select and return from these points the ones that are at least at distance **threshold** from the diagonal for each persistence diagram individually. If location == "lower", first select the top **num_pts** points that are the closest to the diagonal, then select and return from these points the ones that are at most at distance **threshold** from the diagonal for each persistence diagram individually. Parameters: X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. @@ -186,16 +222,16 @@ class ProminentPoints(BaseEstimator, TransformerMixin): Xfit, num_diag = [], len(X) for i in range(num_diag): diag = X[i] - if self.point_type == "finite": + if diag.shape[1] >= 2: if diag.shape[0] > 0: - pers = np.abs(np.matmul(diag[:,:2], [-1., 1.])) + pers = np.abs(diag[:,1] - diag[:,0]) idx_thresh = pers >= self.threshold - thresh_diag, thresh_pers = diag[idx_thresh.flatten()], pers[idx_thresh.flatten()] + thresh_diag, thresh_pers = diag[idx_thresh], pers[idx_thresh] sort_index = np.flip(np.argsort(thresh_pers, axis=None), 0) if self.location == "upper": new_diag = thresh_diag[sort_index[:min(self.num_pts, thresh_diag.shape[0])],:] if self.location == "lower": - new_diag = np.concatenate( [ thresh_diag[sort_index[min(self.num_pts, thresh_diag.shape[0]):],:], diag[~idx_thresh.flatten()] ], axis=0) + new_diag = np.concatenate( [ thresh_diag[sort_index[min(self.num_pts, thresh_diag.shape[0]):],:], diag[~idx_thresh] ], axis=0) else: new_diag = diag @@ -203,11 +239,11 @@ class ProminentPoints(BaseEstimator, TransformerMixin): if diag.shape[0] > 0: birth = diag[:,:1] idx_thresh = birth >= self.threshold - thresh_diag, thresh_birth = diag[idx_thresh.flatten()], birth[idx_thresh.flatten()] + thresh_diag, thresh_birth = diag[idx_thresh], birth[idx_thresh] if self.location == "upper": new_diag = thresh_diag[:min(self.num_pts, thresh_diag.shape[0]),:] if self.location == "lower": - new_diag = np.concatenate( [ thresh_diag[min(self.num_pts, thresh_diag.shape[0]):,:], diag[~idx_thresh.flatten()] ], axis=0) + new_diag = np.concatenate( [ thresh_diag[min(self.num_pts, thresh_diag.shape[0]):,:], diag[~idx_thresh] ], axis=0) else: new_diag = diag @@ -254,21 +290,9 @@ class DiagramSelector(BaseEstimator, TransformerMixin): if self.use: Xfit, num_diag = [], len(X) if self.point_type == "finite": - for i in range(num_diag): - diag = X[i] - if diag.shape[0] != 0: - idx_fin = diag[:,1] != self.limit - Xfit.append(diag[idx_fin,:]) - else: - Xfit.append(diag) - if self.point_type == "essential": - for i in range(num_diag): - diag = X[i] - if diag.shape[0] != 0: - idx_ess = diag[:,1] == self.limit - Xfit.append(np.delete(diag,1,1)[idx_ess,:]) - else: - Xfit.append(np.delete(diag,1,1)) + Xfit = [ diag[diag[:,1] < self.limit] if diag.shape[0] != 0 else diag for diag in X] + else: + Xfit = [ diag[diag[:,1] == self.limit, 0:1] if diag.shape[0] != 0 else diag for diag in X] else: Xfit = X return Xfit diff --git a/src/cython/sktda/vector_methods.py b/src/cython/sktda/vector_methods.py index 99c2f420..3862f815 100644 --- a/src/cython/sktda/vector_methods.py +++ b/src/cython/sktda/vector_methods.py @@ -8,7 +8,7 @@ from sklearn.base import BaseEstimator, TransformerMixin from sklearn.preprocessing import MinMaxScaler, MaxAbsScaler from sklearn.neighbors import DistanceMetric -from .preprocessing import DiagramPreprocessor, BirthPersistenceTransform +from .preprocessing import DiagramScaler, BirthPersistenceTransform ############################################# # Finite Vectorization methods ############## @@ -40,15 +40,15 @@ class PersistenceImage(BaseEstimator, TransformerMixin): y (n x 1 array): persistence diagram labels (unused). """ if np.isnan(np.array(self.im_range)).any(): - new_X = DiagramPreprocessor(use=True, scalers=[([0,1], BirthPersistenceTransform())]).fit_transform(X) - pre = DiagramPreprocessor(use=True, scalers=[([0,1], MinMaxScaler())]).fit(new_X,y) - [mx,my],[Mx,My] = pre.scalers[0][1].data_min_, pre.scalers[0][1].data_max_ + new_X = BirthPersistenceTransform().fit_transform(X) + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(new_X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] self.im_range = np.where(np.isnan(np.array(self.im_range)), np.array([mx, Mx, my, My]), np.array(self.im_range)) return self def transform(self, X): """ - Compute the persistence image for each persistence diagram individually and concatenate the results. + Compute the persistence image for each persistence diagram individually and store the results in a single numpy array. Parameters: X (list of n x 2 numpy arrays): input persistence diagrams. @@ -57,13 +57,13 @@ class PersistenceImage(BaseEstimator, TransformerMixin): Xfit (numpy array with shape (number of diagrams) x (number of pixels = **resolution[0]** x **resolution[1]**)): output persistence images. """ num_diag, Xfit = len(X), [] - new_X = DiagramPreprocessor(use=True, scalers=[([0,1], BirthPersistenceTransform())]).fit_transform(X) + new_X = BirthPersistenceTransform().fit_transform(X) for i in range(num_diag): diagram, num_pts_in_diag = new_X[i], X[i].shape[0] - w = np.ones(num_pts_in_diag) + w = np.empty(num_pts_in_diag) for j in range(num_pts_in_diag): w[j] = self.weight(diagram[j,:]) @@ -81,29 +81,29 @@ class Landscape(BaseEstimator, TransformerMixin): """ This is a class for computing persistence landscapes from a list of persistence diagrams. A persistence landscape is a collection of 1D piecewise-linear functions computed from the rank function associated to the persistence diagram. These piecewise-linear functions are then sampled uniformly on a given range and the corresponding vectors of samples are concatenated and returned. See http://jmlr.org/papers/v16/bubenik15a.html for more details. """ - def __init__(self, num_landscapes=5, resolution=100, ls_range=[np.nan, np.nan]): + def __init__(self, num_landscapes=5, resolution=100, sample_range=[np.nan, np.nan]): """ Constructor for the Landscape class. Attributes: num_landscapes (int): number of piecewise-linear functions to output (default 5). resolution (int): number of sample for all piecewise-linear functions (default 100). - ls_range ([double, double]): minimum and maximum of all piecewise-linear function domains, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + sample_range ([double, double]): minimum and maximum of all piecewise-linear function domains, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. """ - self.num_landscapes, self.resolution, self.ls_range = num_landscapes, resolution, ls_range + self.num_landscapes, self.resolution, self.sample_range = num_landscapes, resolution, sample_range def fit(self, X, y=None): """ - Fit the Landscape class on a list of persistence diagrams: if any of the values in **ls_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + Fit the Landscape class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. Parameters: X (list of n x 2 numpy arrays): input persistence diagrams. y (n x 1 array): persistence diagram labels (unused). """ - if np.isnan(np.array(self.ls_range)).any(): - pre = DiagramPreprocessor(use=True, scalers=[([0,1], MinMaxScaler())]).fit(X,y) - [mx,my],[Mx,My] = pre.scalers[0][1].data_min_, pre.scalers[0][1].data_max_ - self.ls_range = np.where(np.isnan(np.array(self.ls_range)), np.array([mx, My]), np.array(self.ls_range)) + if np.isnan(np.array(self.sample_range)).any(): + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) return self def transform(self, X): @@ -117,7 +117,7 @@ class Landscape(BaseEstimator, TransformerMixin): Xfit (numpy array with shape (number of diagrams) x (number of samples = **num_landscapes** x **resolution**)): output persistence landscapes. """ num_diag, Xfit = len(X), [] - x_values = np.linspace(self.ls_range[0], self.ls_range[1], self.resolution) + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) step_x = x_values[1] - x_values[0] for i in range(num_diag): @@ -131,19 +131,19 @@ class Landscape(BaseEstimator, TransformerMixin): events.append([]) for j in range(num_pts_in_diag): - [px,py] = diagram[j,:] - min_idx = np.minimum(np.maximum(np.ceil((px - self.ls_range[0]) / step_x).astype(int), 0), self.resolution) - mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.ls_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.ls_range[0]) / step_x).astype(int), 0), self.resolution) + [px,py] = diagram[j,:2] + min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) if min_idx < self.resolution and max_idx > 0: - landscape_value = self.ls_range[0] + min_idx * step_x - px + landscape_value = self.sample_range[0] + min_idx * step_x - px for k in range(min_idx, mid_idx): events[k].append(landscape_value) landscape_value += step_x - landscape_value = py - self.ls_range[0] - mid_idx * step_x + landscape_value = py - self.sample_range[0] - mid_idx * step_x for k in range(mid_idx, max_idx): events[k].append(landscape_value) landscape_value -= step_x @@ -163,29 +163,29 @@ class Silhouette(BaseEstimator, TransformerMixin): """ This is a class for computing persistence silhouettes from a list of persistence diagrams. A persistence silhouette is computed by taking a weighted average of the collection of 1D piecewise-linear functions given by the persistence landscapes, and then by uniformly sampling this average on a given range. Finally, the corresponding vector of samples is returned. See https://arxiv.org/abs/1312.0308 for more details. """ - def __init__(self, weight=lambda x: 1, resolution=100, sh_range=[np.nan, np.nan]): + def __init__(self, weight=lambda x: 1, resolution=100, sample_range=[np.nan, np.nan]): """ Constructor for the Silhouette class. Attributes: weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie on lists or numpy arrays of the form [p_x,p_y]. resolution (int): number of samples for the weighted average (default 100). - sh_range ([double, double]): minimum and maximum for the weighted average domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + sample_range ([double, double]): minimum and maximum for the weighted average domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. """ - self.weight, self.resolution, self.sh_range = weight, resolution, sh_range + self.weight, self.resolution, self.sample_range = weight, resolution, sample_range def fit(self, X, y=None): """ - Fit the Silhouette class on a list of persistence diagrams: if any of the values in **sh_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + Fit the Silhouette class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. Parameters: X (list of n x 2 numpy arrays): input persistence diagrams. y (n x 1 array): persistence diagram labels (unused). """ - if np.isnan(np.array(self.sh_range)).any(): - pre = DiagramPreprocessor(use=True, scalers=[([0,1], MinMaxScaler())]).fit(X,y) - [mx,my],[Mx,My] = pre.scalers[0][1].data_min_, pre.scalers[0][1].data_max_ - self.sh_range = np.where(np.isnan(np.array(self.sh_range)), np.array([mx, My]), np.array(self.sh_range)) + if np.isnan(np.array(self.sample_range)).any(): + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) return self def transform(self, X): @@ -199,7 +199,7 @@ class Silhouette(BaseEstimator, TransformerMixin): Xfit (numpy array with shape (number of diagrams) x (**resolution**): output persistence silhouettes. """ num_diag, Xfit = len(X), [] - x_values = np.linspace(self.sh_range[0], self.sh_range[1], self.resolution) + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) step_x = x_values[1] - x_values[0] for i in range(num_diag): @@ -213,20 +213,20 @@ class Silhouette(BaseEstimator, TransformerMixin): for j in range(num_pts_in_diag): - [px,py] = diagram[j,:] + [px,py] = diagram[j,:2] weight = weights[j] / total_weight - min_idx = np.minimum(np.maximum(np.ceil((px - self.sh_range[0]) / step_x).astype(int), 0), self.resolution) - mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sh_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.sh_range[0]) / step_x).astype(int), 0), self.resolution) + min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) if min_idx < self.resolution and max_idx > 0: - silhouette_value = self.sh_range[0] + min_idx * step_x - px + silhouette_value = self.sample_range[0] + min_idx * step_x - px for k in range(min_idx, mid_idx): sh[k] += weight * silhouette_value silhouette_value += step_x - silhouette_value = py - self.sh_range[0] - mid_idx * step_x + silhouette_value = py - self.sample_range[0] - mid_idx * step_x for k in range(mid_idx, max_idx): sh[k] += weight * silhouette_value silhouette_value -= step_x @@ -241,28 +241,28 @@ class BettiCurve(BaseEstimator, TransformerMixin): """ This is a class for computing Betti curves from a list of persistence diagrams. A Betti curve is a 1D piecewise-constant function obtained from the rank function. It is sampled uniformly on a given range and the vector of samples is returned. See https://www.researchgate.net/publication/316604237_Time_Series_Classification_via_Topological_Data_Analysis for more details. """ - def __init__(self, resolution=100, bc_range=[np.nan, np.nan]): + def __init__(self, resolution=100, sample_range=[np.nan, np.nan]): """ Constructor for the BettiCurve class. Attributes: resolution (int): number of sample for the piecewise-constant function (default 100). - bc_range ([double, double]): minimum and maximum of the piecewise-constant function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + sample_range ([double, double]): minimum and maximum of the piecewise-constant function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. """ - self.resolution, self.bc_range = resolution, bc_range + self.resolution, self.sample_range = resolution, sample_range def fit(self, X, y=None): """ - Fit the BettiCurve class on a list of persistence diagrams: if any of the values in **bc_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + Fit the BettiCurve class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. Parameters: X (list of n x 2 numpy arrays): input persistence diagrams. y (n x 1 array): persistence diagram labels (unused). """ - if np.isnan(np.array(self.bc_range)).any(): - pre = DiagramPreprocessor(use=True, scalers=[([0,1], MinMaxScaler())]).fit(X,y) - [mx,my],[Mx,My] = pre.scalers[0][1].data_min_, pre.scalers[0][1].data_max_ - self.bc_range = np.where(np.isnan(np.array(self.bc_range)), np.array([mx, My]), np.array(self.bc_range)) + if np.isnan(np.array(self.sample_range)).any(): + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) return self def transform(self, X): @@ -276,7 +276,7 @@ class BettiCurve(BaseEstimator, TransformerMixin): Xfit (numpy array with shape (number of diagrams) x (**resolution**): output Betti curves. """ num_diag, Xfit = len(X), [] - x_values = np.linspace(self.bc_range[0], self.bc_range[1], self.resolution) + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) step_x = x_values[1] - x_values[0] for i in range(num_diag): @@ -285,9 +285,9 @@ class BettiCurve(BaseEstimator, TransformerMixin): bc = np.zeros(self.resolution) for j in range(num_pts_in_diag): - [px,py] = diagram[j,:] - min_idx = np.minimum(np.maximum(np.ceil((px - self.bc_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.bc_range[0]) / step_x).astype(int), 0), self.resolution) + [px,py] = diagram[j,:2] + min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) for k in range(min_idx, max_idx): bc[k] += 1 @@ -301,7 +301,7 @@ class Entropy(BaseEstimator, TransformerMixin): """ This is a class for computing persistence entropy. Persistence entropy is a statistic for persistence diagrams inspired from Shannon entropy. This statistic can also be used to compute a feature vector, called the entropy summary function. See https://arxiv.org/pdf/1803.08304.pdf for more details. """ - def __init__(self, mode="scalar", normalized=True, resolution=100, ent_range=[np.nan, np.nan]): + def __init__(self, mode="scalar", normalized=True, resolution=100, sample_range=[np.nan, np.nan]): """ Constructor for the Entropy class. @@ -309,9 +309,9 @@ class Entropy(BaseEstimator, TransformerMixin): mode (string): what entropy to compute: either "scalar" for computing the entropy statistics, or "vector" for computing the entropy summary functions (default "scalar"). normalized (bool): whether to normalize the entropy summary function (default True). Used only if **mode** = "vector". resolution (int): number of sample for the entropy summary function (default 100). Used only if **mode** = "vector". - ent_range ([double, double]): minimum and maximum of the entropy summary function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. Used only if **mode** = "vector". + sample_range ([double, double]): minimum and maximum of the entropy summary function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. Used only if **mode** = "vector". """ - self.mode, self.normalized, self.resolution, self.ent_range = mode, normalized, resolution, ent_range + self.mode, self.normalized, self.resolution, self.sample_range = mode, normalized, resolution, sample_range def fit(self, X, y=None): """ @@ -321,10 +321,10 @@ class Entropy(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. y (n x 1 array): persistence diagram labels (unused). """ - if np.isnan(np.array(self.ent_range)).any(): - pre = DiagramPreprocessor(use=True, scalers=[([0,1], MinMaxScaler())]).fit(X,y) - [mx,my],[Mx,My] = pre.scalers[0][1].data_min_, pre.scalers[0][1].data_max_ - self.ent_range = np.where(np.isnan(np.array(self.ent_range)), np.array([mx, My]), np.array(self.ent_range)) + if np.isnan(np.array(self.sample_range)).any(): + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) return self def transform(self, X): @@ -338,14 +338,14 @@ class Entropy(BaseEstimator, TransformerMixin): Xfit (numpy array with shape (number of diagrams) x (1 if **mode** = "scalar" else **resolution**)): output entropy. """ num_diag, Xfit = len(X), [] - x_values = np.linspace(self.ent_range[0], self.ent_range[1], self.resolution) + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) step_x = x_values[1] - x_values[0] - new_X = DiagramPreprocessor(use=True, scalers=[([0,1], BirthPersistenceTransform())]).fit_transform(X) + new_X = BirthPersistenceTransform().fit_transform(X) for i in range(num_diag): orig_diagram, diagram, num_pts_in_diag = X[i], new_X[i], X[i].shape[0] - new_diagram = DiagramPreprocessor(use=True, scalers=[([1], MaxAbsScaler())]).fit_transform([diagram])[0] + new_diagram = DiagramScaler(use=True, scalers=[([1], MaxAbsScaler())]).fit_transform([diagram])[0] if self.mode == "scalar": ent = - np.sum( np.multiply(new_diagram[:,1], np.log(new_diagram[:,1])) ) @@ -354,9 +354,9 @@ class Entropy(BaseEstimator, TransformerMixin): else: ent = np.zeros(self.resolution) for j in range(num_pts_in_diag): - [px,py] = orig_diagram[j,:] - min_idx = np.minimum(np.maximum(np.ceil((px - self.ent_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.ent_range[0]) / step_x).astype(int), 0), self.resolution) + [px,py] = orig_diagram[j,:2] + min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) for k in range(min_idx, max_idx): ent[k] += (-1) * new_diagram[j,1] * np.log(new_diagram[j,1]) if self.normalized: @@ -411,7 +411,7 @@ class TopologicalVector(BaseEstimator, TransformerMixin): for i in range(num_diag): diagram, num_pts_in_diag = X[i], X[i].shape[0] - pers = 0.5 * np.matmul(diagram, np.array([[-1.0],[1.0]])) + pers = 0.5 * (diagram[:,1]-diagram[:,0]) min_pers = np.minimum(pers,np.transpose(pers)) distances = DistanceMetric.get_metric("chebyshev").pairwise(diagram) vect = np.flip(np.sort(np.triu(np.minimum(distances, min_pers)), axis=None), 0) -- cgit v1.2.3 From e83f7bbc33170ea04a9c2b4db1767ab51bfb4a56 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 10 Sep 2019 16:40:15 +0200 Subject: Move cython directory stuff to python directory. Make example work. Fix CMake for sktda. Make sktda a submodule of gudhi package. --- src/cython/example/ex_diagrams.py | 107 ------- src/cython/sktda/kernel_methods.py | 202 ------------- src/cython/sktda/metrics.py | 242 ---------------- src/cython/sktda/preprocessing.py | 298 ------------------- src/cython/sktda/vector_methods.py | 481 ------------------------------- src/python/CMakeLists.txt | 2 + src/python/example/ex_diagrams.py | 106 +++++++ src/python/gudhi/sktda/__init__.py | 6 + src/python/gudhi/sktda/kernel_methods.py | 202 +++++++++++++ src/python/gudhi/sktda/metrics.py | 239 +++++++++++++++ src/python/gudhi/sktda/preprocessing.py | 298 +++++++++++++++++++ src/python/gudhi/sktda/vector_methods.py | 481 +++++++++++++++++++++++++++++++ 12 files changed, 1334 insertions(+), 1330 deletions(-) delete mode 100644 src/cython/example/ex_diagrams.py delete mode 100644 src/cython/sktda/kernel_methods.py delete mode 100644 src/cython/sktda/metrics.py delete mode 100644 src/cython/sktda/preprocessing.py delete mode 100644 src/cython/sktda/vector_methods.py create mode 100644 src/python/example/ex_diagrams.py create mode 100644 src/python/gudhi/sktda/__init__.py create mode 100644 src/python/gudhi/sktda/kernel_methods.py create mode 100644 src/python/gudhi/sktda/metrics.py create mode 100644 src/python/gudhi/sktda/preprocessing.py create mode 100644 src/python/gudhi/sktda/vector_methods.py (limited to 'src') diff --git a/src/cython/example/ex_diagrams.py b/src/cython/example/ex_diagrams.py deleted file mode 100644 index aee20245..00000000 --- a/src/cython/example/ex_diagrams.py +++ /dev/null @@ -1,107 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np -from sklearn.kernel_approximation import RBFSampler - -import sys -sys.path.append("../sktda/") -from preprocessing import * -from metrics import * -from vector_methods import * -from kernel_methods import * - -D = np.array([[0.,4.],[1.,2.],[3.,8.],[6.,8.]]) -plt.scatter(D[:,0],D[:,1]) -plt.plot([0.,10.],[0.,10.]) -plt.show() - -diags = [D] - -LS = Landscape(resolution = 1000) -L = LS.fit_transform(diags) -plt.plot(L[0][:1000]) -plt.plot(L[0][1000:2000]) -plt.plot(L[0][2000:3000]) -plt.show() - -def pow(n): - return lambda x: np.power(x[1]-x[0],n) - -SH = Silhouette(resolution=1000, weight=pow(2)) -sh = SH.fit_transform(diags) -plt.plot(sh[0]) -plt.show() - -BC = BettiCurve(resolution=1000) -bc = BC.fit_transform(diags) -plt.plot(bc[0]) -plt.show() - -CP = ComplexPolynomial(threshold=-1, F="T") -cp = CP.fit_transform(diags) -print("Complex polynomial is " + str(cp[0,:])) - -TV = TopologicalVector(threshold=-1) -tv = TV.fit_transform(diags) -print("Topological vector is " + str(tv[0,:])) - -diagsT = DiagramPreprocessor(use=True, scalers=[([0,1], BirthPersistenceTransform())]).fit_transform(diags) -PI = PersistenceImage(bandwidth=1., weight=lambda x: x[1], im_range=[0,10,0,10], resolution=[100,100]) -pi = PI.fit_transform(diagsT) -plt.imshow(np.flip(np.reshape(pi[0], [100,100]), 0)) -plt.show() - -plt.scatter(D[:,0],D[:,1]) -D = np.array([[1.,5.],[3.,6.],[2.,7.]]) -plt.scatter(D[:,0],D[:,1]) -plt.plot([0.,10.],[0.,10.]) -plt.show() - -diags2 = [D] - -def arctan(C,p): - return lambda x: C*np.arctan(np.power(x[1], p)) - -PWG = PersistenceWeightedGaussianKernel(bandwidth=1., kernel_approx=None, weight=arctan(1.,1.)) -X = PWG.fit(diags) -Y = PWG.transform(diags2) -print("PWG kernel is " + str(Y[0][0])) - -PWG = PersistenceWeightedGaussianKernel(kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2])), weight=arctan(1.,1.)) -X = PWG.fit(diags) -Y = PWG.transform(diags2) -print("Approximate PWG kernel is " + str(Y[0][0])) - -PSS = PersistenceScaleSpaceKernel(bandwidth=1.) -X = PSS.fit(diags) -Y = PSS.transform(diags2) -print("PSS kernel is " + str(Y[0][0])) - -PSS = PersistenceScaleSpaceKernel(kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2]))) -X = PSS.fit(diags) -Y = PSS.transform(diags2) -print("Approximate PSS kernel is " + str(Y[0][0])) - -sW = SlicedWassersteinDistance(num_directions=100) -X = sW.fit(diags) -Y = sW.transform(diags2) -print("SW distance is " + str(Y[0][0])) - -SW = SlicedWassersteinKernel(num_directions=100, bandwidth=1.) -X = SW.fit(diags) -Y = SW.transform(diags2) -print("SW kernel is " + str(Y[0][0])) - -W = BottleneckDistance(epsilon=.001) -X = W.fit(diags) -Y = W.transform(diags2) -print("Bottleneck distance is " + str(Y[0][0])) - -PF = PersistenceFisherKernel(bandwidth_fisher=1., bandwidth=1.) -X = PF.fit(diags) -Y = PF.transform(diags2) -print("PF kernel is " + str(Y[0][0])) - -PF = PersistenceFisherKernel(bandwidth_fisher=1., bandwidth=1., kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2]))) -X = PF.fit(diags) -Y = PF.transform(diags2) -print("Approximate PF kernel is " + str(Y[0][0])) diff --git a/src/cython/sktda/kernel_methods.py b/src/cython/sktda/kernel_methods.py deleted file mode 100644 index d90bf164..00000000 --- a/src/cython/sktda/kernel_methods.py +++ /dev/null @@ -1,202 +0,0 @@ -""" -@author: Mathieu Carriere -All rights reserved -""" - -import numpy as np -from sklearn.base import BaseEstimator, TransformerMixin -from sklearn.metrics import pairwise_distances -from .metrics import SlicedWassersteinDistance, PersistenceFisherDistance - -############################################# -# Kernel methods ############################ -############################################# - -class SlicedWassersteinKernel(BaseEstimator, TransformerMixin): - """ - This is a class for computing the sliced Wasserstein kernel matrix from a list of persistence diagrams. The sliced Wasserstein kernel is computed by exponentiating the corresponding sliced Wasserstein distance with a Gaussian kernel. See http://proceedings.mlr.press/v70/carriere17a.html for more details. - """ - def __init__(self, num_directions=10, bandwidth=1.0): - """ - Constructor for the SlicedWassersteinDistance class. - - Attributes: - bandwidth (double): bandwidth of the Gaussian kernel applied to the sliced Wasserstein distance (default 1.). - num_directions (int): number of lines to sample uniformly from [-pi,pi] in order to approximate and speed up the kernel computation (default 10). If -1, the exact kernel is computed. - """ - self.bandwidth = bandwidth - self.sw_ = SlicedWassersteinDistance(num_directions=num_directions) - - def fit(self, X, y=None): - """ - Fit the SlicedWassersteinKernel class on a list of persistence diagrams: an instance of the SlicedWassersteinDistance class is fitted on the diagrams and then stored. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.sw_.fit(X, y) - return self - - def transform(self, X): - """ - Compute all sliced Wasserstein kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise sliced Wasserstein kernel values. - """ - return np.exp(-self.sw_.transform(X)/self.bandwidth) - -class PersistenceWeightedGaussianKernel(BaseEstimator, TransformerMixin): - """ - This is a class for computing the persistence weighted Gaussian kernel matrix from a list of persistence diagrams. The persistence weighted Gaussian kernel is computed by convolving the persistence diagram points with weighted Gaussian kernels. See http://proceedings.mlr.press/v48/kusano16.html for more details. - """ - def __init__(self, bandwidth=1., weight=lambda x: 1, kernel_approx=None): - """ - Constructor for the PersistenceWeightedGaussianKernel class. - - Attributes: - bandwidth (double): bandwidth of the Gaussian kernel with which persistence diagrams will be convolved (default 1.) - weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie lists or numpy arrays of the form [p_x,p_y]. - kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). - """ - self.bandwidth, self.weight = bandwidth, weight - self.kernel_approx = kernel_approx - - def fit(self, X, y=None): - """ - Fit the PersistenceWeightedGaussianKernel class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams** and the kernel approximation class (if not None) is applied on them. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.diagrams_ = list(X) - self.ws_ = [ np.array([self.weight(self.diagrams_[i][j,:]) for j in range(self.diagrams_[i].shape[0])]) for i in range(len(self.diagrams_)) ] - if self.kernel_approx is not None: - self.approx_ = np.concatenate([np.sum(np.multiply(self.ws_[i][:,np.newaxis], self.kernel_approx.transform(self.diagrams_[i])), axis=0)[np.newaxis,:] for i in range(len(self.diagrams_))]) - return self - - def transform(self, X): - """ - Compute all sliced Wasserstein kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence weighted Gaussian kernel values. - """ - Xp = list(X) - Xfit = np.zeros((len(Xp), len(self.diagrams_))) - if len(self.diagrams_) == len(Xp) and np.all([np.array_equal(self.diagrams_[i], Xp[i]) for i in range(len(Xp))]): - if self.kernel_approx is not None: - Xfit = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.matmul(self.approx_, self.approx_.T) - else: - for i in range(len(self.diagrams_)): - for j in range(i+1, len(self.diagrams_)): - W = np.matmul(self.ws_[i][:,np.newaxis], self.ws_[j][np.newaxis,:]) - E = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.exp(-np.square(pairwise_distances(self.diagrams_[i], self.diagrams_[j]))/(2*np.square(self.bandwidth))) - Xfit[i,j] = np.sum(np.multiply(W, E)) - Xfit[j,i] = Xfit[i,j] - else: - ws = [ np.array([self.weight(Xp[i][j,:]) for j in range(Xp[i].shape[0])]) for i in range(len(Xp)) ] - if self.kernel_approx is not None: - approx = np.concatenate([np.sum(np.multiply(ws[i][:,np.newaxis], self.kernel_approx.transform(Xp[i])), axis=0)[np.newaxis,:] for i in range(len(Xp))]) - Xfit = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.matmul(approx, self.approx_.T) - else: - for i in range(len(Xp)): - for j in range(len(self.diagrams_)): - W = np.matmul(ws[i][:,np.newaxis], self.ws_[j][np.newaxis,:]) - E = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.exp(-np.square(pairwise_distances(Xp[i], self.diagrams_[j]))/(2*np.square(self.bandwidth))) - Xfit[i,j] = np.sum(np.multiply(W, E)) - - return Xfit - -class PersistenceScaleSpaceKernel(BaseEstimator, TransformerMixin): - """ - This is a class for computing the persistence scale space kernel matrix from a list of persistence diagrams. The persistence scale space kernel is computed by adding the symmetric to the diagonal of each point in each persistence diagram, and then convolving the points with a Gaussian kernel. See https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Reininghaus_A_Stable_Multi-Scale_2015_CVPR_paper.pdf for more details. - """ - def __init__(self, bandwidth=1., kernel_approx=None): - """ - Constructor for the PersistenceScaleSpaceKernel class. - - Attributes: - bandwidth (double): bandwidth of the Gaussian kernel with which persistence diagrams will be convolved (default 1.) - kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). - """ - self.pwg_ = PersistenceWeightedGaussianKernel(bandwidth=bandwidth, weight=lambda x: 1 if x[1] >= x[0] else -1, kernel_approx=kernel_approx) - - def fit(self, X, y=None): - """ - Fit the PersistenceScaleSpaceKernel class on a list of persistence diagrams: symmetric to the diagonal of all points are computed and an instance of the PersistenceWeightedGaussianKernel class is fitted on the diagrams and then stored. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.diagrams_ = list(X) - for i in range(len(self.diagrams_)): - op_D = np.matmul(self.diagrams_[i], np.array([[0.,1.], [1.,0.]])) - self.diagrams_[i] = np.concatenate([self.diagrams_[i], op_D], axis=0) - self.pwg_.fit(X) - return self - - def transform(self, X): - """ - Compute all persistence scale space kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence scale space kernel values. - """ - Xp = list(X) - for i in range(len(Xp)): - op_X = np.matmul(Xp[i], np.array([[0.,1.], [1.,0.]])) - Xp[i] = np.concatenate([Xp[i], op_X], axis=0) - return self.pwg_.transform(Xp) - -class PersistenceFisherKernel(BaseEstimator, TransformerMixin): - """ - This is a class for computing the persistence Fisher kernel matrix from a list of persistence diagrams. The persistence Fisher kernel is computed by exponentiating the corresponding persistence Fisher distance with a Gaussian kernel. See papers.nips.cc/paper/8205-persistence-fisher-kernel-a-riemannian-manifold-kernel-for-persistence-diagrams for more details. - """ - def __init__(self, bandwidth_fisher=1., bandwidth=1., kernel_approx=None): - """ - Constructor for the PersistenceFisherKernel class. - - Attributes: - bandwidth (double): bandwidth of the Gaussian kernel applied to the persistence Fisher distance (default 1.). - bandwidth_fisher (double): bandwidth of the Gaussian kernel used to turn persistence diagrams into probability distributions by PersistenceFisherDistance class (default 1.). - kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). - """ - self.bandwidth = bandwidth - self.pf_ = PersistenceFisherDistance(bandwidth=bandwidth_fisher, kernel_approx=kernel_approx) - - def fit(self, X, y=None): - """ - Fit the PersistenceFisherKernel class on a list of persistence diagrams: an instance of the PersistenceFisherDistance class is fitted on the diagrams and then stored. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.pf_.fit(X, y) - return self - - def transform(self, X): - """ - Compute all persistence Fisher kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence Fisher kernel values. - """ - return np.exp(-self.pf_.transform(X)/self.bandwidth) - diff --git a/src/cython/sktda/metrics.py b/src/cython/sktda/metrics.py deleted file mode 100644 index 18db432a..00000000 --- a/src/cython/sktda/metrics.py +++ /dev/null @@ -1,242 +0,0 @@ -""" -@author: Mathieu Carriere -All rights reserved -""" - -import sys -import numpy as np -from sklearn.base import BaseEstimator, TransformerMixin -from sklearn.metrics import pairwise_distances -try: - from gudhi import bottleneck_distance - USE_GUDHI = True -except ImportError: - USE_GUDHI = False - print("Gudhi not found: BottleneckDistance will return null matrix, and exact SlicedWassersteinDistance not available") - -############################################# -# Metrics ################################### -############################################# - -class SlicedWassersteinDistance(BaseEstimator, TransformerMixin): - """ - This is a class for computing the sliced Wasserstein distance matrix from a list of persistence diagrams. The Sliced Wasserstein distance is computed by projecting the persistence diagrams onto lines, comparing the projections with the 1-norm, and finally integrating over all possible lines. See http://proceedings.mlr.press/v70/carriere17a.html for more details. - """ - def __init__(self, num_directions=10): - """ - Constructor for the SlicedWassersteinDistance class. - - Attributes: - num_directions (int): number of lines to sample uniformly from [-pi,pi] in order to approximate and speed up the distance computation (default 10). - """ - self.num_directions = num_directions - thetas = np.linspace(-np.pi/2, np.pi/2, num=self.num_directions+1)[np.newaxis,:-1] - self.lines_ = np.concatenate([np.cos(thetas), np.sin(thetas)], axis=0) - - def fit(self, X, y=None): - """ - Fit the SlicedWassersteinDistance class on a list of persistence diagrams: persistence diagrams are projected onto the different lines. The diagrams themselves and their projections are then stored in a numpy array called **diagrams**. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.diagrams_ = X - self.approx_ = [np.matmul(X[i], self.lines_) for i in range(len(X))] - diag_proj = (1./2) * np.ones((2,2)) - self.approx_diag_ = [np.matmul(np.matmul(X[i], diag_proj), self.lines_) for i in range(len(X))] - return self - - def transform(self, X): - """ - Compute all sliced Wasserstein distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise sliced Wasserstein distances. - """ - Xfit = np.zeros((len(X), len(self.approx_))) - if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): - for i in range(len(self.approx_)): - for j in range(i+1, len(self.approx_)): - A = np.sort(np.concatenate([self.approx_[i], self.approx_diag_[j]], axis=0), axis=0) - B = np.sort(np.concatenate([self.approx_[j], self.approx_diag_[i]], axis=0), axis=0) - L1 = np.sum(np.abs(A-B), axis=0) - Xfit[i,j] = np.mean(L1) - Xfit[j,i] = Xfit[i,j] - else: - diag_proj = (1./2) * np.ones((2,2)) - approx = [np.matmul(X[i], self.lines_) for i in range(len(X))] - approx_diag = [np.matmul(np.matmul(X[i], diag_proj), self.lines_) for i in range(len(X))] - for i in range(len(approx)): - for j in range(len(self.approx_)): - A = np.sort(np.concatenate([approx[i], self.approx_diag_[j]], axis=0), axis=0) - B = np.sort(np.concatenate([self.approx_[j], approx_diag[i]], axis=0), axis=0) - L1 = np.sum(np.abs(A-B), axis=0) - Xfit[i,j] = np.mean(L1) - - return Xfit - -class BottleneckDistance(BaseEstimator, TransformerMixin): - """ - This is a class for computing the bottleneck distance matrix from a list of persistence diagrams. - """ - def __init__(self, epsilon=1e-3): - """ - Constructor for the BottleneckDistance class. - - Attributes: - epsilon (double): approximation quality (default 1e-4). - """ - self.epsilon = epsilon - - def fit(self, X, y=None): - """ - Fit the BottleneckDistance class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams**. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.diagrams_ = X - return self - - def transform(self, X): - """ - Compute all bottleneck distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise bottleneck distances. - """ - num_diag1 = len(X) - - if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): - matrix = np.zeros((num_diag1, num_diag1)) - - if USE_GUDHI: - for i in range(num_diag1): - #sys.stdout.write( str(i*1.0 / num_diag1) + "\r") - for j in range(i+1, num_diag1): - matrix[i,j] = bottleneck_distance(X[i], X[j], self.epsilon) - matrix[j,i] = matrix[i,j] - else: - print("Gudhi required---returning null matrix") - - else: - num_diag2 = len(self.diagrams_) - matrix = np.zeros((num_diag1, num_diag2)) - - if USE_GUDHI: - for i in range(num_diag1): - #sys.stdout.write( str(i*1.0 / num_diag1) + "\r") - for j in range(num_diag2): - matrix[i,j] = bottleneck_distance(X[i], self.diagrams_[j], self.epsilon) - else: - print("Gudhi required---returning null matrix") - - Xfit = matrix - - return Xfit - -class PersistenceFisherDistance(BaseEstimator, TransformerMixin): - """ - This is a class for computing the persistence Fisher distance matrix from a list of persistence diagrams. The persistence Fisher distance is obtained by computing the original Fisher distance between the probability distributions associated to the persistence diagrams given by convolving them with a Gaussian kernel. See http://papers.nips.cc/paper/8205-persistence-fisher-kernel-a-riemannian-manifold-kernel-for-persistence-diagrams for more details. - """ - def __init__(self, bandwidth=1., kernel_approx=None): - """ - Constructor for the PersistenceFisherDistance class. - - Attributes: - bandwidth (double): bandwidth of the Gaussian kernel used to turn persistence diagrams into probability distributions (default 1.). - kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). - """ - self.bandwidth, self.kernel_approx = bandwidth, kernel_approx - - def fit(self, X, y=None): - """ - Fit the PersistenceFisherDistance class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams** and the kernel approximation class (if not None) is applied on them. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.diagrams_ = X - projection = (1./2) * np.ones((2,2)) - self.diagonal_projections_ = [np.matmul(X[i], projection) for i in range(len(X))] - if self.kernel_approx is not None: - self.approx_ = [self.kernel_approx.transform(X[i]) for i in range(len(X))] - self.approx_diagonal_ = [self.kernel_approx.transform(self.diagonal_projections_[i]) for i in range(len(X))] - return self - - def transform(self, X): - """ - Compute all persistence Fisher distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence Fisher distances. - """ - Xfit = np.zeros((len(X), len(self.diagrams_))) - if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): - for i in range(len(self.diagrams_)): - for j in range(i+1, len(self.diagrams_)): - if self.kernel_approx is not None: - Z = np.concatenate([self.approx_[i], self.approx_diagonal_[i], self.approx_[j], self.approx_diagonal_[j]], axis=0) - U, V = np.sum(np.concatenate([self.approx_[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], self.approx_diagonal_[i]], axis=0), axis=0) - vectori, vectorj = np.abs(np.matmul(Z, U.T)), np.abs(np.matmul(Z, V.T)) - vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) - if vectori_sum != 0: - vectori = vectori/vectori_sum - if vectorj_sum != 0: - vectorj = vectorj/vectorj_sum - Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) - Xfit[j,i] = Xfit[i,j] - else: - Z = np.concatenate([self.diagrams_[i], self.diagonal_projections_[i], self.diagrams_[j], self.diagonal_projections_[j]], axis=0) - U, V = np.concatenate([self.diagrams_[i], self.diagonal_projections_[j]], axis=0), np.concatenate([self.diagrams_[j], self.diagonal_projections_[i]], axis=0) - vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) - vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) - vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) - if vectori_sum != 0: - vectori = vectori/vectori_sum - if vectorj_sum != 0: - vectorj = vectorj/vectorj_sum - Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) - Xfit[j,i] = Xfit[i,j] - else: - projection = (1./2) * np.ones((2,2)) - diagonal_projections = [np.matmul(X[i], projection) for i in range(len(X))] - if self.kernel_approx is not None: - approx = [self.kernel_approx.transform(X[i]) for i in range(len(X))] - approx_diagonal = [self.kernel_approx.transform(diagonal_projections[i]) for i in range(len(X))] - for i in range(len(X)): - for j in range(len(self.diagrams_)): - if self.kernel_approx is not None: - Z = np.concatenate([approx[i], approx_diagonal[i], self.approx_[j], self.approx_diagonal_[j]], axis=0) - U, V = np.sum(np.concatenate([approx[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], approx_diagonal[i]], axis=0), axis=0) - vectori, vectorj = np.abs(np.matmul(Z, U.T)), np.abs(np.matmul(Z, V.T)) - vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) - if vectori_sum != 0: - vectori = vectori/vectori_sum - if vectorj_sum != 0: - vectorj = vectorj/vectorj_sum - Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) - else: - Z = np.concatenate([X[i], diagonal_projections[i], self.diagrams_[j], self.diagonal_projections_[j]], axis=0) - U, V = np.concatenate([X[i], self.diagonal_projections_[j]], axis=0), np.concatenate([self.diagrams_[j], diagonal_projections[i]], axis=0) - vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) - vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) - vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) - if vectori_sum != 0: - vectori = vectori/vectori_sum - if vectorj_sum != 0: - vectorj = vectorj/vectorj_sum - Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) - return Xfit diff --git a/src/cython/sktda/preprocessing.py b/src/cython/sktda/preprocessing.py deleted file mode 100644 index 512b02f3..00000000 --- a/src/cython/sktda/preprocessing.py +++ /dev/null @@ -1,298 +0,0 @@ -""" -@author: Mathieu Carriere -All rights reserved -""" - -import numpy as np -from sklearn.base import BaseEstimator, TransformerMixin -from sklearn.preprocessing import StandardScaler - -############################################# -# Preprocessing ############################# -############################################# - -class BirthPersistenceTransform(BaseEstimator, TransformerMixin): - """ - This is a class for the affine transformation (x,y) -> (x,y-x) to be applied on persistence diagrams. - """ - def __init__(self): - """ - Constructor for BirthPersistenceTransform class. - """ - return None - - def fit(self, X, y=None): - """ - Fit the BirthPersistenceTransform class on a list of persistence diagrams (this function actually does nothing but is useful when BirthPersistenceTransform is included in a scikit-learn Pipeline). - - Parameters: - X (n x 2 numpy array): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - return self - - def transform(self, X): - """ - Apply the BirthPersistenceTransform function on the persistence diagrams. - - Parameters: - X (list of n x 2 numpy array): input persistence diagrams. - - Returns: - Xfit (list of n x 2 numpy array): transformed persistence diagrams. - """ - Xfit = [] - for diag in X: - new_diag = np.empty(diag.shape) - np.copyto(new_diag, diag) - new_diag[:,1] = new_diag[:,1] - new_diag[:,0] - Xfit.append(new_diag) - return Xfit - -class Clamping(BaseEstimator, TransformerMixin): - """ - This is a class for clamping values. It can be used as a parameter for the DiagramScaler class, for instance if you want to clamp abscissae or ordinates of persistence diagrams. - """ - def __init__(self, limit=np.inf): - """ - Constructor for the Clamping class. - - Attributes: - limit (double): clamping value (default np.inf). - """ - self.limit = limit - - def fit(self, X, y=None): - """ - Fit the Clamping class on a list of list of values (this function actually does nothing but is useful when Clamping is included in a scikit-learn Pipeline). - - Parameters: - X (list of numpy arrays of size n): input values. - y (n x 1 array): value labels (unused). - """ - return self - - def transform(self, X): - """ - Clamp each list of values individually. - - Parameters: - X (list of numpy arrays of size n): input list of list of values. - - Returns: - Xfit (list of numpy arrays of size n): output list of list of values. - """ - Xfit = [np.where(L >= self.limit, self.limit * np.ones(L.shape), L) for L in X] - return Xfit - -class DiagramScaler(BaseEstimator, TransformerMixin): - """ - This is a class for preprocessing persistence diagrams with a given list of scalers, such as those included in scikit-learn. - """ - def __init__(self, use=False, scalers=[]): - """ - Constructor for the DiagramPreprocessor class. - - Attributes: - use (bool): whether to use the class or not (default False). - scalers (list of classes): list of scalers to be fit on the persistence diagrams (default []). Each element of the list is a tuple with two elements: the first one is a list of coordinates, and the second one is a scaler (i.e. a class with fit() and transform() methods) that is going to be applied to these coordinates. Common scalers can be found in the scikit-learn library (such as MinMaxScaler for instance). - """ - self.scalers = scalers - self.use = use - - def fit(self, X, y=None): - """ - Fit the DiagramPreprocessor class on a list of persistence diagrams: persistence diagrams are concatenated in a big numpy array, and scalers are fit (by calling their fit() method) on their corresponding coordinates in this big array. - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - if self.use: - if len(X) == 1: - P = X[0] - else: - P = np.concatenate(X,0) - for (indices, scaler) in self.scalers: - scaler.fit(np.reshape(P[:,indices], [-1, 1])) - return self - - def transform(self, X): - """ - Apply the DiagramPreprocessor function on the persistence diagrams. The fitted scalers are applied (by calling their transform() method) to their corresponding coordinates in each persistence diagram individually. - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - - Returns: - Xfit (list of n x 2 or n x 1 numpy arrays): transformed persistence diagrams. - """ - Xfit = [np.copy(d) for d in X] - if self.use: - for i in range(len(Xfit)): - if Xfit[i].shape[0] > 0: - for (indices, scaler) in self.scalers: - Xfit[i][:,indices] = scaler.transform(Xfit[i][:,indices]) - return Xfit - -class Padding(BaseEstimator, TransformerMixin): - """ - This is a class for padding a list of persistence diagrams with dummy points, so that all persistence diagrams end up with the same number of points. - """ - def __init__(self, use=False): - """ - Constructor for the Padding class. - - Attributes: - use (bool): whether to use the class or not (default False). - """ - self.use = use - - def fit(self, X, y=None): - """ - Fit the Padding class on a list of persistence diagrams (this function actually does nothing but is useful when Padding is included in a scikit-learn Pipeline). - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.max_pts = max([len(diag) for diag in X]) - return self - - def transform(self, X): - """ - Add dummy points to each persistence diagram so that they all have the same cardinality. All points are given an additional coordinate indicating if the point was added after padding (0) or already present before (1). - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - - Returns: - Xfit (list of n x 3 or n x 2 numpy arrays): padded persistence diagrams. - """ - if self.use: - Xfit, num_diag = [], len(X) - for diag in X: - diag_pad = np.pad(diag, ((0,max(0, self.max_pts - diag.shape[0])), (0,1)), "constant", constant_values=((0,0),(0,0))) - diag_pad[:diag.shape[0],2] = np.ones(diag.shape[0]) - Xfit.append(diag_pad) - else: - Xfit = X - return Xfit - -class ProminentPoints(BaseEstimator, TransformerMixin): - """ - This is a class for removing points that are close or far from the diagonal in persistence diagrams. If persistence diagrams are n x 2 numpy arrays (i.e. persistence diagrams with ordinary features), points are ordered and thresholded by distance-to-diagonal. If persistence diagrams are n x 1 numpy arrays (i.e. persistence diagrams with essential features), points are not ordered and thresholded by first coordinate. - """ - def __init__(self, use=False, num_pts=10, threshold=-1, location="upper"): - """ - Constructor for the ProminentPoints class. - - Attributes: - use (bool): whether to use the class or not (default False). - location (string): either "upper" or "lower" (default "upper"). Whether to keep the points that are far away ("upper") or close ("lower") to the diagonal. - num_pts (int): cardinality threshold (default 10). If location == "upper", keep the top **num_pts** points that are the farthest away from the diagonal. If location == "lower", keep the top **num_pts** points that are the closest to the diagonal. - threshold (double): distance-to-diagonal threshold (default -1). If location == "upper", keep the points that are at least at a distance **threshold** from the diagonal. If location == "lower", keep the points that are at most at a distance **threshold** from the diagonal. - """ - self.num_pts = num_pts - self.threshold = threshold - self.use = use - self.location = location - - def fit(self, X, y=None): - """ - Fit the ProminentPoints class on a list of persistence diagrams (this function actually does nothing but is useful when ProminentPoints is included in a scikit-learn Pipeline). - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - return self - - def transform(self, X): - """ - If location == "upper", first select the top **num_pts** points that are the farthest away from the diagonal, then select and return from these points the ones that are at least at distance **threshold** from the diagonal for each persistence diagram individually. If location == "lower", first select the top **num_pts** points that are the closest to the diagonal, then select and return from these points the ones that are at most at distance **threshold** from the diagonal for each persistence diagram individually. - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - - Returns: - Xfit (list of n x 2 or n x 1 numpy arrays): thresholded persistence diagrams. - """ - if self.use: - Xfit, num_diag = [], len(X) - for i in range(num_diag): - diag = X[i] - if diag.shape[1] >= 2: - if diag.shape[0] > 0: - pers = np.abs(diag[:,1] - diag[:,0]) - idx_thresh = pers >= self.threshold - thresh_diag, thresh_pers = diag[idx_thresh], pers[idx_thresh] - sort_index = np.flip(np.argsort(thresh_pers, axis=None), 0) - if self.location == "upper": - new_diag = thresh_diag[sort_index[:min(self.num_pts, thresh_diag.shape[0])],:] - if self.location == "lower": - new_diag = np.concatenate( [ thresh_diag[sort_index[min(self.num_pts, thresh_diag.shape[0]):],:], diag[~idx_thresh] ], axis=0) - else: - new_diag = diag - - else: - if diag.shape[0] > 0: - birth = diag[:,:1] - idx_thresh = birth >= self.threshold - thresh_diag, thresh_birth = diag[idx_thresh], birth[idx_thresh] - if self.location == "upper": - new_diag = thresh_diag[:min(self.num_pts, thresh_diag.shape[0]),:] - if self.location == "lower": - new_diag = np.concatenate( [ thresh_diag[min(self.num_pts, thresh_diag.shape[0]):,:], diag[~idx_thresh] ], axis=0) - else: - new_diag = diag - - Xfit.append(new_diag) - else: - Xfit = X - return Xfit - -class DiagramSelector(BaseEstimator, TransformerMixin): - """ - This is a class for extracting finite or essential points in persistence diagrams. - """ - def __init__(self, use=False, limit=np.inf, point_type="finite"): - """ - Constructor for the DiagramSelector class. - - Attributes: - use (bool): whether to use the class or not (default False). - limit (double): second coordinate value that is the criterion for being an essential point (default numpy.inf). - point_type (string): either "finite" or "essential". The type of the points that are going to be extracted. - """ - self.use, self.limit, self.point_type = use, limit, point_type - - def fit(self, X, y=None): - """ - Fit the DiagramSelector class on a list of persistence diagrams (this function actually does nothing but is useful when DiagramSelector is included in a scikit-learn Pipeline). - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - return self - - def transform(self, X): - """ - Extract and return the finite or essential points of each persistence diagram individually. - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - - Returns: - Xfit (list of n x 2 or n x 1 numpy arrays): extracted persistence diagrams. - """ - if self.use: - Xfit, num_diag = [], len(X) - if self.point_type == "finite": - Xfit = [ diag[diag[:,1] < self.limit] if diag.shape[0] != 0 else diag for diag in X] - else: - Xfit = [ diag[diag[:,1] == self.limit, 0:1] if diag.shape[0] != 0 else diag for diag in X] - else: - Xfit = X - return Xfit diff --git a/src/cython/sktda/vector_methods.py b/src/cython/sktda/vector_methods.py deleted file mode 100644 index 3862f815..00000000 --- a/src/cython/sktda/vector_methods.py +++ /dev/null @@ -1,481 +0,0 @@ -""" -@author: Mathieu Carriere -All rights reserved -""" - -import numpy as np -from sklearn.base import BaseEstimator, TransformerMixin -from sklearn.preprocessing import MinMaxScaler, MaxAbsScaler -from sklearn.neighbors import DistanceMetric - -from .preprocessing import DiagramScaler, BirthPersistenceTransform - -############################################# -# Finite Vectorization methods ############## -############################################# - -class PersistenceImage(BaseEstimator, TransformerMixin): - """ - This is a class for computing persistence images from a list of persistence diagrams. A persistence image is a 2D function computed from a persistence diagram by convolving the diagram points with a weighted Gaussian kernel. The plane is then discretized into an image with pixels, which is flattened and returned as a vector. See http://jmlr.org/papers/v18/16-337.html for more details. - """ - def __init__(self, bandwidth=1., weight=lambda x: 1, resolution=[20,20], im_range=[np.nan, np.nan, np.nan, np.nan]): - """ - Constructor for the PersistenceImage class. - - Attributes: - bandwidth (double): bandwidth of the Gaussian kernel (default 1.). - weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie lists or numpy arrays of the form [p_x,p_y]. - resolution ([int,int]): size (in pixels) of the persistence image (default [20,20]). - im_range ([double,double,double,double]): minimum and maximum of each axis of the persistence image, of the form [x_min, x_max, y_min, y_max] (default [numpy.nan, numpy.nan, numpy.nan, numpyp.nan]). If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. - """ - self.bandwidth, self.weight = bandwidth, weight - self.resolution, self.im_range = resolution, im_range - - def fit(self, X, y=None): - """ - Fit the PersistenceImage class on a list of persistence diagrams: if any of the values in **im_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - if np.isnan(np.array(self.im_range)).any(): - new_X = BirthPersistenceTransform().fit_transform(X) - pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(new_X,y) - [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] - self.im_range = np.where(np.isnan(np.array(self.im_range)), np.array([mx, Mx, my, My]), np.array(self.im_range)) - return self - - def transform(self, X): - """ - Compute the persistence image for each persistence diagram individually and store the results in a single numpy array. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array with shape (number of diagrams) x (number of pixels = **resolution[0]** x **resolution[1]**)): output persistence images. - """ - num_diag, Xfit = len(X), [] - new_X = BirthPersistenceTransform().fit_transform(X) - - for i in range(num_diag): - - diagram, num_pts_in_diag = new_X[i], X[i].shape[0] - - w = np.empty(num_pts_in_diag) - for j in range(num_pts_in_diag): - w[j] = self.weight(diagram[j,:]) - - x_values, y_values = np.linspace(self.im_range[0], self.im_range[1], self.resolution[0]), np.linspace(self.im_range[2], self.im_range[3], self.resolution[1]) - Xs, Ys = np.tile((diagram[:,0][:,np.newaxis,np.newaxis]-x_values[np.newaxis,np.newaxis,:]),[1,self.resolution[1],1]), np.tile(diagram[:,1][:,np.newaxis,np.newaxis]-y_values[np.newaxis,:,np.newaxis],[1,1,self.resolution[0]]) - image = np.tensordot(w, np.exp((-np.square(Xs)-np.square(Ys))/(2*np.square(self.bandwidth)))/(self.bandwidth*np.sqrt(2*np.pi)), 1) - - Xfit.append(image.flatten()[np.newaxis,:]) - - Xfit = np.concatenate(Xfit,0) - - return Xfit - -class Landscape(BaseEstimator, TransformerMixin): - """ - This is a class for computing persistence landscapes from a list of persistence diagrams. A persistence landscape is a collection of 1D piecewise-linear functions computed from the rank function associated to the persistence diagram. These piecewise-linear functions are then sampled uniformly on a given range and the corresponding vectors of samples are concatenated and returned. See http://jmlr.org/papers/v16/bubenik15a.html for more details. - """ - def __init__(self, num_landscapes=5, resolution=100, sample_range=[np.nan, np.nan]): - """ - Constructor for the Landscape class. - - Attributes: - num_landscapes (int): number of piecewise-linear functions to output (default 5). - resolution (int): number of sample for all piecewise-linear functions (default 100). - sample_range ([double, double]): minimum and maximum of all piecewise-linear function domains, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. - """ - self.num_landscapes, self.resolution, self.sample_range = num_landscapes, resolution, sample_range - - def fit(self, X, y=None): - """ - Fit the Landscape class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - if np.isnan(np.array(self.sample_range)).any(): - pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) - [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] - self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) - return self - - def transform(self, X): - """ - Compute the persistence landscape for each persistence diagram individually and concatenate the results. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array with shape (number of diagrams) x (number of samples = **num_landscapes** x **resolution**)): output persistence landscapes. - """ - num_diag, Xfit = len(X), [] - x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) - step_x = x_values[1] - x_values[0] - - for i in range(num_diag): - - diagram, num_pts_in_diag = X[i], X[i].shape[0] - - ls = np.zeros([self.num_landscapes, self.resolution]) - - events = [] - for j in range(self.resolution): - events.append([]) - - for j in range(num_pts_in_diag): - [px,py] = diagram[j,:2] - min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - - if min_idx < self.resolution and max_idx > 0: - - landscape_value = self.sample_range[0] + min_idx * step_x - px - for k in range(min_idx, mid_idx): - events[k].append(landscape_value) - landscape_value += step_x - - landscape_value = py - self.sample_range[0] - mid_idx * step_x - for k in range(mid_idx, max_idx): - events[k].append(landscape_value) - landscape_value -= step_x - - for j in range(self.resolution): - events[j].sort(reverse=True) - for k in range( min(self.num_landscapes, len(events[j])) ): - ls[k,j] = events[j][k] - - Xfit.append(np.sqrt(2)*np.reshape(ls,[1,-1])) - - Xfit = np.concatenate(Xfit,0) - - return Xfit - -class Silhouette(BaseEstimator, TransformerMixin): - """ - This is a class for computing persistence silhouettes from a list of persistence diagrams. A persistence silhouette is computed by taking a weighted average of the collection of 1D piecewise-linear functions given by the persistence landscapes, and then by uniformly sampling this average on a given range. Finally, the corresponding vector of samples is returned. See https://arxiv.org/abs/1312.0308 for more details. - """ - def __init__(self, weight=lambda x: 1, resolution=100, sample_range=[np.nan, np.nan]): - """ - Constructor for the Silhouette class. - - Attributes: - weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie on lists or numpy arrays of the form [p_x,p_y]. - resolution (int): number of samples for the weighted average (default 100). - sample_range ([double, double]): minimum and maximum for the weighted average domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. - """ - self.weight, self.resolution, self.sample_range = weight, resolution, sample_range - - def fit(self, X, y=None): - """ - Fit the Silhouette class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - if np.isnan(np.array(self.sample_range)).any(): - pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) - [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] - self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) - return self - - def transform(self, X): - """ - Compute the persistence silhouette for each persistence diagram individually and concatenate the results. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array with shape (number of diagrams) x (**resolution**): output persistence silhouettes. - """ - num_diag, Xfit = len(X), [] - x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) - step_x = x_values[1] - x_values[0] - - for i in range(num_diag): - - diagram, num_pts_in_diag = X[i], X[i].shape[0] - - sh, weights = np.zeros(self.resolution), np.zeros(num_pts_in_diag) - for j in range(num_pts_in_diag): - weights[j] = self.weight(diagram[j,:]) - total_weight = np.sum(weights) - - for j in range(num_pts_in_diag): - - [px,py] = diagram[j,:2] - weight = weights[j] / total_weight - min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - - if min_idx < self.resolution and max_idx > 0: - - silhouette_value = self.sample_range[0] + min_idx * step_x - px - for k in range(min_idx, mid_idx): - sh[k] += weight * silhouette_value - silhouette_value += step_x - - silhouette_value = py - self.sample_range[0] - mid_idx * step_x - for k in range(mid_idx, max_idx): - sh[k] += weight * silhouette_value - silhouette_value -= step_x - - Xfit.append(np.reshape(np.sqrt(2) * sh, [1,-1])) - - Xfit = np.concatenate(Xfit, 0) - - return Xfit - -class BettiCurve(BaseEstimator, TransformerMixin): - """ - This is a class for computing Betti curves from a list of persistence diagrams. A Betti curve is a 1D piecewise-constant function obtained from the rank function. It is sampled uniformly on a given range and the vector of samples is returned. See https://www.researchgate.net/publication/316604237_Time_Series_Classification_via_Topological_Data_Analysis for more details. - """ - def __init__(self, resolution=100, sample_range=[np.nan, np.nan]): - """ - Constructor for the BettiCurve class. - - Attributes: - resolution (int): number of sample for the piecewise-constant function (default 100). - sample_range ([double, double]): minimum and maximum of the piecewise-constant function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. - """ - self.resolution, self.sample_range = resolution, sample_range - - def fit(self, X, y=None): - """ - Fit the BettiCurve class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - if np.isnan(np.array(self.sample_range)).any(): - pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) - [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] - self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) - return self - - def transform(self, X): - """ - Compute the Betti curve for each persistence diagram individually and concatenate the results. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array with shape (number of diagrams) x (**resolution**): output Betti curves. - """ - num_diag, Xfit = len(X), [] - x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) - step_x = x_values[1] - x_values[0] - - for i in range(num_diag): - - diagram, num_pts_in_diag = X[i], X[i].shape[0] - - bc = np.zeros(self.resolution) - for j in range(num_pts_in_diag): - [px,py] = diagram[j,:2] - min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - for k in range(min_idx, max_idx): - bc[k] += 1 - - Xfit.append(np.reshape(bc,[1,-1])) - - Xfit = np.concatenate(Xfit, 0) - - return Xfit - -class Entropy(BaseEstimator, TransformerMixin): - """ - This is a class for computing persistence entropy. Persistence entropy is a statistic for persistence diagrams inspired from Shannon entropy. This statistic can also be used to compute a feature vector, called the entropy summary function. See https://arxiv.org/pdf/1803.08304.pdf for more details. - """ - def __init__(self, mode="scalar", normalized=True, resolution=100, sample_range=[np.nan, np.nan]): - """ - Constructor for the Entropy class. - - Attributes: - mode (string): what entropy to compute: either "scalar" for computing the entropy statistics, or "vector" for computing the entropy summary functions (default "scalar"). - normalized (bool): whether to normalize the entropy summary function (default True). Used only if **mode** = "vector". - resolution (int): number of sample for the entropy summary function (default 100). Used only if **mode** = "vector". - sample_range ([double, double]): minimum and maximum of the entropy summary function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. Used only if **mode** = "vector". - """ - self.mode, self.normalized, self.resolution, self.sample_range = mode, normalized, resolution, sample_range - - def fit(self, X, y=None): - """ - Fit the Entropy class on a list of persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - if np.isnan(np.array(self.sample_range)).any(): - pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) - [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] - self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) - return self - - def transform(self, X): - """ - Compute the entropy for each persistence diagram individually and concatenate the results. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array with shape (number of diagrams) x (1 if **mode** = "scalar" else **resolution**)): output entropy. - """ - num_diag, Xfit = len(X), [] - x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) - step_x = x_values[1] - x_values[0] - new_X = BirthPersistenceTransform().fit_transform(X) - - for i in range(num_diag): - - orig_diagram, diagram, num_pts_in_diag = X[i], new_X[i], X[i].shape[0] - new_diagram = DiagramScaler(use=True, scalers=[([1], MaxAbsScaler())]).fit_transform([diagram])[0] - - if self.mode == "scalar": - ent = - np.sum( np.multiply(new_diagram[:,1], np.log(new_diagram[:,1])) ) - Xfit.append(np.array([[ent]])) - - else: - ent = np.zeros(self.resolution) - for j in range(num_pts_in_diag): - [px,py] = orig_diagram[j,:2] - min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - for k in range(min_idx, max_idx): - ent[k] += (-1) * new_diagram[j,1] * np.log(new_diagram[j,1]) - if self.normalized: - ent = ent / np.linalg.norm(ent, ord=1) - Xfit.append(np.reshape(ent,[1,-1])) - - Xfit = np.concatenate(Xfit, 0) - - return Xfit - -class TopologicalVector(BaseEstimator, TransformerMixin): - """ - This is a class for computing topological vectors from a list of persistence diagrams. The topological vector associated to a persistence diagram is the sorted vector of a slight modification of the pairwise distances between the persistence diagram points. See https://diglib.eg.org/handle/10.1111/cgf12692 for more details. - """ - def __init__(self, threshold=10): - """ - Constructor for the TopologicalVector class. - - Attributes: - threshold (int): number of distances to keep (default 10). This is the dimension of the topological vector. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding topological vector as threshold. - """ - self.threshold = threshold - - def fit(self, X, y=None): - """ - Fit the TopologicalVector class on a list of persistence diagrams (this function actually does nothing but is useful when TopologicalVector is included in a scikit-learn Pipeline). - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - return self - - def transform(self, X): - """ - Compute the topological vector for each persistence diagram individually and concatenate the results. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array with shape (number of diagrams) x (**threshold**): output topological vectors. - """ - if self.threshold == -1: - thresh = np.array([X[i].shape[0] for i in range(len(X))]).max() - else: - thresh = self.threshold - - num_diag = len(X) - Xfit = np.zeros([num_diag, thresh]) - - for i in range(num_diag): - - diagram, num_pts_in_diag = X[i], X[i].shape[0] - pers = 0.5 * (diagram[:,1]-diagram[:,0]) - min_pers = np.minimum(pers,np.transpose(pers)) - distances = DistanceMetric.get_metric("chebyshev").pairwise(diagram) - vect = np.flip(np.sort(np.triu(np.minimum(distances, min_pers)), axis=None), 0) - dim = min(len(vect), thresh) - Xfit[i, :dim] = vect[:dim] - - return Xfit - -class ComplexPolynomial(BaseEstimator, TransformerMixin): - """ - This is a class for computing complex polynomials from a list of persistence diagrams. The persistence diagram points are seen as the roots of some complex polynomial, whose coefficients are returned in a complex vector. See https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27 for more details. - """ - def __init__(self, F="R", threshold=10): - """ - Constructor for the ComplexPolynomial class. - - Attributes: - F (char): either "R", "S" or "T" (default "R"). Type of complex polynomial that is going to be computed (explained in https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27). - threshold (int): number of coefficients (default 10). This is the dimension of the complex vector of coefficients. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding complex vector of coefficients as threshold. - """ - self.threshold, self.F = threshold, F - - def fit(self, X, y=None): - """ - Fit the ComplexPolynomial class on a list of persistence diagrams (this function actually does nothing but is useful when ComplexPolynomial is included in a scikit-learn Pipeline). - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - return self - - def transform(self, X): - """ - Compute the complex vector of coefficients for each persistence diagram individually and concatenate the results. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array with shape (number of diagrams) x (**threshold**): output complex vectors of coefficients. - """ - if self.threshold == -1: - thresh = np.array([X[i].shape[0] for i in range(len(X))]).max() - else: - thresh = self.threshold - - Xfit = np.zeros([len(X), thresh]) + 1j * np.zeros([len(X), thresh]) - for d in range(len(X)): - D, N = X[d], X[d].shape[0] - if self.F == "R": - roots = D[:,0] + 1j * D[:,1] - elif self.F == "S": - alpha = np.linalg.norm(D, axis=1) - alpha = np.where(alpha==0, np.ones(N), alpha) - roots = np.multiply( np.multiply( (D[:,0]+1j*D[:,1]), (D[:,1]-D[:,0]) ), 1./(np.sqrt(2)*alpha) ) - elif self.F == "T": - alpha = np.linalg.norm(D, axis=1) - roots = np.multiply( (D[:,1]-D[:,0])/2, np.cos(alpha) - np.sin(alpha) + 1j * (np.cos(alpha) + np.sin(alpha)) ) - coeff = [0] * (N+1) - coeff[N] = 1 - for i in range(1, N+1): - for j in range(N-i-1, N): - coeff[j] += ((-1) * roots[i-1] * coeff[j+1]) - coeff = np.array(coeff[::-1])[1:] - Xfit[d, :min(thresh, coeff.shape[0])] = coeff[:min(thresh, coeff.shape[0])] - return Xfit diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 9e128d30..26cd6ec1 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -49,6 +49,7 @@ if(PYTHONINTERP_FOUND) set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'alpha_complex', ") set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'euclidean_witness_complex', ") set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'euclidean_strong_witness_complex', ") + set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'sktda', ") add_gudhi_debug_info("Python version ${PYTHON_VERSION_STRING}") add_gudhi_debug_info("Cython version ${CYTHON_VERSION}") @@ -199,6 +200,7 @@ if(PYTHONINTERP_FOUND) # Other .py files file(COPY "gudhi/persistence_graphical_tools.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") + file(COPY "gudhi/sktda" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi/") add_custom_command( OUTPUT gudhi.so diff --git a/src/python/example/ex_diagrams.py b/src/python/example/ex_diagrams.py new file mode 100644 index 00000000..3dc6fe84 --- /dev/null +++ b/src/python/example/ex_diagrams.py @@ -0,0 +1,106 @@ +import matplotlib.pyplot as plt +import numpy as np +from sklearn.kernel_approximation import RBFSampler + +from gudhi.sktda import Landscape, Silhouette, BettiCurve, ComplexPolynomial,\ + TopologicalVector, DiagramScaler, BirthPersistenceTransform,\ + PersistenceImage, PersistenceWeightedGaussianKernel,\ + PersistenceScaleSpaceKernel, SlicedWassersteinDistance,\ + SlicedWassersteinKernel, BottleneckDistance, PersistenceFisherKernel + +D = np.array([[0.,4.],[1.,2.],[3.,8.],[6.,8.]]) +plt.scatter(D[:,0],D[:,1]) +plt.plot([0.,10.],[0.,10.]) +plt.show() + +diags = [D] + +LS = Landscape(resolution = 1000) +L = LS.fit_transform(diags) +plt.plot(L[0][:1000]) +plt.plot(L[0][1000:2000]) +plt.plot(L[0][2000:3000]) +plt.show() + +def pow(n): + return lambda x: np.power(x[1]-x[0],n) + +SH = Silhouette(resolution=1000, weight=pow(2)) +sh = SH.fit_transform(diags) +plt.plot(sh[0]) +plt.show() + +BC = BettiCurve(resolution=1000) +bc = BC.fit_transform(diags) +plt.plot(bc[0]) +plt.show() + +CP = ComplexPolynomial(threshold=-1, F="T") +cp = CP.fit_transform(diags) +print("Complex polynomial is " + str(cp[0,:])) + +TV = TopologicalVector(threshold=-1) +tv = TV.fit_transform(diags) +print("Topological vector is " + str(tv[0,:])) + +#diagsT = DiagramPreprocessor(use=True, scalers=[([0,1], BirthPersistenceTransform())]).fit_transform(diags) +#PI = PersistenceImage(bandwidth=1., weight=lambda x: x[1], im_range=[0,10,0,10], resolution=[100,100]) +#pi = PI.fit_transform(diagsT) +#plt.imshow(np.flip(np.reshape(pi[0], [100,100]), 0)) +#plt.show() + +plt.scatter(D[:,0],D[:,1]) +D = np.array([[1.,5.],[3.,6.],[2.,7.]]) +plt.scatter(D[:,0],D[:,1]) +plt.plot([0.,10.],[0.,10.]) +plt.show() + +diags2 = [D] + +def arctan(C,p): + return lambda x: C*np.arctan(np.power(x[1], p)) + +PWG = PersistenceWeightedGaussianKernel(bandwidth=1., kernel_approx=None, weight=arctan(1.,1.)) +X = PWG.fit(diags) +Y = PWG.transform(diags2) +print("PWG kernel is " + str(Y[0][0])) + +PWG = PersistenceWeightedGaussianKernel(kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2])), weight=arctan(1.,1.)) +X = PWG.fit(diags) +Y = PWG.transform(diags2) +print("Approximate PWG kernel is " + str(Y[0][0])) + +PSS = PersistenceScaleSpaceKernel(bandwidth=1.) +X = PSS.fit(diags) +Y = PSS.transform(diags2) +print("PSS kernel is " + str(Y[0][0])) + +PSS = PersistenceScaleSpaceKernel(kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2]))) +X = PSS.fit(diags) +Y = PSS.transform(diags2) +print("Approximate PSS kernel is " + str(Y[0][0])) + +sW = SlicedWassersteinDistance(num_directions=100) +X = sW.fit(diags) +Y = sW.transform(diags2) +print("SW distance is " + str(Y[0][0])) + +SW = SlicedWassersteinKernel(num_directions=100, bandwidth=1.) +X = SW.fit(diags) +Y = SW.transform(diags2) +print("SW kernel is " + str(Y[0][0])) + +W = BottleneckDistance(epsilon=.001) +X = W.fit(diags) +Y = W.transform(diags2) +print("Bottleneck distance is " + str(Y[0][0])) + +PF = PersistenceFisherKernel(bandwidth_fisher=1., bandwidth=1.) +X = PF.fit(diags) +Y = PF.transform(diags2) +print("PF kernel is " + str(Y[0][0])) + +PF = PersistenceFisherKernel(bandwidth_fisher=1., bandwidth=1., kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2]))) +X = PF.fit(diags) +Y = PF.transform(diags2) +print("Approximate PF kernel is " + str(Y[0][0])) diff --git a/src/python/gudhi/sktda/__init__.py b/src/python/gudhi/sktda/__init__.py new file mode 100644 index 00000000..f020248d --- /dev/null +++ b/src/python/gudhi/sktda/__init__.py @@ -0,0 +1,6 @@ +from .kernel_methods import * +from .metrics import * +from .preprocessing import * +from .vector_methods import * + +__all__ = ["kernel_methods", "metrics", "preprocessing", "vector_methods"] diff --git a/src/python/gudhi/sktda/kernel_methods.py b/src/python/gudhi/sktda/kernel_methods.py new file mode 100644 index 00000000..d90bf164 --- /dev/null +++ b/src/python/gudhi/sktda/kernel_methods.py @@ -0,0 +1,202 @@ +""" +@author: Mathieu Carriere +All rights reserved +""" + +import numpy as np +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.metrics import pairwise_distances +from .metrics import SlicedWassersteinDistance, PersistenceFisherDistance + +############################################# +# Kernel methods ############################ +############################################# + +class SlicedWassersteinKernel(BaseEstimator, TransformerMixin): + """ + This is a class for computing the sliced Wasserstein kernel matrix from a list of persistence diagrams. The sliced Wasserstein kernel is computed by exponentiating the corresponding sliced Wasserstein distance with a Gaussian kernel. See http://proceedings.mlr.press/v70/carriere17a.html for more details. + """ + def __init__(self, num_directions=10, bandwidth=1.0): + """ + Constructor for the SlicedWassersteinDistance class. + + Attributes: + bandwidth (double): bandwidth of the Gaussian kernel applied to the sliced Wasserstein distance (default 1.). + num_directions (int): number of lines to sample uniformly from [-pi,pi] in order to approximate and speed up the kernel computation (default 10). If -1, the exact kernel is computed. + """ + self.bandwidth = bandwidth + self.sw_ = SlicedWassersteinDistance(num_directions=num_directions) + + def fit(self, X, y=None): + """ + Fit the SlicedWassersteinKernel class on a list of persistence diagrams: an instance of the SlicedWassersteinDistance class is fitted on the diagrams and then stored. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.sw_.fit(X, y) + return self + + def transform(self, X): + """ + Compute all sliced Wasserstein kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise sliced Wasserstein kernel values. + """ + return np.exp(-self.sw_.transform(X)/self.bandwidth) + +class PersistenceWeightedGaussianKernel(BaseEstimator, TransformerMixin): + """ + This is a class for computing the persistence weighted Gaussian kernel matrix from a list of persistence diagrams. The persistence weighted Gaussian kernel is computed by convolving the persistence diagram points with weighted Gaussian kernels. See http://proceedings.mlr.press/v48/kusano16.html for more details. + """ + def __init__(self, bandwidth=1., weight=lambda x: 1, kernel_approx=None): + """ + Constructor for the PersistenceWeightedGaussianKernel class. + + Attributes: + bandwidth (double): bandwidth of the Gaussian kernel with which persistence diagrams will be convolved (default 1.) + weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie lists or numpy arrays of the form [p_x,p_y]. + kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). + """ + self.bandwidth, self.weight = bandwidth, weight + self.kernel_approx = kernel_approx + + def fit(self, X, y=None): + """ + Fit the PersistenceWeightedGaussianKernel class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams** and the kernel approximation class (if not None) is applied on them. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = list(X) + self.ws_ = [ np.array([self.weight(self.diagrams_[i][j,:]) for j in range(self.diagrams_[i].shape[0])]) for i in range(len(self.diagrams_)) ] + if self.kernel_approx is not None: + self.approx_ = np.concatenate([np.sum(np.multiply(self.ws_[i][:,np.newaxis], self.kernel_approx.transform(self.diagrams_[i])), axis=0)[np.newaxis,:] for i in range(len(self.diagrams_))]) + return self + + def transform(self, X): + """ + Compute all sliced Wasserstein kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence weighted Gaussian kernel values. + """ + Xp = list(X) + Xfit = np.zeros((len(Xp), len(self.diagrams_))) + if len(self.diagrams_) == len(Xp) and np.all([np.array_equal(self.diagrams_[i], Xp[i]) for i in range(len(Xp))]): + if self.kernel_approx is not None: + Xfit = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.matmul(self.approx_, self.approx_.T) + else: + for i in range(len(self.diagrams_)): + for j in range(i+1, len(self.diagrams_)): + W = np.matmul(self.ws_[i][:,np.newaxis], self.ws_[j][np.newaxis,:]) + E = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.exp(-np.square(pairwise_distances(self.diagrams_[i], self.diagrams_[j]))/(2*np.square(self.bandwidth))) + Xfit[i,j] = np.sum(np.multiply(W, E)) + Xfit[j,i] = Xfit[i,j] + else: + ws = [ np.array([self.weight(Xp[i][j,:]) for j in range(Xp[i].shape[0])]) for i in range(len(Xp)) ] + if self.kernel_approx is not None: + approx = np.concatenate([np.sum(np.multiply(ws[i][:,np.newaxis], self.kernel_approx.transform(Xp[i])), axis=0)[np.newaxis,:] for i in range(len(Xp))]) + Xfit = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.matmul(approx, self.approx_.T) + else: + for i in range(len(Xp)): + for j in range(len(self.diagrams_)): + W = np.matmul(ws[i][:,np.newaxis], self.ws_[j][np.newaxis,:]) + E = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.exp(-np.square(pairwise_distances(Xp[i], self.diagrams_[j]))/(2*np.square(self.bandwidth))) + Xfit[i,j] = np.sum(np.multiply(W, E)) + + return Xfit + +class PersistenceScaleSpaceKernel(BaseEstimator, TransformerMixin): + """ + This is a class for computing the persistence scale space kernel matrix from a list of persistence diagrams. The persistence scale space kernel is computed by adding the symmetric to the diagonal of each point in each persistence diagram, and then convolving the points with a Gaussian kernel. See https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Reininghaus_A_Stable_Multi-Scale_2015_CVPR_paper.pdf for more details. + """ + def __init__(self, bandwidth=1., kernel_approx=None): + """ + Constructor for the PersistenceScaleSpaceKernel class. + + Attributes: + bandwidth (double): bandwidth of the Gaussian kernel with which persistence diagrams will be convolved (default 1.) + kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). + """ + self.pwg_ = PersistenceWeightedGaussianKernel(bandwidth=bandwidth, weight=lambda x: 1 if x[1] >= x[0] else -1, kernel_approx=kernel_approx) + + def fit(self, X, y=None): + """ + Fit the PersistenceScaleSpaceKernel class on a list of persistence diagrams: symmetric to the diagonal of all points are computed and an instance of the PersistenceWeightedGaussianKernel class is fitted on the diagrams and then stored. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = list(X) + for i in range(len(self.diagrams_)): + op_D = np.matmul(self.diagrams_[i], np.array([[0.,1.], [1.,0.]])) + self.diagrams_[i] = np.concatenate([self.diagrams_[i], op_D], axis=0) + self.pwg_.fit(X) + return self + + def transform(self, X): + """ + Compute all persistence scale space kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence scale space kernel values. + """ + Xp = list(X) + for i in range(len(Xp)): + op_X = np.matmul(Xp[i], np.array([[0.,1.], [1.,0.]])) + Xp[i] = np.concatenate([Xp[i], op_X], axis=0) + return self.pwg_.transform(Xp) + +class PersistenceFisherKernel(BaseEstimator, TransformerMixin): + """ + This is a class for computing the persistence Fisher kernel matrix from a list of persistence diagrams. The persistence Fisher kernel is computed by exponentiating the corresponding persistence Fisher distance with a Gaussian kernel. See papers.nips.cc/paper/8205-persistence-fisher-kernel-a-riemannian-manifold-kernel-for-persistence-diagrams for more details. + """ + def __init__(self, bandwidth_fisher=1., bandwidth=1., kernel_approx=None): + """ + Constructor for the PersistenceFisherKernel class. + + Attributes: + bandwidth (double): bandwidth of the Gaussian kernel applied to the persistence Fisher distance (default 1.). + bandwidth_fisher (double): bandwidth of the Gaussian kernel used to turn persistence diagrams into probability distributions by PersistenceFisherDistance class (default 1.). + kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). + """ + self.bandwidth = bandwidth + self.pf_ = PersistenceFisherDistance(bandwidth=bandwidth_fisher, kernel_approx=kernel_approx) + + def fit(self, X, y=None): + """ + Fit the PersistenceFisherKernel class on a list of persistence diagrams: an instance of the PersistenceFisherDistance class is fitted on the diagrams and then stored. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.pf_.fit(X, y) + return self + + def transform(self, X): + """ + Compute all persistence Fisher kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence Fisher kernel values. + """ + return np.exp(-self.pf_.transform(X)/self.bandwidth) + diff --git a/src/python/gudhi/sktda/metrics.py b/src/python/gudhi/sktda/metrics.py new file mode 100644 index 00000000..8092f7af --- /dev/null +++ b/src/python/gudhi/sktda/metrics.py @@ -0,0 +1,239 @@ +""" +@author: Mathieu Carriere +All rights reserved +""" + +import numpy as np +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.metrics import pairwise_distances +try: + from .. import bottleneck_distance + USE_GUDHI = True +except ImportError: + USE_GUDHI = False + print("Gudhi not found: BottleneckDistance will return null matrix, and exact SlicedWassersteinDistance not available") + +############################################# +# Metrics ################################### +############################################# + +class SlicedWassersteinDistance(BaseEstimator, TransformerMixin): + """ + This is a class for computing the sliced Wasserstein distance matrix from a list of persistence diagrams. The Sliced Wasserstein distance is computed by projecting the persistence diagrams onto lines, comparing the projections with the 1-norm, and finally integrating over all possible lines. See http://proceedings.mlr.press/v70/carriere17a.html for more details. + """ + def __init__(self, num_directions=10): + """ + Constructor for the SlicedWassersteinDistance class. + + Attributes: + num_directions (int): number of lines to sample uniformly from [-pi,pi] in order to approximate and speed up the distance computation (default 10). + """ + self.num_directions = num_directions + thetas = np.linspace(-np.pi/2, np.pi/2, num=self.num_directions+1)[np.newaxis,:-1] + self.lines_ = np.concatenate([np.cos(thetas), np.sin(thetas)], axis=0) + + def fit(self, X, y=None): + """ + Fit the SlicedWassersteinDistance class on a list of persistence diagrams: persistence diagrams are projected onto the different lines. The diagrams themselves and their projections are then stored in a numpy array called **diagrams**. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = X + self.approx_ = [np.matmul(X[i], self.lines_) for i in range(len(X))] + diag_proj = (1./2) * np.ones((2,2)) + self.approx_diag_ = [np.matmul(np.matmul(X[i], diag_proj), self.lines_) for i in range(len(X))] + return self + + def transform(self, X): + """ + Compute all sliced Wasserstein distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise sliced Wasserstein distances. + """ + Xfit = np.zeros((len(X), len(self.approx_))) + if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): + for i in range(len(self.approx_)): + for j in range(i+1, len(self.approx_)): + A = np.sort(np.concatenate([self.approx_[i], self.approx_diag_[j]], axis=0), axis=0) + B = np.sort(np.concatenate([self.approx_[j], self.approx_diag_[i]], axis=0), axis=0) + L1 = np.sum(np.abs(A-B), axis=0) + Xfit[i,j] = np.mean(L1) + Xfit[j,i] = Xfit[i,j] + else: + diag_proj = (1./2) * np.ones((2,2)) + approx = [np.matmul(X[i], self.lines_) for i in range(len(X))] + approx_diag = [np.matmul(np.matmul(X[i], diag_proj), self.lines_) for i in range(len(X))] + for i in range(len(approx)): + for j in range(len(self.approx_)): + A = np.sort(np.concatenate([approx[i], self.approx_diag_[j]], axis=0), axis=0) + B = np.sort(np.concatenate([self.approx_[j], approx_diag[i]], axis=0), axis=0) + L1 = np.sum(np.abs(A-B), axis=0) + Xfit[i,j] = np.mean(L1) + + return Xfit + +class BottleneckDistance(BaseEstimator, TransformerMixin): + """ + This is a class for computing the bottleneck distance matrix from a list of persistence diagrams. + """ + def __init__(self, epsilon=1e-3): + """ + Constructor for the BottleneckDistance class. + + Attributes: + epsilon (double): approximation quality (default 1e-4). + """ + self.epsilon = epsilon + + def fit(self, X, y=None): + """ + Fit the BottleneckDistance class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams**. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = X + return self + + def transform(self, X): + """ + Compute all bottleneck distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise bottleneck distances. + """ + num_diag1 = len(X) + + if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): + matrix = np.zeros((num_diag1, num_diag1)) + + if USE_GUDHI: + for i in range(num_diag1): + for j in range(i+1, num_diag1): + matrix[i,j] = bottleneck_distance(X[i], X[j], self.epsilon) + matrix[j,i] = matrix[i,j] + else: + print("Gudhi required---returning null matrix") + + else: + num_diag2 = len(self.diagrams_) + matrix = np.zeros((num_diag1, num_diag2)) + + if USE_GUDHI: + for i in range(num_diag1): + for j in range(num_diag2): + matrix[i,j] = bottleneck_distance(X[i], self.diagrams_[j], self.epsilon) + else: + print("Gudhi required---returning null matrix") + + Xfit = matrix + + return Xfit + +class PersistenceFisherDistance(BaseEstimator, TransformerMixin): + """ + This is a class for computing the persistence Fisher distance matrix from a list of persistence diagrams. The persistence Fisher distance is obtained by computing the original Fisher distance between the probability distributions associated to the persistence diagrams given by convolving them with a Gaussian kernel. See http://papers.nips.cc/paper/8205-persistence-fisher-kernel-a-riemannian-manifold-kernel-for-persistence-diagrams for more details. + """ + def __init__(self, bandwidth=1., kernel_approx=None): + """ + Constructor for the PersistenceFisherDistance class. + + Attributes: + bandwidth (double): bandwidth of the Gaussian kernel used to turn persistence diagrams into probability distributions (default 1.). + kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). + """ + self.bandwidth, self.kernel_approx = bandwidth, kernel_approx + + def fit(self, X, y=None): + """ + Fit the PersistenceFisherDistance class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams** and the kernel approximation class (if not None) is applied on them. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = X + projection = (1./2) * np.ones((2,2)) + self.diagonal_projections_ = [np.matmul(X[i], projection) for i in range(len(X))] + if self.kernel_approx is not None: + self.approx_ = [self.kernel_approx.transform(X[i]) for i in range(len(X))] + self.approx_diagonal_ = [self.kernel_approx.transform(self.diagonal_projections_[i]) for i in range(len(X))] + return self + + def transform(self, X): + """ + Compute all persistence Fisher distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence Fisher distances. + """ + Xfit = np.zeros((len(X), len(self.diagrams_))) + if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): + for i in range(len(self.diagrams_)): + for j in range(i+1, len(self.diagrams_)): + if self.kernel_approx is not None: + Z = np.concatenate([self.approx_[i], self.approx_diagonal_[i], self.approx_[j], self.approx_diagonal_[j]], axis=0) + U, V = np.sum(np.concatenate([self.approx_[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], self.approx_diagonal_[i]], axis=0), axis=0) + vectori, vectorj = np.abs(np.matmul(Z, U.T)), np.abs(np.matmul(Z, V.T)) + vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) + if vectori_sum != 0: + vectori = vectori/vectori_sum + if vectorj_sum != 0: + vectorj = vectorj/vectorj_sum + Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) + Xfit[j,i] = Xfit[i,j] + else: + Z = np.concatenate([self.diagrams_[i], self.diagonal_projections_[i], self.diagrams_[j], self.diagonal_projections_[j]], axis=0) + U, V = np.concatenate([self.diagrams_[i], self.diagonal_projections_[j]], axis=0), np.concatenate([self.diagrams_[j], self.diagonal_projections_[i]], axis=0) + vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) + vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) + vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) + if vectori_sum != 0: + vectori = vectori/vectori_sum + if vectorj_sum != 0: + vectorj = vectorj/vectorj_sum + Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) + Xfit[j,i] = Xfit[i,j] + else: + projection = (1./2) * np.ones((2,2)) + diagonal_projections = [np.matmul(X[i], projection) for i in range(len(X))] + if self.kernel_approx is not None: + approx = [self.kernel_approx.transform(X[i]) for i in range(len(X))] + approx_diagonal = [self.kernel_approx.transform(diagonal_projections[i]) for i in range(len(X))] + for i in range(len(X)): + for j in range(len(self.diagrams_)): + if self.kernel_approx is not None: + Z = np.concatenate([approx[i], approx_diagonal[i], self.approx_[j], self.approx_diagonal_[j]], axis=0) + U, V = np.sum(np.concatenate([approx[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], approx_diagonal[i]], axis=0), axis=0) + vectori, vectorj = np.abs(np.matmul(Z, U.T)), np.abs(np.matmul(Z, V.T)) + vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) + if vectori_sum != 0: + vectori = vectori/vectori_sum + if vectorj_sum != 0: + vectorj = vectorj/vectorj_sum + Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) + else: + Z = np.concatenate([X[i], diagonal_projections[i], self.diagrams_[j], self.diagonal_projections_[j]], axis=0) + U, V = np.concatenate([X[i], self.diagonal_projections_[j]], axis=0), np.concatenate([self.diagrams_[j], diagonal_projections[i]], axis=0) + vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) + vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) + vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) + if vectori_sum != 0: + vectori = vectori/vectori_sum + if vectorj_sum != 0: + vectorj = vectorj/vectorj_sum + Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) + return Xfit diff --git a/src/python/gudhi/sktda/preprocessing.py b/src/python/gudhi/sktda/preprocessing.py new file mode 100644 index 00000000..512b02f3 --- /dev/null +++ b/src/python/gudhi/sktda/preprocessing.py @@ -0,0 +1,298 @@ +""" +@author: Mathieu Carriere +All rights reserved +""" + +import numpy as np +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.preprocessing import StandardScaler + +############################################# +# Preprocessing ############################# +############################################# + +class BirthPersistenceTransform(BaseEstimator, TransformerMixin): + """ + This is a class for the affine transformation (x,y) -> (x,y-x) to be applied on persistence diagrams. + """ + def __init__(self): + """ + Constructor for BirthPersistenceTransform class. + """ + return None + + def fit(self, X, y=None): + """ + Fit the BirthPersistenceTransform class on a list of persistence diagrams (this function actually does nothing but is useful when BirthPersistenceTransform is included in a scikit-learn Pipeline). + + Parameters: + X (n x 2 numpy array): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + Apply the BirthPersistenceTransform function on the persistence diagrams. + + Parameters: + X (list of n x 2 numpy array): input persistence diagrams. + + Returns: + Xfit (list of n x 2 numpy array): transformed persistence diagrams. + """ + Xfit = [] + for diag in X: + new_diag = np.empty(diag.shape) + np.copyto(new_diag, diag) + new_diag[:,1] = new_diag[:,1] - new_diag[:,0] + Xfit.append(new_diag) + return Xfit + +class Clamping(BaseEstimator, TransformerMixin): + """ + This is a class for clamping values. It can be used as a parameter for the DiagramScaler class, for instance if you want to clamp abscissae or ordinates of persistence diagrams. + """ + def __init__(self, limit=np.inf): + """ + Constructor for the Clamping class. + + Attributes: + limit (double): clamping value (default np.inf). + """ + self.limit = limit + + def fit(self, X, y=None): + """ + Fit the Clamping class on a list of list of values (this function actually does nothing but is useful when Clamping is included in a scikit-learn Pipeline). + + Parameters: + X (list of numpy arrays of size n): input values. + y (n x 1 array): value labels (unused). + """ + return self + + def transform(self, X): + """ + Clamp each list of values individually. + + Parameters: + X (list of numpy arrays of size n): input list of list of values. + + Returns: + Xfit (list of numpy arrays of size n): output list of list of values. + """ + Xfit = [np.where(L >= self.limit, self.limit * np.ones(L.shape), L) for L in X] + return Xfit + +class DiagramScaler(BaseEstimator, TransformerMixin): + """ + This is a class for preprocessing persistence diagrams with a given list of scalers, such as those included in scikit-learn. + """ + def __init__(self, use=False, scalers=[]): + """ + Constructor for the DiagramPreprocessor class. + + Attributes: + use (bool): whether to use the class or not (default False). + scalers (list of classes): list of scalers to be fit on the persistence diagrams (default []). Each element of the list is a tuple with two elements: the first one is a list of coordinates, and the second one is a scaler (i.e. a class with fit() and transform() methods) that is going to be applied to these coordinates. Common scalers can be found in the scikit-learn library (such as MinMaxScaler for instance). + """ + self.scalers = scalers + self.use = use + + def fit(self, X, y=None): + """ + Fit the DiagramPreprocessor class on a list of persistence diagrams: persistence diagrams are concatenated in a big numpy array, and scalers are fit (by calling their fit() method) on their corresponding coordinates in this big array. + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if self.use: + if len(X) == 1: + P = X[0] + else: + P = np.concatenate(X,0) + for (indices, scaler) in self.scalers: + scaler.fit(np.reshape(P[:,indices], [-1, 1])) + return self + + def transform(self, X): + """ + Apply the DiagramPreprocessor function on the persistence diagrams. The fitted scalers are applied (by calling their transform() method) to their corresponding coordinates in each persistence diagram individually. + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + + Returns: + Xfit (list of n x 2 or n x 1 numpy arrays): transformed persistence diagrams. + """ + Xfit = [np.copy(d) for d in X] + if self.use: + for i in range(len(Xfit)): + if Xfit[i].shape[0] > 0: + for (indices, scaler) in self.scalers: + Xfit[i][:,indices] = scaler.transform(Xfit[i][:,indices]) + return Xfit + +class Padding(BaseEstimator, TransformerMixin): + """ + This is a class for padding a list of persistence diagrams with dummy points, so that all persistence diagrams end up with the same number of points. + """ + def __init__(self, use=False): + """ + Constructor for the Padding class. + + Attributes: + use (bool): whether to use the class or not (default False). + """ + self.use = use + + def fit(self, X, y=None): + """ + Fit the Padding class on a list of persistence diagrams (this function actually does nothing but is useful when Padding is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.max_pts = max([len(diag) for diag in X]) + return self + + def transform(self, X): + """ + Add dummy points to each persistence diagram so that they all have the same cardinality. All points are given an additional coordinate indicating if the point was added after padding (0) or already present before (1). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + + Returns: + Xfit (list of n x 3 or n x 2 numpy arrays): padded persistence diagrams. + """ + if self.use: + Xfit, num_diag = [], len(X) + for diag in X: + diag_pad = np.pad(diag, ((0,max(0, self.max_pts - diag.shape[0])), (0,1)), "constant", constant_values=((0,0),(0,0))) + diag_pad[:diag.shape[0],2] = np.ones(diag.shape[0]) + Xfit.append(diag_pad) + else: + Xfit = X + return Xfit + +class ProminentPoints(BaseEstimator, TransformerMixin): + """ + This is a class for removing points that are close or far from the diagonal in persistence diagrams. If persistence diagrams are n x 2 numpy arrays (i.e. persistence diagrams with ordinary features), points are ordered and thresholded by distance-to-diagonal. If persistence diagrams are n x 1 numpy arrays (i.e. persistence diagrams with essential features), points are not ordered and thresholded by first coordinate. + """ + def __init__(self, use=False, num_pts=10, threshold=-1, location="upper"): + """ + Constructor for the ProminentPoints class. + + Attributes: + use (bool): whether to use the class or not (default False). + location (string): either "upper" or "lower" (default "upper"). Whether to keep the points that are far away ("upper") or close ("lower") to the diagonal. + num_pts (int): cardinality threshold (default 10). If location == "upper", keep the top **num_pts** points that are the farthest away from the diagonal. If location == "lower", keep the top **num_pts** points that are the closest to the diagonal. + threshold (double): distance-to-diagonal threshold (default -1). If location == "upper", keep the points that are at least at a distance **threshold** from the diagonal. If location == "lower", keep the points that are at most at a distance **threshold** from the diagonal. + """ + self.num_pts = num_pts + self.threshold = threshold + self.use = use + self.location = location + + def fit(self, X, y=None): + """ + Fit the ProminentPoints class on a list of persistence diagrams (this function actually does nothing but is useful when ProminentPoints is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + If location == "upper", first select the top **num_pts** points that are the farthest away from the diagonal, then select and return from these points the ones that are at least at distance **threshold** from the diagonal for each persistence diagram individually. If location == "lower", first select the top **num_pts** points that are the closest to the diagonal, then select and return from these points the ones that are at most at distance **threshold** from the diagonal for each persistence diagram individually. + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + + Returns: + Xfit (list of n x 2 or n x 1 numpy arrays): thresholded persistence diagrams. + """ + if self.use: + Xfit, num_diag = [], len(X) + for i in range(num_diag): + diag = X[i] + if diag.shape[1] >= 2: + if diag.shape[0] > 0: + pers = np.abs(diag[:,1] - diag[:,0]) + idx_thresh = pers >= self.threshold + thresh_diag, thresh_pers = diag[idx_thresh], pers[idx_thresh] + sort_index = np.flip(np.argsort(thresh_pers, axis=None), 0) + if self.location == "upper": + new_diag = thresh_diag[sort_index[:min(self.num_pts, thresh_diag.shape[0])],:] + if self.location == "lower": + new_diag = np.concatenate( [ thresh_diag[sort_index[min(self.num_pts, thresh_diag.shape[0]):],:], diag[~idx_thresh] ], axis=0) + else: + new_diag = diag + + else: + if diag.shape[0] > 0: + birth = diag[:,:1] + idx_thresh = birth >= self.threshold + thresh_diag, thresh_birth = diag[idx_thresh], birth[idx_thresh] + if self.location == "upper": + new_diag = thresh_diag[:min(self.num_pts, thresh_diag.shape[0]),:] + if self.location == "lower": + new_diag = np.concatenate( [ thresh_diag[min(self.num_pts, thresh_diag.shape[0]):,:], diag[~idx_thresh] ], axis=0) + else: + new_diag = diag + + Xfit.append(new_diag) + else: + Xfit = X + return Xfit + +class DiagramSelector(BaseEstimator, TransformerMixin): + """ + This is a class for extracting finite or essential points in persistence diagrams. + """ + def __init__(self, use=False, limit=np.inf, point_type="finite"): + """ + Constructor for the DiagramSelector class. + + Attributes: + use (bool): whether to use the class or not (default False). + limit (double): second coordinate value that is the criterion for being an essential point (default numpy.inf). + point_type (string): either "finite" or "essential". The type of the points that are going to be extracted. + """ + self.use, self.limit, self.point_type = use, limit, point_type + + def fit(self, X, y=None): + """ + Fit the DiagramSelector class on a list of persistence diagrams (this function actually does nothing but is useful when DiagramSelector is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + Extract and return the finite or essential points of each persistence diagram individually. + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + + Returns: + Xfit (list of n x 2 or n x 1 numpy arrays): extracted persistence diagrams. + """ + if self.use: + Xfit, num_diag = [], len(X) + if self.point_type == "finite": + Xfit = [ diag[diag[:,1] < self.limit] if diag.shape[0] != 0 else diag for diag in X] + else: + Xfit = [ diag[diag[:,1] == self.limit, 0:1] if diag.shape[0] != 0 else diag for diag in X] + else: + Xfit = X + return Xfit diff --git a/src/python/gudhi/sktda/vector_methods.py b/src/python/gudhi/sktda/vector_methods.py new file mode 100644 index 00000000..3862f815 --- /dev/null +++ b/src/python/gudhi/sktda/vector_methods.py @@ -0,0 +1,481 @@ +""" +@author: Mathieu Carriere +All rights reserved +""" + +import numpy as np +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.preprocessing import MinMaxScaler, MaxAbsScaler +from sklearn.neighbors import DistanceMetric + +from .preprocessing import DiagramScaler, BirthPersistenceTransform + +############################################# +# Finite Vectorization methods ############## +############################################# + +class PersistenceImage(BaseEstimator, TransformerMixin): + """ + This is a class for computing persistence images from a list of persistence diagrams. A persistence image is a 2D function computed from a persistence diagram by convolving the diagram points with a weighted Gaussian kernel. The plane is then discretized into an image with pixels, which is flattened and returned as a vector. See http://jmlr.org/papers/v18/16-337.html for more details. + """ + def __init__(self, bandwidth=1., weight=lambda x: 1, resolution=[20,20], im_range=[np.nan, np.nan, np.nan, np.nan]): + """ + Constructor for the PersistenceImage class. + + Attributes: + bandwidth (double): bandwidth of the Gaussian kernel (default 1.). + weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie lists or numpy arrays of the form [p_x,p_y]. + resolution ([int,int]): size (in pixels) of the persistence image (default [20,20]). + im_range ([double,double,double,double]): minimum and maximum of each axis of the persistence image, of the form [x_min, x_max, y_min, y_max] (default [numpy.nan, numpy.nan, numpy.nan, numpyp.nan]). If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + """ + self.bandwidth, self.weight = bandwidth, weight + self.resolution, self.im_range = resolution, im_range + + def fit(self, X, y=None): + """ + Fit the PersistenceImage class on a list of persistence diagrams: if any of the values in **im_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.im_range)).any(): + new_X = BirthPersistenceTransform().fit_transform(X) + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(new_X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.im_range = np.where(np.isnan(np.array(self.im_range)), np.array([mx, Mx, my, My]), np.array(self.im_range)) + return self + + def transform(self, X): + """ + Compute the persistence image for each persistence diagram individually and store the results in a single numpy array. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (number of pixels = **resolution[0]** x **resolution[1]**)): output persistence images. + """ + num_diag, Xfit = len(X), [] + new_X = BirthPersistenceTransform().fit_transform(X) + + for i in range(num_diag): + + diagram, num_pts_in_diag = new_X[i], X[i].shape[0] + + w = np.empty(num_pts_in_diag) + for j in range(num_pts_in_diag): + w[j] = self.weight(diagram[j,:]) + + x_values, y_values = np.linspace(self.im_range[0], self.im_range[1], self.resolution[0]), np.linspace(self.im_range[2], self.im_range[3], self.resolution[1]) + Xs, Ys = np.tile((diagram[:,0][:,np.newaxis,np.newaxis]-x_values[np.newaxis,np.newaxis,:]),[1,self.resolution[1],1]), np.tile(diagram[:,1][:,np.newaxis,np.newaxis]-y_values[np.newaxis,:,np.newaxis],[1,1,self.resolution[0]]) + image = np.tensordot(w, np.exp((-np.square(Xs)-np.square(Ys))/(2*np.square(self.bandwidth)))/(self.bandwidth*np.sqrt(2*np.pi)), 1) + + Xfit.append(image.flatten()[np.newaxis,:]) + + Xfit = np.concatenate(Xfit,0) + + return Xfit + +class Landscape(BaseEstimator, TransformerMixin): + """ + This is a class for computing persistence landscapes from a list of persistence diagrams. A persistence landscape is a collection of 1D piecewise-linear functions computed from the rank function associated to the persistence diagram. These piecewise-linear functions are then sampled uniformly on a given range and the corresponding vectors of samples are concatenated and returned. See http://jmlr.org/papers/v16/bubenik15a.html for more details. + """ + def __init__(self, num_landscapes=5, resolution=100, sample_range=[np.nan, np.nan]): + """ + Constructor for the Landscape class. + + Attributes: + num_landscapes (int): number of piecewise-linear functions to output (default 5). + resolution (int): number of sample for all piecewise-linear functions (default 100). + sample_range ([double, double]): minimum and maximum of all piecewise-linear function domains, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + """ + self.num_landscapes, self.resolution, self.sample_range = num_landscapes, resolution, sample_range + + def fit(self, X, y=None): + """ + Fit the Landscape class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.sample_range)).any(): + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) + return self + + def transform(self, X): + """ + Compute the persistence landscape for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (number of samples = **num_landscapes** x **resolution**)): output persistence landscapes. + """ + num_diag, Xfit = len(X), [] + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) + step_x = x_values[1] - x_values[0] + + for i in range(num_diag): + + diagram, num_pts_in_diag = X[i], X[i].shape[0] + + ls = np.zeros([self.num_landscapes, self.resolution]) + + events = [] + for j in range(self.resolution): + events.append([]) + + for j in range(num_pts_in_diag): + [px,py] = diagram[j,:2] + min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + + if min_idx < self.resolution and max_idx > 0: + + landscape_value = self.sample_range[0] + min_idx * step_x - px + for k in range(min_idx, mid_idx): + events[k].append(landscape_value) + landscape_value += step_x + + landscape_value = py - self.sample_range[0] - mid_idx * step_x + for k in range(mid_idx, max_idx): + events[k].append(landscape_value) + landscape_value -= step_x + + for j in range(self.resolution): + events[j].sort(reverse=True) + for k in range( min(self.num_landscapes, len(events[j])) ): + ls[k,j] = events[j][k] + + Xfit.append(np.sqrt(2)*np.reshape(ls,[1,-1])) + + Xfit = np.concatenate(Xfit,0) + + return Xfit + +class Silhouette(BaseEstimator, TransformerMixin): + """ + This is a class for computing persistence silhouettes from a list of persistence diagrams. A persistence silhouette is computed by taking a weighted average of the collection of 1D piecewise-linear functions given by the persistence landscapes, and then by uniformly sampling this average on a given range. Finally, the corresponding vector of samples is returned. See https://arxiv.org/abs/1312.0308 for more details. + """ + def __init__(self, weight=lambda x: 1, resolution=100, sample_range=[np.nan, np.nan]): + """ + Constructor for the Silhouette class. + + Attributes: + weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie on lists or numpy arrays of the form [p_x,p_y]. + resolution (int): number of samples for the weighted average (default 100). + sample_range ([double, double]): minimum and maximum for the weighted average domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + """ + self.weight, self.resolution, self.sample_range = weight, resolution, sample_range + + def fit(self, X, y=None): + """ + Fit the Silhouette class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.sample_range)).any(): + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) + return self + + def transform(self, X): + """ + Compute the persistence silhouette for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (**resolution**): output persistence silhouettes. + """ + num_diag, Xfit = len(X), [] + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) + step_x = x_values[1] - x_values[0] + + for i in range(num_diag): + + diagram, num_pts_in_diag = X[i], X[i].shape[0] + + sh, weights = np.zeros(self.resolution), np.zeros(num_pts_in_diag) + for j in range(num_pts_in_diag): + weights[j] = self.weight(diagram[j,:]) + total_weight = np.sum(weights) + + for j in range(num_pts_in_diag): + + [px,py] = diagram[j,:2] + weight = weights[j] / total_weight + min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + + if min_idx < self.resolution and max_idx > 0: + + silhouette_value = self.sample_range[0] + min_idx * step_x - px + for k in range(min_idx, mid_idx): + sh[k] += weight * silhouette_value + silhouette_value += step_x + + silhouette_value = py - self.sample_range[0] - mid_idx * step_x + for k in range(mid_idx, max_idx): + sh[k] += weight * silhouette_value + silhouette_value -= step_x + + Xfit.append(np.reshape(np.sqrt(2) * sh, [1,-1])) + + Xfit = np.concatenate(Xfit, 0) + + return Xfit + +class BettiCurve(BaseEstimator, TransformerMixin): + """ + This is a class for computing Betti curves from a list of persistence diagrams. A Betti curve is a 1D piecewise-constant function obtained from the rank function. It is sampled uniformly on a given range and the vector of samples is returned. See https://www.researchgate.net/publication/316604237_Time_Series_Classification_via_Topological_Data_Analysis for more details. + """ + def __init__(self, resolution=100, sample_range=[np.nan, np.nan]): + """ + Constructor for the BettiCurve class. + + Attributes: + resolution (int): number of sample for the piecewise-constant function (default 100). + sample_range ([double, double]): minimum and maximum of the piecewise-constant function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + """ + self.resolution, self.sample_range = resolution, sample_range + + def fit(self, X, y=None): + """ + Fit the BettiCurve class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.sample_range)).any(): + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) + return self + + def transform(self, X): + """ + Compute the Betti curve for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (**resolution**): output Betti curves. + """ + num_diag, Xfit = len(X), [] + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) + step_x = x_values[1] - x_values[0] + + for i in range(num_diag): + + diagram, num_pts_in_diag = X[i], X[i].shape[0] + + bc = np.zeros(self.resolution) + for j in range(num_pts_in_diag): + [px,py] = diagram[j,:2] + min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + for k in range(min_idx, max_idx): + bc[k] += 1 + + Xfit.append(np.reshape(bc,[1,-1])) + + Xfit = np.concatenate(Xfit, 0) + + return Xfit + +class Entropy(BaseEstimator, TransformerMixin): + """ + This is a class for computing persistence entropy. Persistence entropy is a statistic for persistence diagrams inspired from Shannon entropy. This statistic can also be used to compute a feature vector, called the entropy summary function. See https://arxiv.org/pdf/1803.08304.pdf for more details. + """ + def __init__(self, mode="scalar", normalized=True, resolution=100, sample_range=[np.nan, np.nan]): + """ + Constructor for the Entropy class. + + Attributes: + mode (string): what entropy to compute: either "scalar" for computing the entropy statistics, or "vector" for computing the entropy summary functions (default "scalar"). + normalized (bool): whether to normalize the entropy summary function (default True). Used only if **mode** = "vector". + resolution (int): number of sample for the entropy summary function (default 100). Used only if **mode** = "vector". + sample_range ([double, double]): minimum and maximum of the entropy summary function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. Used only if **mode** = "vector". + """ + self.mode, self.normalized, self.resolution, self.sample_range = mode, normalized, resolution, sample_range + + def fit(self, X, y=None): + """ + Fit the Entropy class on a list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.sample_range)).any(): + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) + return self + + def transform(self, X): + """ + Compute the entropy for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (1 if **mode** = "scalar" else **resolution**)): output entropy. + """ + num_diag, Xfit = len(X), [] + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) + step_x = x_values[1] - x_values[0] + new_X = BirthPersistenceTransform().fit_transform(X) + + for i in range(num_diag): + + orig_diagram, diagram, num_pts_in_diag = X[i], new_X[i], X[i].shape[0] + new_diagram = DiagramScaler(use=True, scalers=[([1], MaxAbsScaler())]).fit_transform([diagram])[0] + + if self.mode == "scalar": + ent = - np.sum( np.multiply(new_diagram[:,1], np.log(new_diagram[:,1])) ) + Xfit.append(np.array([[ent]])) + + else: + ent = np.zeros(self.resolution) + for j in range(num_pts_in_diag): + [px,py] = orig_diagram[j,:2] + min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + for k in range(min_idx, max_idx): + ent[k] += (-1) * new_diagram[j,1] * np.log(new_diagram[j,1]) + if self.normalized: + ent = ent / np.linalg.norm(ent, ord=1) + Xfit.append(np.reshape(ent,[1,-1])) + + Xfit = np.concatenate(Xfit, 0) + + return Xfit + +class TopologicalVector(BaseEstimator, TransformerMixin): + """ + This is a class for computing topological vectors from a list of persistence diagrams. The topological vector associated to a persistence diagram is the sorted vector of a slight modification of the pairwise distances between the persistence diagram points. See https://diglib.eg.org/handle/10.1111/cgf12692 for more details. + """ + def __init__(self, threshold=10): + """ + Constructor for the TopologicalVector class. + + Attributes: + threshold (int): number of distances to keep (default 10). This is the dimension of the topological vector. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding topological vector as threshold. + """ + self.threshold = threshold + + def fit(self, X, y=None): + """ + Fit the TopologicalVector class on a list of persistence diagrams (this function actually does nothing but is useful when TopologicalVector is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + Compute the topological vector for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (**threshold**): output topological vectors. + """ + if self.threshold == -1: + thresh = np.array([X[i].shape[0] for i in range(len(X))]).max() + else: + thresh = self.threshold + + num_diag = len(X) + Xfit = np.zeros([num_diag, thresh]) + + for i in range(num_diag): + + diagram, num_pts_in_diag = X[i], X[i].shape[0] + pers = 0.5 * (diagram[:,1]-diagram[:,0]) + min_pers = np.minimum(pers,np.transpose(pers)) + distances = DistanceMetric.get_metric("chebyshev").pairwise(diagram) + vect = np.flip(np.sort(np.triu(np.minimum(distances, min_pers)), axis=None), 0) + dim = min(len(vect), thresh) + Xfit[i, :dim] = vect[:dim] + + return Xfit + +class ComplexPolynomial(BaseEstimator, TransformerMixin): + """ + This is a class for computing complex polynomials from a list of persistence diagrams. The persistence diagram points are seen as the roots of some complex polynomial, whose coefficients are returned in a complex vector. See https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27 for more details. + """ + def __init__(self, F="R", threshold=10): + """ + Constructor for the ComplexPolynomial class. + + Attributes: + F (char): either "R", "S" or "T" (default "R"). Type of complex polynomial that is going to be computed (explained in https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27). + threshold (int): number of coefficients (default 10). This is the dimension of the complex vector of coefficients. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding complex vector of coefficients as threshold. + """ + self.threshold, self.F = threshold, F + + def fit(self, X, y=None): + """ + Fit the ComplexPolynomial class on a list of persistence diagrams (this function actually does nothing but is useful when ComplexPolynomial is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + Compute the complex vector of coefficients for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (**threshold**): output complex vectors of coefficients. + """ + if self.threshold == -1: + thresh = np.array([X[i].shape[0] for i in range(len(X))]).max() + else: + thresh = self.threshold + + Xfit = np.zeros([len(X), thresh]) + 1j * np.zeros([len(X), thresh]) + for d in range(len(X)): + D, N = X[d], X[d].shape[0] + if self.F == "R": + roots = D[:,0] + 1j * D[:,1] + elif self.F == "S": + alpha = np.linalg.norm(D, axis=1) + alpha = np.where(alpha==0, np.ones(N), alpha) + roots = np.multiply( np.multiply( (D[:,0]+1j*D[:,1]), (D[:,1]-D[:,0]) ), 1./(np.sqrt(2)*alpha) ) + elif self.F == "T": + alpha = np.linalg.norm(D, axis=1) + roots = np.multiply( (D[:,1]-D[:,0])/2, np.cos(alpha) - np.sin(alpha) + 1j * (np.cos(alpha) + np.sin(alpha)) ) + coeff = [0] * (N+1) + coeff[N] = 1 + for i in range(1, N+1): + for j in range(N-i-1, N): + coeff[j] += ((-1) * roots[i-1] * coeff[j+1]) + coeff = np.array(coeff[::-1])[1:] + Xfit[d, :min(thresh, coeff.shape[0])] = coeff[:min(thresh, coeff.shape[0])] + return Xfit -- cgit v1.2.3 From af98fb120eea4ebc09531de9f74684b50212ab7a Mon Sep 17 00:00:00 2001 From: mathieu Date: Wed, 11 Sep 2019 20:16:02 -0400 Subject: fixed error in DiagramScaler --- src/python/gudhi/sktda/preprocessing.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/sktda/preprocessing.py b/src/python/gudhi/sktda/preprocessing.py index 512b02f3..3c625053 100644 --- a/src/python/gudhi/sktda/preprocessing.py +++ b/src/python/gudhi/sktda/preprocessing.py @@ -64,25 +64,25 @@ class Clamping(BaseEstimator, TransformerMixin): def fit(self, X, y=None): """ - Fit the Clamping class on a list of list of values (this function actually does nothing but is useful when Clamping is included in a scikit-learn Pipeline). + Fit the Clamping class on a list of values (this function actually does nothing but is useful when Clamping is included in a scikit-learn Pipeline). Parameters: - X (list of numpy arrays of size n): input values. + X (numpy array of size n): input values. y (n x 1 array): value labels (unused). """ return self def transform(self, X): """ - Clamp each list of values individually. + Clamp list of values. Parameters: - X (list of numpy arrays of size n): input list of list of values. + X (numpy array of size n): input list of values. Returns: - Xfit (list of numpy arrays of size n): output list of list of values. + Xfit (numpy array of size n): output list of values. """ - Xfit = [np.where(L >= self.limit, self.limit * np.ones(L.shape), L) for L in X] + Xfit = np.where(X >= self.limit, self.limit * np.ones(X.shape), X) return Xfit class DiagramScaler(BaseEstimator, TransformerMixin): @@ -132,7 +132,8 @@ class DiagramScaler(BaseEstimator, TransformerMixin): for i in range(len(Xfit)): if Xfit[i].shape[0] > 0: for (indices, scaler) in self.scalers: - Xfit[i][:,indices] = scaler.transform(Xfit[i][:,indices]) + for I in indices: + Xfit[i][:,I] = np.squeeze(scaler.transform(np.reshape(Xfit[i][:,I], [-1,1]))) return Xfit class Padding(BaseEstimator, TransformerMixin): -- cgit v1.2.3 From d21df79601349387c4f7a6a1a3b19483f76f9380 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 20 Sep 2019 10:37:58 +0200 Subject: C++ 14 is the new standard for the project --- src/cmake/modules/GUDHI_compilation_flags.cmake | 2 +- src/python/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/cmake/modules/GUDHI_compilation_flags.cmake b/src/cmake/modules/GUDHI_compilation_flags.cmake index 86cd531b..6cd2614d 100644 --- a/src/cmake/modules/GUDHI_compilation_flags.cmake +++ b/src/cmake/modules/GUDHI_compilation_flags.cmake @@ -38,7 +38,7 @@ function(can_cgal_use_cxx11_thread_local) check_cxx_source_compiles("${CGAL_CAN_USE_CXX11_THREAD_LOCAL}" CGAL_CAN_USE_CXX11_THREAD_LOCAL_RESULT) endfunction() -set (CMAKE_CXX_STANDARD 11) +set (CMAKE_CXX_STANDARD 14) enable_testing() diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 9e128d30..5508cbc7 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -73,7 +73,7 @@ if(PYTHONINTERP_FOUND) if(MSVC) set(GUDHI_PYTHON_EXTRA_COMPILE_ARGS "${GUDHI_PYTHON_EXTRA_COMPILE_ARGS}'/fp:strict', ") else(MSVC) - set(GUDHI_PYTHON_EXTRA_COMPILE_ARGS "${GUDHI_PYTHON_EXTRA_COMPILE_ARGS}'-std=c++11', ") + set(GUDHI_PYTHON_EXTRA_COMPILE_ARGS "${GUDHI_PYTHON_EXTRA_COMPILE_ARGS}'-std=c++14', ") endif(MSVC) if(CMAKE_COMPILER_IS_GNUCXX) set(GUDHI_PYTHON_EXTRA_COMPILE_ARGS "${GUDHI_PYTHON_EXTRA_COMPILE_ARGS}'-frounding-math', ") -- cgit v1.2.3 From 89d678a78dd2bf05ef3d00e889fe0d9c51fb3b32 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 20 Sep 2019 17:36:57 +0200 Subject: c++14 in installation documentation --- src/common/doc/installation.h | 2 +- src/python/doc/installation.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/common/doc/installation.h b/src/common/doc/installation.h index 54f86573..2e64bef8 100644 --- a/src/common/doc/installation.h +++ b/src/common/doc/installation.h @@ -5,7 +5,7 @@ * Examples of GUDHI headers inclusion can be found in \ref utilities. * * \section compiling Compiling - * The library uses c++11 and requires Boost ≥ 1.56.0 + * The library uses c++14 and requires Boost ≥ 1.56.0 * and CMake ≥ 3.1. * It is a multi-platform library and compiles on Linux, Mac OSX and Visual Studio 2015. * diff --git a/src/python/doc/installation.rst b/src/python/doc/installation.rst index 5a6ad9f4..77d9e8b3 100644 --- a/src/python/doc/installation.rst +++ b/src/python/doc/installation.rst @@ -12,7 +12,7 @@ The easiest way to install the Python version of GUDHI is using Compiling ********* -The library uses c++11 and requires `Boost `_ ≥ 1.56.0, +The library uses c++14 and requires `Boost `_ ≥ 1.56.0, `CMake `_ ≥ 3.1 to generate makefiles, `NumPy `_ and `Cython `_ to compile the GUDHI Python module. -- cgit v1.2.3 From 1b007fc59f08bd01e1521eb1c0773b598bdf158b Mon Sep 17 00:00:00 2001 From: tlacombe Date: Mon, 23 Sep 2019 11:14:24 +0200 Subject: wasserstein distance added on fork --- src/python/doc/wasserstein_distance_sum.inc | 14 ++++++ src/python/doc/wasserstein_distance_user.rst | 39 +++++++++++++++ src/python/gudhi/wasserstein.py | 75 ++++++++++++++++++++++++++++ src/python/test/test_wasserstein_distance.py | 22 ++++++++ 4 files changed, 150 insertions(+) create mode 100644 src/python/doc/wasserstein_distance_sum.inc create mode 100644 src/python/doc/wasserstein_distance_user.rst create mode 100644 src/python/gudhi/wasserstein.py create mode 100755 src/python/test/test_wasserstein_distance.py (limited to 'src') diff --git a/src/python/doc/wasserstein_distance_sum.inc b/src/python/doc/wasserstein_distance_sum.inc new file mode 100644 index 00000000..0263f80f --- /dev/null +++ b/src/python/doc/wasserstein_distance_sum.inc @@ -0,0 +1,14 @@ +.. table:: + :widths: 30 50 20 + + +-----------------------------------------------------------------+----------------------------------------------------------------------+------------------------------------------------------------------+ + | .. figure:: | The p-Wasserstein distance measures the similarity between two | :Author: Theo Lacombe | + | ../../doc/Bottleneck_distance/perturb_pd.png | persistence diagrams. It's the minimum value c that can be achieve by| | + | :figclass: align-center | a perfect matching between the points of the two diagrams (+ all the | :Introduced in: GUDHI 2.0.0 | + | | diagonal points), where the value of a matching is defined as the | | + | Wasserstein distance is the p-th root of the sum of the | p-th root of the sum of all edges lengths to the power p. Edges | :Copyright: MIT (`GPL v3 `_) | + | edges lengths to the power p. | lengths are measured in norm q, for $1 \leq q \leq \infty$. | | + | | | :Requires: `Python Optimal Transport (POT)` | + +-----------------------------------------------------------------+----------------------------------------------------------------------+------------------------------------------------------------------+ + | * :doc:`wasserstein_distance_user` | | + +-----------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------+ diff --git a/src/python/doc/wasserstein_distance_user.rst b/src/python/doc/wasserstein_distance_user.rst new file mode 100644 index 00000000..a51cfb71 --- /dev/null +++ b/src/python/doc/wasserstein_distance_user.rst @@ -0,0 +1,39 @@ +:orphan: + +.. To get rid of WARNING: document isn't included in any toctree + +Wasserstein distance user manual +=============================== +Definition +---------- + +.. include:: wasserstein_distance_sum.inc + +This implementation is based on ideas from "Large Scale Computation of Means and Cluster for Persistence Diagrams via Optimal Transport". + +Function +-------- +.. autofunction:: gudhi.wasserstein_distance + + +Basic example +------------- + +This example computes the 1-Wasserstein distance from 2 persistence diagrams with euclidean ground metric. +Note that persistence diagrams must be submitted as (n x 2) numpy arrays and must not contain inf values. + +.. testcode:: + + import gudhi + + diag1 = np.array([[2.7, 3.7],[9.6, 14.],[34.2, 34.974]]) + diag2 = np.array([[2.8, 4.45],[9.5, 14.1]]) + + message = "Wasserstein distance value = " + '%.2f' % gudhi.wasserstein_distance(diag1, diag2, q=2., p=1.) + print(message) + +The output is: + +.. testoutput:: + + Wasserstein distance value = 1.45 diff --git a/src/python/gudhi/wasserstein.py b/src/python/gudhi/wasserstein.py new file mode 100644 index 00000000..cc527ed8 --- /dev/null +++ b/src/python/gudhi/wasserstein.py @@ -0,0 +1,75 @@ +import numpy as np +import scipy.spatial.distance as sc +try: + import ot +except ImportError: + print("POT (Python Optimal Transport) package is not installed. Try to run $ pip install POT") + +""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + Author(s): Theo Lacombe + + Copyright (C) 2016 Inria + + Modification(s): + - YYYY/MM Author: Description of the modification +""" + +def proj_on_diag(X): + ''' + param X: (n x 2) array encoding the points of a persistent diagram. + return: (n x 2) arary encoding the (respective orthogonal) projections of the points onto the diagonal + ''' + Z = (X[:,0] + X[:,1]) / 2. + return np.array([Z , Z]).T + + +def build_dist_matrix(X, Y, p=2., q=2.): + ''' + param X: (n x 2) np.array encoding the (points of the) first diagram. + param Y: (m x 2) np.array encoding the second diagram. + param q: Ground metric (i.e. norm l_q). + param p: exponent for the Wasserstein metric. + return: (n+1) x (m+1) np.array encoding the cost matrix C. + For 1 <= i <= n, 1 <= j <= m, C[i,j] encodes the distance between X[i] and Y[j], while C[i, m+1] (resp. C[n+1, j]) encodes the distance (to the p) between X[i] (resp Y[j]) and its orthogonal proj onto the diagonal. + note also that C[n+1, m+1] = 0 (it costs nothing to move from the diagonal to the diagonal). + ''' + Xdiag = proj_on_diag(X) + Ydiag = proj_on_diag(Y) + if np.isinf(p): + C = sc.cdist(X,Y, metric='chebyshev', p=q)**p + Cxd = np.linalg.norm(X - Xdiag, ord=q, axis=1)**p + Cdy = np.linalg.norm(Y - Ydiag, ord=q, axis=1)**p + else: + C = sc.cdist(X,Y, metric='minkowski', p=q)**p + Cxd = np.linalg.norm(X - Xdiag, ord=q, axis=1)**p + Cdy = np.linalg.norm(Y - Ydiag, ord=q, axis=1)**p + Cf = np.hstack((C, Cxd[:,None])) + Cdy = np.append(Cdy, 0) + + Cf = np.vstack((Cf, Cdy[None,:])) + + return Cf + + +def wasserstein_distance(X, Y, p=2., q=2.): + ''' + param X, Y: (n x 2) and (m x 2) numpy array (points of persistence diagrams) + param q: Ground metric (i.e. norm l_q); Default value is 2 (euclidean norm). + param p: exponent for Wasserstein; Default value is 2. + return: float, the p-Wasserstein distance (1 <= p < infty) with respect to the q-norm as ground metric. + ''' + M = build_dist_matrix(X, Y, p=p, q=q) + n = len(X) + m = len(Y) + a = 1.0 / (n + m) * np.ones(n) # weight vector of the input diagram. Uniform here. + hat_a = np.append(a, m/(n+m)) # so that we have a probability measure, required by POT + b = 1.0 / (n + m) * np.ones(m) # weight vector of the input diagram. Uniform here. + hat_b = np.append(b, n/(m+n)) # so that we have a probability measure, required by POT + + # Comptuation of the otcost using the ot.emd2 library. + # Note: it is the squared Wasserstein distance. + ot_cost = (n+m) * ot.emd2(hat_a, hat_b, M) + + return np.power(ot_cost, 1./p) + diff --git a/src/python/test/test_wasserstein_distance.py b/src/python/test/test_wasserstein_distance.py new file mode 100755 index 00000000..a5f7cf77 --- /dev/null +++ b/src/python/test/test_wasserstein_distance.py @@ -0,0 +1,22 @@ +import gudhi + +""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + Author(s): Theo Lacombe + + Copyright (C) 2016 Inria + + Modification(s): + - YYYY/MM Author: Description of the modification +""" + +__author__ = "Theo Lacombe" +__copyright__ = "Copyright (C) 2016 Inria" +__license__ = "MIT" + + +def test_basic_bottleneck(): + diag1 = np.array([[2.7, 3.7], [9.6, 14.0], [34.2, 34.974]]) + diag2 = np.array([[2.8, 4.45], [9.5, 14.1]]) + + assert gudhi.wasserstein_distance(diag1, diag2) == 1.4453593023967701 -- cgit v1.2.3 From 52d095ab15b6c308218861608975cc8ed90d187e Mon Sep 17 00:00:00 2001 From: tlacombe Date: Mon, 23 Sep 2019 18:07:22 +0200 Subject: update of CMakeLists and third_party_libraries.cmake to add wasserstein distance and POT dependancies --- src/cmake/modules/GUDHI_third_party_libraries.cmake | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/cmake/modules/GUDHI_third_party_libraries.cmake b/src/cmake/modules/GUDHI_third_party_libraries.cmake index 360a230b..5c76eeae 100644 --- a/src/cmake/modules/GUDHI_third_party_libraries.cmake +++ b/src/cmake/modules/GUDHI_third_party_libraries.cmake @@ -125,6 +125,7 @@ if( PYTHONINTERP_FOUND ) find_python_module("numpy") find_python_module("scipy") find_python_module("sphinx") + find_python_module("ot") endif() if(NOT GUDHI_PYTHON_PATH) -- cgit v1.2.3 From 982fa3738f847b53c72e43c3c854ff47ce846d1c Mon Sep 17 00:00:00 2001 From: tlacombe Date: Mon, 23 Sep 2019 18:08:01 +0200 Subject: update CMakeLists --- src/python/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src') diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 9e128d30..063a19e8 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -49,6 +49,7 @@ if(PYTHONINTERP_FOUND) set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'alpha_complex', ") set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'euclidean_witness_complex', ") set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'euclidean_strong_witness_complex', ") + set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'wasserstein', ") add_gudhi_debug_info("Python version ${PYTHON_VERSION_STRING}") add_gudhi_debug_info("Cython version ${CYTHON_VERSION}") @@ -199,6 +200,7 @@ if(PYTHONINTERP_FOUND) # Other .py files file(COPY "gudhi/persistence_graphical_tools.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") + file(COPY "gudhi/wasserstein.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") add_custom_command( OUTPUT gudhi.so @@ -371,6 +373,11 @@ if(PYTHONINTERP_FOUND) # Reader utils add_gudhi_py_test(test_reader_utils) + # Wasserstein + if(OT_FOUND) + add_gudhi_py_test(test_wasserstein_distance) + endif(OT_FOUND) + # Documentation generation is available through sphinx - requires all modules if(SPHINX_PATH) if(MATPLOTLIB_FOUND) -- cgit v1.2.3 From cb3346903c8b37ca617bf8c01e00eedc03031624 Mon Sep 17 00:00:00 2001 From: tlacombe Date: Mon, 23 Sep 2019 18:08:45 +0200 Subject: update test wasserstein. --- src/python/test/test_wasserstein_distance.py | 36 ++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/python/test/test_wasserstein_distance.py b/src/python/test/test_wasserstein_distance.py index a5f7cf77..c1b568e2 100755 --- a/src/python/test/test_wasserstein_distance.py +++ b/src/python/test/test_wasserstein_distance.py @@ -1,22 +1,50 @@ import gudhi +import numpy as np """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. Author(s): Theo Lacombe - Copyright (C) 2016 Inria + Copyright (C) 2019 Inria Modification(s): - YYYY/MM Author: Description of the modification """ __author__ = "Theo Lacombe" -__copyright__ = "Copyright (C) 2016 Inria" +__copyright__ = "Copyright (C) 2019 Inria" __license__ = "MIT" -def test_basic_bottleneck(): +def test_basic_wasserstein(): diag1 = np.array([[2.7, 3.7], [9.6, 14.0], [34.2, 34.974]]) diag2 = np.array([[2.8, 4.45], [9.5, 14.1]]) + diag3 = np.array([[0, 2], [4, 6]]) + diag4 = np.array([[0, 3], [4, 8]]) + emptydiag = np.array([[]]) + + assert gudhi.wasserstein_distance(emptydiag, emptydiag, q=2., p=1.) == 0. + assert gudhi.wasserstein_distance(emptydiag, emptydiag, q=np.inf, p=1.) == 0. + assert gudhi.wasserstein_distance(emptydiag, emptydiag, q=np.inf, p=2.) == 0. + assert gudhi.wasserstein_distance(emptydiag, emptydiag, q=2., p=2.) == 0. + + assert gudhi.wasserstein_distance(diag3, emptydiag, q=np.inf, p=1.) == 2. + assert gudhi.wasserstein_distance(diag3, emptydiag, q=1., p=1.) == 4. + + assert gudhi.wasserstein_distance(diag4, emptydiag, q=1., p=2.) == 5. # thank you Pythagorician triplets + assert gudhi.wasserstein_distance(diag4, emptydiag, q=np.inf, p=2.) == 2.5 + assert gudhi.wasserstein_distance(diag4, emptydiag, q=2., p=2.) == 3.5355339059327378 + + assert gudhi.wasserstein_distance(diag1, diag2, q=2., p=1.) == 1.4453593023967701 + assert gudhi.wasserstein_distance(diag1, diag2, q=2.35, p=1.74) == 0.9772734057168739 + + assert gudhi.wasserstein_distance(diag1, emptydiag, q=2.35, p=1.7863) == 3.141592214572228 + + assert gudhi.wasserstein_distance(diag3, diag4, q=1., p=1.) == 3. + assert gudhi.wasserstein_distance(diag3, diag4, q=np.inf, p=1.) == 3. # no diag matching here + assert gudhi.wasserstein_distance(diag3, diag4, q=np.inf, p=2.) == np.sqrt(5) + assert gudhi.wasserstein_distance(diag3, diag4, q=1., p=2.) == np.sqrt(5) + assert gudhi.wasserstein_distance(diag3, diag4, q=4.5, p=2.) == np.sqrt(5) + + - assert gudhi.wasserstein_distance(diag1, diag2) == 1.4453593023967701 -- cgit v1.2.3 From 36dfb09493f56f666367df39e5d1a170e49a1a23 Mon Sep 17 00:00:00 2001 From: tlacombe Date: Mon, 23 Sep 2019 18:10:14 +0200 Subject: updated doc for sphinx compilation --- src/python/doc/wasserstein_distance_user.rst | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/python/doc/wasserstein_distance_user.rst b/src/python/doc/wasserstein_distance_user.rst index a51cfb71..bcb7f19d 100644 --- a/src/python/doc/wasserstein_distance_user.rst +++ b/src/python/doc/wasserstein_distance_user.rst @@ -25,6 +25,7 @@ Note that persistence diagrams must be submitted as (n x 2) numpy arrays and mus .. testcode:: import gudhi + import numpy as np diag1 = np.array([[2.7, 3.7],[9.6, 14.],[34.2, 34.974]]) diag2 = np.array([[2.8, 4.45],[9.5, 14.1]]) -- cgit v1.2.3 From 3c98951fd157fe750f7df5b29258a19d4d314c1e Mon Sep 17 00:00:00 2001 From: tlacombe Date: Mon, 23 Sep 2019 18:11:34 +0200 Subject: updated wasserstein.py ; added _ in front of private functions, added q=np.inf, added emptydiagram management. --- src/python/gudhi/wasserstein.py | 51 +++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/wasserstein.py b/src/python/gudhi/wasserstein.py index cc527ed8..db42cc08 100644 --- a/src/python/gudhi/wasserstein.py +++ b/src/python/gudhi/wasserstein.py @@ -9,13 +9,13 @@ except ImportError: See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. Author(s): Theo Lacombe - Copyright (C) 2016 Inria + Copyright (C) 2019 Inria Modification(s): - YYYY/MM Author: Description of the modification """ -def proj_on_diag(X): +def _proj_on_diag(X): ''' param X: (n x 2) array encoding the points of a persistent diagram. return: (n x 2) arary encoding the (respective orthogonal) projections of the points onto the diagonal @@ -24,7 +24,7 @@ def proj_on_diag(X): return np.array([Z , Z]).T -def build_dist_matrix(X, Y, p=2., q=2.): +def _build_dist_matrix(X, Y, p=2., q=2.): ''' param X: (n x 2) np.array encoding the (points of the) first diagram. param Y: (m x 2) np.array encoding the second diagram. @@ -34,10 +34,10 @@ def build_dist_matrix(X, Y, p=2., q=2.): For 1 <= i <= n, 1 <= j <= m, C[i,j] encodes the distance between X[i] and Y[j], while C[i, m+1] (resp. C[n+1, j]) encodes the distance (to the p) between X[i] (resp Y[j]) and its orthogonal proj onto the diagonal. note also that C[n+1, m+1] = 0 (it costs nothing to move from the diagonal to the diagonal). ''' - Xdiag = proj_on_diag(X) - Ydiag = proj_on_diag(Y) - if np.isinf(p): - C = sc.cdist(X,Y, metric='chebyshev', p=q)**p + Xdiag = _proj_on_diag(X) + Ydiag = _proj_on_diag(Y) + if np.isinf(q): + C = sc.cdist(X,Y, metric='chebyshev')**p Cxd = np.linalg.norm(X - Xdiag, ord=q, axis=1)**p Cdy = np.linalg.norm(Y - Ydiag, ord=q, axis=1)**p else: @@ -52,24 +52,45 @@ def build_dist_matrix(X, Y, p=2., q=2.): return Cf +def _perstot(X, p, q): + ''' + param X: (n x 2) numpy array (points of a given diagram) + param q: Ground metric on the (upper-half) plane (i.e. norm l_q in R^2); Default value is 2 (euclidean norm). + param p: exponent for Wasserstein; Default value is 2. + return: float, the total persistence of the diagram (that is, its distance to the empty diagram). + ''' + Xdiag = _proj_on_diag(X) + return (np.sum(np.linalg.norm(X - Xdiag, ord=q, axis=1)**p))**(1/p) + + def wasserstein_distance(X, Y, p=2., q=2.): ''' param X, Y: (n x 2) and (m x 2) numpy array (points of persistence diagrams) - param q: Ground metric (i.e. norm l_q); Default value is 2 (euclidean norm). + param q: Ground metric on the (upper-half) plane (i.e. norm l_q in R^2); Default value is 2 (euclidean norm). param p: exponent for Wasserstein; Default value is 2. return: float, the p-Wasserstein distance (1 <= p < infty) with respect to the q-norm as ground metric. ''' - M = build_dist_matrix(X, Y, p=p, q=q) n = len(X) m = len(Y) - a = 1.0 / (n + m) * np.ones(n) # weight vector of the input diagram. Uniform here. - hat_a = np.append(a, m/(n+m)) # so that we have a probability measure, required by POT - b = 1.0 / (n + m) * np.ones(m) # weight vector of the input diagram. Uniform here. - hat_b = np.append(b, n/(m+n)) # so that we have a probability measure, required by POT + + # handle empty diagrams + if X.size == 0: + if Y.size == 0: + return 0. + else: + return _perstot(Y, p, q) + elif Y.size == 0: + return _perstot(X, p, q) + + M = _build_dist_matrix(X, Y, p=p, q=q) + a = np.full(n+1, 1. / (n + m) ) # weight vector of the input diagram. Uniform here. + a[-1] = a[-1] * m # normalized so that we have a probability measure, required by POT + b = np.full(m+1, 1. / (n + m) ) # weight vector of the input diagram. Uniform here. + b[-1] = b[-1] * n # so that we have a probability measure, required by POT # Comptuation of the otcost using the ot.emd2 library. # Note: it is the squared Wasserstein distance. - ot_cost = (n+m) * ot.emd2(hat_a, hat_b, M) + ot_cost = (n+m) * ot.emd2(a, b, M) - return np.power(ot_cost, 1./p) + return ot_cost ** (1./p) -- cgit v1.2.3 From b0c4bcce51e4c17660b378c374796d6d300002ed Mon Sep 17 00:00:00 2001 From: tlacombe Date: Mon, 23 Sep 2019 18:16:27 +0200 Subject: updated pot install instructions, including conda --- src/python/gudhi/wasserstein.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/python/gudhi/wasserstein.py b/src/python/gudhi/wasserstein.py index db42cc08..ae5f75a5 100644 --- a/src/python/gudhi/wasserstein.py +++ b/src/python/gudhi/wasserstein.py @@ -3,7 +3,7 @@ import scipy.spatial.distance as sc try: import ot except ImportError: - print("POT (Python Optimal Transport) package is not installed. Try to run $ pip install POT") + print("POT (Python Optimal Transport) package is not installed. Try to run $ conda install -c conda-forge pot ; or $ pip install POT") """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. -- cgit v1.2.3 From bbbba969e769277140920b17f0d92e4e00f1b904 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 24 Sep 2019 15:19:25 +0200 Subject: no python documentation generation if POT is not found --- src/python/CMakeLists.txt | 60 +++++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 063a19e8..07931d10 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -65,6 +65,9 @@ if(PYTHONINTERP_FOUND) if(SCIPY_FOUND) add_gudhi_debug_info("Scipy version ${SCIPY_VERSION}") endif() + if(OT_FOUND) + add_gudhi_debug_info("POT version ${OT_VERSION}") + endif() set(GUDHI_PYTHON_EXTRA_COMPILE_ARGS "${GUDHI_PYTHON_EXTRA_COMPILE_ARGS}'-DBOOST_RESULT_OF_USE_DECLTYPE', ") set(GUDHI_PYTHON_EXTRA_COMPILE_ARGS "${GUDHI_PYTHON_EXTRA_COMPILE_ARGS}'-DBOOST_ALL_NO_LIB', ") @@ -375,7 +378,7 @@ if(PYTHONINTERP_FOUND) # Wasserstein if(OT_FOUND) - add_gudhi_py_test(test_wasserstein_distance) + add_gudhi_py_test(test_wasserstein_distance) endif(OT_FOUND) # Documentation generation is available through sphinx - requires all modules @@ -383,32 +386,37 @@ if(PYTHONINTERP_FOUND) if(MATPLOTLIB_FOUND) if(NUMPY_FOUND) if(SCIPY_FOUND) - if(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) - set (GUDHI_SPHINX_MESSAGE "Generating API documentation with Sphinx in ${CMAKE_CURRENT_BINARY_DIR}/sphinx/") - # User warning - Sphinx is a static pages generator, and configured to work fine with user_version - # Images and biblio warnings because not found on developper version - if (GUDHI_PYTHON_PATH STREQUAL "src/python") - set (GUDHI_SPHINX_MESSAGE "${GUDHI_SPHINX_MESSAGE} \n WARNING : Sphinx is configured for user version, you run it on developper version. Images and biblio will miss") - endif() - # sphinx target requires gudhi.so, because conf.py reads gudhi version from it - add_custom_target(sphinx - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/doc - COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" - ${SPHINX_PATH} -b html ${CMAKE_CURRENT_SOURCE_DIR}/doc ${CMAKE_CURRENT_BINARY_DIR}/sphinx - DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/gudhi.so" - COMMENT "${GUDHI_SPHINX_MESSAGE}" VERBATIM) - - add_test(NAME sphinx_py_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" - ${SPHINX_PATH} -b doctest ${CMAKE_CURRENT_SOURCE_DIR}/doc ${CMAKE_CURRENT_BINARY_DIR}/doctest) - - # Set missing or not modules - set(GUDHI_MODULES ${GUDHI_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MODULES") - else(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) - message("++ Python documentation module will not be compiled because it requires a Eigen3 and CGAL version >= 4.11.0") + if(OT_FOUND) + if(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) + set (GUDHI_SPHINX_MESSAGE "Generating API documentation with Sphinx in ${CMAKE_CURRENT_BINARY_DIR}/sphinx/") + # User warning - Sphinx is a static pages generator, and configured to work fine with user_version + # Images and biblio warnings because not found on developper version + if (GUDHI_PYTHON_PATH STREQUAL "src/python") + set (GUDHI_SPHINX_MESSAGE "${GUDHI_SPHINX_MESSAGE} \n WARNING : Sphinx is configured for user version, you run it on developper version. Images and biblio will miss") + endif() + # sphinx target requires gudhi.so, because conf.py reads gudhi version from it + add_custom_target(sphinx + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/doc + COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" + ${SPHINX_PATH} -b html ${CMAKE_CURRENT_SOURCE_DIR}/doc ${CMAKE_CURRENT_BINARY_DIR}/sphinx + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/gudhi.so" + COMMENT "${GUDHI_SPHINX_MESSAGE}" VERBATIM) + + add_test(NAME sphinx_py_test + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" + ${SPHINX_PATH} -b doctest ${CMAKE_CURRENT_SOURCE_DIR}/doc ${CMAKE_CURRENT_BINARY_DIR}/doctest) + + # Set missing or not modules + set(GUDHI_MODULES ${GUDHI_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MODULES") + else(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) + message("++ Python documentation module will not be compiled because it requires a Eigen3 and CGAL version >= 4.11.0") + set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MISSING_MODULES") + endif(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) + else(OT_FOUND) + message("++ Python documentation module will not be compiled because POT was not found") set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MISSING_MODULES") - endif(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) + endif(OT_FOUND) else(SCIPY_FOUND) message("++ Python documentation module will not be compiled because scipy was not found") set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MISSING_MODULES") -- cgit v1.2.3 From eb94efd322862ee32775493ad8b389682dadc75e Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Wed, 9 Oct 2019 10:54:31 +0900 Subject: Fix wikipedia link for Cech complex --- src/Cech_complex/doc/Intro_cech_complex.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/Cech_complex/doc/Intro_cech_complex.h b/src/Cech_complex/doc/Intro_cech_complex.h index 90086de7..80c88dc6 100644 --- a/src/Cech_complex/doc/Intro_cech_complex.h +++ b/src/Cech_complex/doc/Intro_cech_complex.h @@ -24,7 +24,7 @@ namespace cech_complex { * \section cechdefinition Čech complex definition * * Čech complex - * (Wikipedia) is a + * (Wikipedia) is a * simplicial complex constructed * from a proximity graph. The set of all simplices is filtered by the radius of their minimal enclosing ball. * -- cgit v1.2.3 From 470ee54e1bdb0ff074db9213412766476ccf51fb Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 10 Oct 2019 10:03:21 +0200 Subject: Add maximal edge length parameter for sparse rips persistence utility --- src/Rips_complex/utilities/ripscomplex.md | 1 + src/Rips_complex/utilities/sparse_rips_persistence.cpp | 15 +++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/Rips_complex/utilities/ripscomplex.md b/src/Rips_complex/utilities/ripscomplex.md index 03838085..61f31e3c 100644 --- a/src/Rips_complex/utilities/ripscomplex.md +++ b/src/Rips_complex/utilities/ripscomplex.md @@ -99,6 +99,7 @@ where `dim` is the dimension of the homological feature, `birth` and `death` are * `-h [ --help ]` Produce help message * `-o [ --output-file ]` Name of file in which the persistence diagram is written. Default print in standard output. +* `-r [ --max-edge-length ]` (default = inf) Maximal length of an edge for the Rips complex construction. * `-e [ --approximation ]` (default = .5) Epsilon, where the sparse Rips complex is a (1+epsilon)/(1-epsilon)-approximation of the Rips complex. * `-d [ --cpx-dimension ]` (default = INT_MAX) Maximal dimension of the Rips complex we want to compute. * `-p [ --field-charac ]` (default = 11) Characteristic p of the coefficient field Z/pZ for computing homology. diff --git a/src/Rips_complex/utilities/sparse_rips_persistence.cpp b/src/Rips_complex/utilities/sparse_rips_persistence.cpp index 1a86eafe..cefd8a67 100644 --- a/src/Rips_complex/utilities/sparse_rips_persistence.cpp +++ b/src/Rips_complex/utilities/sparse_rips_persistence.cpp @@ -28,21 +28,24 @@ using Persistent_cohomology = Gudhi::persistent_cohomology::Persistent_cohomolog using Point = std::vector; using Points_off_reader = Gudhi::Points_off_reader; -void program_options(int argc, char* argv[], std::string& off_file_points, std::string& filediag, double& epsilon, +void program_options(int argc, char* argv[], std::string& off_file_points, std::string& filediag, + Filtration_value& threshold, double& epsilon, int& dim_max, int& p, Filtration_value& min_persistence); int main(int argc, char* argv[]) { std::string off_file_points; std::string filediag; + Filtration_value threshold; double epsilon; int dim_max; int p; Filtration_value min_persistence; - program_options(argc, argv, off_file_points, filediag, epsilon, dim_max, p, min_persistence); + program_options(argc, argv, off_file_points, filediag, threshold, epsilon, dim_max, p, min_persistence); Points_off_reader off_reader(off_file_points); - Sparse_rips sparse_rips(off_reader.get_point_cloud(), Gudhi::Euclidean_distance(), epsilon); + Sparse_rips sparse_rips(off_reader.get_point_cloud(), Gudhi::Euclidean_distance(), epsilon, + -std::numeric_limits::infinity(), threshold); // Construct the Rips complex in a Simplex Tree Simplex_tree simplex_tree; @@ -73,7 +76,8 @@ int main(int argc, char* argv[]) { return 0; } -void program_options(int argc, char* argv[], std::string& off_file_points, std::string& filediag, double& epsilon, +void program_options(int argc, char* argv[], std::string& off_file_points, std::string& filediag, + Filtration_value& threshold, double& epsilon, int& dim_max, int& p, Filtration_value& min_persistence) { namespace po = boost::program_options; po::options_description hidden("Hidden options"); @@ -84,6 +88,9 @@ void program_options(int argc, char* argv[], std::string& off_file_points, std:: visible.add_options()("help,h", "produce help message")( "output-file,o", po::value(&filediag)->default_value(std::string()), "Name of file in which the persistence diagram is written. Default print in std::cout")( + "max-edge-length,r", + po::value(&threshold)->default_value(std::numeric_limits::infinity()), + "Maximal length of an edge for the Rips complex construction.")( "approximation,e", po::value(&epsilon)->default_value(.5), "Epsilon, where the sparse Rips complex is a (1+epsilon)-approximation of the Rips complex.")( "cpx-dimension,d", po::value(&dim_max)->default_value(std::numeric_limits::max()), -- cgit v1.2.3 From f9ec015c1bdd01068771d0d04ff55e0436ffc879 Mon Sep 17 00:00:00 2001 From: tlacombe Date: Thu, 17 Oct 2019 11:55:22 +0200 Subject: updated index.rst to include Wasserstein distance --- src/python/doc/index.rst | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/python/doc/index.rst b/src/python/doc/index.rst index e379bc23..16d918bc 100644 --- a/src/python/doc/index.rst +++ b/src/python/doc/index.rst @@ -73,6 +73,11 @@ Bottleneck distance .. include:: bottleneck_distance_sum.inc +Wasserstein distance +=================== + +.. include:: wasserstein_distance_sum.inc + Persistence graphical tools =========================== -- cgit v1.2.3 From 91632989f92b89752dd4e59836dff80b43f349f1 Mon Sep 17 00:00:00 2001 From: tlacombe Date: Thu, 17 Oct 2019 11:56:20 +0200 Subject: updated wasserstein doc to be sphinx-compatible + correction of typo in the .rst --- src/python/doc/wasserstein_distance_sum.inc | 4 ++-- src/python/gudhi/wasserstein.py | 32 +++++++++++++++-------------- 2 files changed, 19 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/python/doc/wasserstein_distance_sum.inc b/src/python/doc/wasserstein_distance_sum.inc index 0263f80f..3b0b9025 100644 --- a/src/python/doc/wasserstein_distance_sum.inc +++ b/src/python/doc/wasserstein_distance_sum.inc @@ -7,8 +7,8 @@ | :figclass: align-center | a perfect matching between the points of the two diagrams (+ all the | :Introduced in: GUDHI 2.0.0 | | | diagonal points), where the value of a matching is defined as the | | | Wasserstein distance is the p-th root of the sum of the | p-th root of the sum of all edges lengths to the power p. Edges | :Copyright: MIT (`GPL v3 `_) | - | edges lengths to the power p. | lengths are measured in norm q, for $1 \leq q \leq \infty$. | | - | | | :Requires: `Python Optimal Transport (POT)` | + | edges lengths to the power p. | lengths are measured in norm q, for :math:`1 \leq q \leq \infty`. | | + | | | :Requires: Python Optimal Transport (POT) :math:`\geq` 0.5.1 | +-----------------------------------------------------------------+----------------------------------------------------------------------+------------------------------------------------------------------+ | * :doc:`wasserstein_distance_user` | | +-----------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------+ diff --git a/src/python/gudhi/wasserstein.py b/src/python/gudhi/wasserstein.py index ae5f75a5..0b2fe79a 100644 --- a/src/python/gudhi/wasserstein.py +++ b/src/python/gudhi/wasserstein.py @@ -17,8 +17,8 @@ except ImportError: def _proj_on_diag(X): ''' - param X: (n x 2) array encoding the points of a persistent diagram. - return: (n x 2) arary encoding the (respective orthogonal) projections of the points onto the diagonal + :param X: (n x 2) array encoding the points of a persistent diagram. + :returns: (n x 2) arary encoding the (respective orthogonal) projections of the points onto the diagonal ''' Z = (X[:,0] + X[:,1]) / 2. return np.array([Z , Z]).T @@ -26,11 +26,11 @@ def _proj_on_diag(X): def _build_dist_matrix(X, Y, p=2., q=2.): ''' - param X: (n x 2) np.array encoding the (points of the) first diagram. - param Y: (m x 2) np.array encoding the second diagram. - param q: Ground metric (i.e. norm l_q). - param p: exponent for the Wasserstein metric. - return: (n+1) x (m+1) np.array encoding the cost matrix C. + :param X: (n x 2) np.array encoding the (points of the) first diagram. + :param Y: (m x 2) np.array encoding the second diagram. + :param q: Ground metric (i.e. norm l_q). + :param p: exponent for the Wasserstein metric. + :returns: (n+1) x (m+1) np.array encoding the cost matrix C. For 1 <= i <= n, 1 <= j <= m, C[i,j] encodes the distance between X[i] and Y[j], while C[i, m+1] (resp. C[n+1, j]) encodes the distance (to the p) between X[i] (resp Y[j]) and its orthogonal proj onto the diagonal. note also that C[n+1, m+1] = 0 (it costs nothing to move from the diagonal to the diagonal). ''' @@ -54,10 +54,10 @@ def _build_dist_matrix(X, Y, p=2., q=2.): def _perstot(X, p, q): ''' - param X: (n x 2) numpy array (points of a given diagram) - param q: Ground metric on the (upper-half) plane (i.e. norm l_q in R^2); Default value is 2 (euclidean norm). - param p: exponent for Wasserstein; Default value is 2. - return: float, the total persistence of the diagram (that is, its distance to the empty diagram). + :param X: (n x 2) numpy array (points of a given diagram) + :param q: Ground metric on the (upper-half) plane (i.e. norm l_q in R^2); Default value is 2 (euclidean norm). + :param p: exponent for Wasserstein; Default value is 2. + :returns: float, the total persistence of the diagram (that is, its distance to the empty diagram). ''' Xdiag = _proj_on_diag(X) return (np.sum(np.linalg.norm(X - Xdiag, ord=q, axis=1)**p))**(1/p) @@ -65,10 +65,12 @@ def _perstot(X, p, q): def wasserstein_distance(X, Y, p=2., q=2.): ''' - param X, Y: (n x 2) and (m x 2) numpy array (points of persistence diagrams) - param q: Ground metric on the (upper-half) plane (i.e. norm l_q in R^2); Default value is 2 (euclidean norm). - param p: exponent for Wasserstein; Default value is 2. - return: float, the p-Wasserstein distance (1 <= p < infty) with respect to the q-norm as ground metric. + :param X: (n x 2) np.array encoding the (points of the) first diagram. + :param Y: (m x 2) np.array encoding the second diagram. + :param q: Ground metric on the (upper-half) plane (i.e. norm l_q in R^2); Default value is 2 (euclidean norm). + :param p: exponent for Wasserstein; Default value is 2. + :returns: the p-Wasserstein distance (1 <= p < infty) with respect to the q-norm as ground metric. + :rtype: float ''' n = len(X) m = len(Y) -- cgit v1.2.3 From a6c844172072f1798bbbe6d5c9b4f3a548cb4c50 Mon Sep 17 00:00:00 2001 From: Siargey Kachanovich Date: Thu, 17 Oct 2019 14:21:07 +0200 Subject: Include for std::fabs --- src/common/include/gudhi/Unitary_tests_utils.h | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/common/include/gudhi/Unitary_tests_utils.h b/src/common/include/gudhi/Unitary_tests_utils.h index 4ad4dae8..7d039304 100644 --- a/src/common/include/gudhi/Unitary_tests_utils.h +++ b/src/common/include/gudhi/Unitary_tests_utils.h @@ -14,6 +14,7 @@ #include #include // for std::numeric_limits<> +#include // for std::fabs template void GUDHI_TEST_FLOAT_EQUALITY_CHECK(FloatingType a, FloatingType b, -- cgit v1.2.3 From ddf16037ca92675a5f13ea84d1f3c94467932dc7 Mon Sep 17 00:00:00 2001 From: Jose Marino Date: Thu, 17 Oct 2019 15:01:23 -0600 Subject: fix documentation not being generated with Doxygen 1.8.16 --- src/Doxyfile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/Doxyfile.in b/src/Doxyfile.in index 57775498..ec551882 100644 --- a/src/Doxyfile.in +++ b/src/Doxyfile.in @@ -765,7 +765,7 @@ INPUT_ENCODING = UTF-8 # *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, # *.qsf, *.as and *.js. -FILE_PATTERNS = +#FILE_PATTERNS = # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. -- cgit v1.2.3 From 7ea7dfe83e93dc3d33d8d50917f718c05f32ca7f Mon Sep 17 00:00:00 2001 From: tlacombe Date: Thu, 17 Oct 2019 23:54:39 +0200 Subject: removed GPL license, updated expected gudhi release (set at 3.1.0 for now) --- src/python/doc/wasserstein_distance_sum.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/python/doc/wasserstein_distance_sum.inc b/src/python/doc/wasserstein_distance_sum.inc index 3b0b9025..24b72c0e 100644 --- a/src/python/doc/wasserstein_distance_sum.inc +++ b/src/python/doc/wasserstein_distance_sum.inc @@ -4,9 +4,9 @@ +-----------------------------------------------------------------+----------------------------------------------------------------------+------------------------------------------------------------------+ | .. figure:: | The p-Wasserstein distance measures the similarity between two | :Author: Theo Lacombe | | ../../doc/Bottleneck_distance/perturb_pd.png | persistence diagrams. It's the minimum value c that can be achieve by| | - | :figclass: align-center | a perfect matching between the points of the two diagrams (+ all the | :Introduced in: GUDHI 2.0.0 | + | :figclass: align-center | a perfect matching between the points of the two diagrams (+ all the | :Introduced in: GUDHI 3.1.0 | | | diagonal points), where the value of a matching is defined as the | | - | Wasserstein distance is the p-th root of the sum of the | p-th root of the sum of all edges lengths to the power p. Edges | :Copyright: MIT (`GPL v3 `_) | + | Wasserstein distance is the p-th root of the sum of the | p-th root of the sum of all edges lengths to the power p. Edges | :Copyright: MIT | | edges lengths to the power p. | lengths are measured in norm q, for :math:`1 \leq q \leq \infty`. | | | | | :Requires: Python Optimal Transport (POT) :math:`\geq` 0.5.1 | +-----------------------------------------------------------------+----------------------------------------------------------------------+------------------------------------------------------------------+ -- cgit v1.2.3 From a625d5879beaaae47118bc79fda17f441483dffe Mon Sep 17 00:00:00 2001 From: tlacombe Date: Thu, 17 Oct 2019 23:55:17 +0200 Subject: Few improvements to the documentation. --- src/python/gudhi/wasserstein.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/wasserstein.py b/src/python/gudhi/wasserstein.py index 0b2fe79a..4e17811d 100644 --- a/src/python/gudhi/wasserstein.py +++ b/src/python/gudhi/wasserstein.py @@ -26,8 +26,8 @@ def _proj_on_diag(X): def _build_dist_matrix(X, Y, p=2., q=2.): ''' - :param X: (n x 2) np.array encoding the (points of the) first diagram. - :param Y: (m x 2) np.array encoding the second diagram. + :param X: (n x 2) numpy.array encoding the (points of the) first diagram. + :param Y: (m x 2) numpy.array encoding the second diagram. :param q: Ground metric (i.e. norm l_q). :param p: exponent for the Wasserstein metric. :returns: (n+1) x (m+1) np.array encoding the cost matrix C. @@ -54,19 +54,19 @@ def _build_dist_matrix(X, Y, p=2., q=2.): def _perstot(X, p, q): ''' - :param X: (n x 2) numpy array (points of a given diagram) - :param q: Ground metric on the (upper-half) plane (i.e. norm l_q in R^2); Default value is 2 (euclidean norm). + :param X: (n x 2) numpy.array (points of a given diagram). + :param q: Ground metric on the (upper-half) plane (i.e. norm l_q in R^2); Default value is 2 (Euclidean norm). :param p: exponent for Wasserstein; Default value is 2. :returns: float, the total persistence of the diagram (that is, its distance to the empty diagram). ''' Xdiag = _proj_on_diag(X) - return (np.sum(np.linalg.norm(X - Xdiag, ord=q, axis=1)**p))**(1/p) + return (np.sum(np.linalg.norm(X - Xdiag, ord=q, axis=1)**p))**(1./p) def wasserstein_distance(X, Y, p=2., q=2.): ''' - :param X: (n x 2) np.array encoding the (points of the) first diagram. - :param Y: (m x 2) np.array encoding the second diagram. + :param X: (n x 2) numpy.array encoding the (finite points of the) first diagram. Must not contain essential points (i.e. with infinite coordinate). + :param Y: (m x 2) numpy.array encoding the second diagram. :param q: Ground metric on the (upper-half) plane (i.e. norm l_q in R^2); Default value is 2 (euclidean norm). :param p: exponent for Wasserstein; Default value is 2. :returns: the p-Wasserstein distance (1 <= p < infty) with respect to the q-norm as ground metric. -- cgit v1.2.3 From a3e609d04f296f80edf622ba1d6e0efe71eb8c41 Mon Sep 17 00:00:00 2001 From: Théo Lacombe Date: Fri, 18 Oct 2019 22:17:48 +0200 Subject: Update src/python/gudhi/wasserstein.py Co-Authored-By: Marc Glisse --- src/python/gudhi/wasserstein.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/python/gudhi/wasserstein.py b/src/python/gudhi/wasserstein.py index 4e17811d..32d236c5 100644 --- a/src/python/gudhi/wasserstein.py +++ b/src/python/gudhi/wasserstein.py @@ -69,7 +69,7 @@ def wasserstein_distance(X, Y, p=2., q=2.): :param Y: (m x 2) numpy.array encoding the second diagram. :param q: Ground metric on the (upper-half) plane (i.e. norm l_q in R^2); Default value is 2 (euclidean norm). :param p: exponent for Wasserstein; Default value is 2. - :returns: the p-Wasserstein distance (1 <= p < infty) with respect to the q-norm as ground metric. + :returns: the p-Wasserstein distance (1 <= p < infinity) with respect to the q-norm as ground metric. :rtype: float ''' n = len(X) -- cgit v1.2.3 From 12f62eca36952053169a71f70169a0e15fa481fc Mon Sep 17 00:00:00 2001 From: tlacombe Date: Fri, 18 Oct 2019 22:30:07 +0200 Subject: correction edges lengths ==> edge lengths --- src/python/doc/wasserstein_distance_sum.inc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/python/doc/wasserstein_distance_sum.inc b/src/python/doc/wasserstein_distance_sum.inc index 24b72c0e..ffd4d312 100644 --- a/src/python/doc/wasserstein_distance_sum.inc +++ b/src/python/doc/wasserstein_distance_sum.inc @@ -3,11 +3,11 @@ +-----------------------------------------------------------------+----------------------------------------------------------------------+------------------------------------------------------------------+ | .. figure:: | The p-Wasserstein distance measures the similarity between two | :Author: Theo Lacombe | - | ../../doc/Bottleneck_distance/perturb_pd.png | persistence diagrams. It's the minimum value c that can be achieve by| | - | :figclass: align-center | a perfect matching between the points of the two diagrams (+ all the | :Introduced in: GUDHI 3.1.0 | + | ../../doc/Bottleneck_distance/perturb_pd.png | persistence diagrams. It's the minimum value c that can be achieved | | + | :figclass: align-center | by a perfect matching between the points of the two diagrams (+ all | :Introduced in: GUDHI 3.1.0 | | | diagonal points), where the value of a matching is defined as the | | - | Wasserstein distance is the p-th root of the sum of the | p-th root of the sum of all edges lengths to the power p. Edges | :Copyright: MIT | - | edges lengths to the power p. | lengths are measured in norm q, for :math:`1 \leq q \leq \infty`. | | + | Wasserstein distance is the p-th root of the sum of the | p-th root of the sum of all edge lengths to the power p. Edge lengths| :Copyright: MIT | + | edge lengths to the power p. | are measured in norm q, for :math:`1 \leq q \leq \infty`. | | | | | :Requires: Python Optimal Transport (POT) :math:`\geq` 0.5.1 | +-----------------------------------------------------------------+----------------------------------------------------------------------+------------------------------------------------------------------+ | * :doc:`wasserstein_distance_user` | | -- cgit v1.2.3 From e0feb6725f3970eea66c8e3268ec1a55020279bd Mon Sep 17 00:00:00 2001 From: Théo Lacombe Date: Fri, 18 Oct 2019 22:31:38 +0200 Subject: Update src/python/gudhi/wasserstein.py typo correction Co-Authored-By: Marc Glisse --- src/python/gudhi/wasserstein.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/python/gudhi/wasserstein.py b/src/python/gudhi/wasserstein.py index 32d236c5..445772e4 100644 --- a/src/python/gudhi/wasserstein.py +++ b/src/python/gudhi/wasserstein.py @@ -18,7 +18,7 @@ except ImportError: def _proj_on_diag(X): ''' :param X: (n x 2) array encoding the points of a persistent diagram. - :returns: (n x 2) arary encoding the (respective orthogonal) projections of the points onto the diagonal + :returns: (n x 2) array encoding the (respective orthogonal) projections of the points onto the diagonal ''' Z = (X[:,0] + X[:,1]) / 2. return np.array([Z , Z]).T -- cgit v1.2.3 From 0c6641b4e109f5116d6dad04bbab9bde0d56e347 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 24 Oct 2019 08:24:24 +0200 Subject: Add debug traces for read_persistence_intervals_and_dimension --- src/common/include/gudhi/reader_utils.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/common/include/gudhi/reader_utils.h b/src/common/include/gudhi/reader_utils.h index 98335552..ee811967 100644 --- a/src/common/include/gudhi/reader_utils.h +++ b/src/common/include/gudhi/reader_utils.h @@ -305,8 +305,12 @@ void read_persistence_intervals_and_dimension(std::string const& filename, Outpu std::string line; getline(in, line); if (line.length() != 0 && line[0] != '#') { - double numbers[4]; + double numbers[4] = {0.}; int n = sscanf(line.c_str(), "%lf %lf %lf %lf", &numbers[0], &numbers[1], &numbers[2], &numbers[3]); +#ifdef DEBUG_TRACES + std::cout << "[" << n << "] = " << numbers[0] << "," << numbers[1] + << "," << numbers[2] << "," << numbers[3] << std::endl; +#endif // DEBUG_TRACES if (n >= 2) { int dim = (n >= 3 ? static_cast(numbers[n - 3]) : -1); *out++ = std::make_tuple(dim, numbers[n - 2], numbers[n - 1]); -- cgit v1.2.3 From 8227cd68d5aa7c9eeda5dd474f2536b896b6f491 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 24 Oct 2019 08:30:06 +0200 Subject: Fix issues #113 (replace 'is not' with '!=') and #109 (replace palette with a more visible one) --- src/python/gudhi/persistence_graphical_tools.py | 57 +++++++++++-------------- 1 file changed, 25 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/persistence_graphical_tools.py b/src/python/gudhi/persistence_graphical_tools.py index 181bc8ea..a8e2051b 100644 --- a/src/python/gudhi/persistence_graphical_tools.py +++ b/src/python/gudhi/persistence_graphical_tools.py @@ -44,27 +44,6 @@ def __min_birth_max_death(persistence, band=0.0): max_death += band return (min_birth, max_death) - -""" -Only 13 colors for the palette -""" -palette = [ - "#ff0000", - "#00ff00", - "#0000ff", - "#00ffff", - "#ff00ff", - "#ffff00", - "#000000", - "#880000", - "#008800", - "#000088", - "#888800", - "#880088", - "#008888", -] - - def plot_persistence_barcode( persistence=[], persistence_file="", @@ -73,6 +52,7 @@ def plot_persistence_barcode( max_barcodes=1000, inf_delta=0.1, legend=False, + colormap=None ): """This function plots the persistence bar code from persistence values list or from a :doc:`persistence file `. @@ -95,6 +75,9 @@ def plot_persistence_barcode( :type inf_delta: float. :param legend: Display the dimension color legend (default is False). :type legend: boolean. + :param colormap: A matplotlib-like qualitative colormaps. Default is None + which means :code:`matplotlib.cm.Set1.colors`. + :type colormap: tuple of colors (3-tuple of float between 0. and 1.). :returns: A matplotlib object containing horizontal bar plot of persistence (launch `show()` method on it to display it). """ @@ -102,7 +85,7 @@ def plot_persistence_barcode( import matplotlib.pyplot as plt import matplotlib.patches as mpatches - if persistence_file is not "": + if persistence_file != "": if path.isfile(persistence_file): # Reset persistence persistence = [] @@ -116,7 +99,7 @@ def plot_persistence_barcode( print("file " + persistence_file + " not found.") return None - if max_barcodes is not 1000: + if max_barcodes != 1000: print("Deprecated parameter. It has been replaced by max_intervals") max_intervals = max_barcodes @@ -127,6 +110,9 @@ def plot_persistence_barcode( key=lambda life_time: life_time[1][1] - life_time[1][0], reverse=True, )[:max_intervals] + + if colormap == None: + colormap = plt.cm.Set1.colors persistence = sorted(persistence, key=lambda birth: birth[1][0]) @@ -147,7 +133,7 @@ def plot_persistence_barcode( height=0.8, left=interval[1][0], alpha=alpha, - color=palette[interval[0]], + color=colormap[interval[0]], linewidth=0, ) else: @@ -158,7 +144,7 @@ def plot_persistence_barcode( height=0.8, left=interval[1][0], alpha=alpha, - color=palette[interval[0]], + color=colormap[interval[0]], linewidth=0, ) ind = ind + 1 @@ -167,7 +153,7 @@ def plot_persistence_barcode( dimensions = list(set(item[0] for item in persistence)) plt.legend( handles=[ - mpatches.Patch(color=palette[dim], label=str(dim)) + mpatches.Patch(color=colormap[dim], label=str(dim)) for dim in dimensions ], loc="lower right", @@ -190,6 +176,7 @@ def plot_persistence_diagram( max_plots=1000, inf_delta=0.1, legend=False, + colormap=None ): """This function plots the persistence diagram from persistence values list or from a :doc:`persistence file `. @@ -214,6 +201,9 @@ def plot_persistence_diagram( :type inf_delta: float. :param legend: Display the dimension color legend (default is False). :type legend: boolean. + :param colormap: A matplotlib-like qualitative colormaps. Default is None + which means :code:`matplotlib.cm.Set1.colors`. + :type colormap: tuple of colors (3-tuple of float between 0. and 1.). :returns: A matplotlib object containing diagram plot of persistence (launch `show()` method on it to display it). """ @@ -221,7 +211,7 @@ def plot_persistence_diagram( import matplotlib.pyplot as plt import matplotlib.patches as mpatches - if persistence_file is not "": + if persistence_file != "": if path.isfile(persistence_file): # Reset persistence persistence = [] @@ -235,7 +225,7 @@ def plot_persistence_diagram( print("file " + persistence_file + " not found.") return None - if max_plots is not 1000: + if max_plots != 1000: print("Deprecated parameter. It has been replaced by max_intervals") max_intervals = max_plots @@ -247,6 +237,9 @@ def plot_persistence_diagram( reverse=True, )[:max_intervals] + if colormap == None: + colormap = plt.cm.Set1.colors + (min_birth, max_death) = __min_birth_max_death(persistence, band) delta = (max_death - min_birth) * inf_delta # Replace infinity values with max_death + delta for diagram to be more @@ -272,19 +265,19 @@ def plot_persistence_diagram( interval[1][0], interval[1][1], alpha=alpha, - color=palette[interval[0]], + color=colormap[interval[0]], ) else: # Infinite death case for diagram to be nicer plt.scatter( - interval[1][0], infinity, alpha=alpha, color=palette[interval[0]] + interval[1][0], infinity, alpha=alpha, color=colormap[interval[0]] ) if legend: dimensions = list(set(item[0] for item in persistence)) plt.legend( handles=[ - mpatches.Patch(color=palette[dim], label=str(dim)) + mpatches.Patch(color=colormap[dim], label=str(dim)) for dim in dimensions ] ) @@ -354,7 +347,7 @@ def plot_persistence_density( import matplotlib.pyplot as plt from scipy.stats import kde - if persistence_file is not "": + if persistence_file != "": if dimension is None: # All dimension case dimension = -1 -- cgit v1.2.3 From 87dc932aeb793b52a6009d39c7580c5f56133511 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Fri, 25 Oct 2019 17:28:25 +0200 Subject: Add token doc file so sphinx builds the doc. --- src/python/doc/sktda.rst | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/python/doc/sktda.rst (limited to 'src') diff --git a/src/python/doc/sktda.rst b/src/python/doc/sktda.rst new file mode 100644 index 00000000..d36d9ab4 --- /dev/null +++ b/src/python/doc/sktda.rst @@ -0,0 +1,35 @@ +:orphan: + +.. To get rid of WARNING: document isn't included in any toctree + +=================================== +sktda reference manual +=================================== + +Preprocessing +------------- +.. automodule:: gudhi.sktda.preprocessing + :members: + :undoc-members: + :show-inheritance: + +Vector methods +-------------- +.. automodule:: gudhi.sktda.vector_methods + :members: + :undoc-members: + :show-inheritance: + +Kernel methods +-------------- +.. automodule:: gudhi.sktda.kernel_methods + :members: + :undoc-members: + :show-inheritance: + +Metrics +------- +.. automodule:: gudhi.sktda.metrics + :members: + :undoc-members: + :show-inheritance: -- cgit v1.2.3 From 6609f46ac09e2fc08723177ad2bea1ce363aa4fa Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sun, 27 Oct 2019 09:00:49 +0100 Subject: Add interpreter to example --- src/python/example/ex_diagrams.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/python/example/ex_diagrams.py b/src/python/example/ex_diagrams.py index 3dc6fe84..a3efbac9 100644 --- a/src/python/example/ex_diagrams.py +++ b/src/python/example/ex_diagrams.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python + import matplotlib.pyplot as plt import numpy as np from sklearn.kernel_approximation import RBFSampler -- cgit v1.2.3 From c5eadabc2bd2df02ad80141b480492e59a88df4e Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sun, 27 Oct 2019 09:01:16 +0100 Subject: Make example executable --- src/python/example/ex_diagrams.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 src/python/example/ex_diagrams.py (limited to 'src') diff --git a/src/python/example/ex_diagrams.py b/src/python/example/ex_diagrams.py old mode 100644 new mode 100755 -- cgit v1.2.3 From 753dfe850f87bd1bb6bcb62741cf79697c4f2abd Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sun, 27 Oct 2019 10:17:43 +0100 Subject: Let cmake detect sklearn Not used yet, but it should protect the doc and future tests. --- src/cmake/modules/GUDHI_third_party_libraries.cmake | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/cmake/modules/GUDHI_third_party_libraries.cmake b/src/cmake/modules/GUDHI_third_party_libraries.cmake index 360a230b..d053e94f 100644 --- a/src/cmake/modules/GUDHI_third_party_libraries.cmake +++ b/src/cmake/modules/GUDHI_third_party_libraries.cmake @@ -125,6 +125,7 @@ if( PYTHONINTERP_FOUND ) find_python_module("numpy") find_python_module("scipy") find_python_module("sphinx") + find_python_module("sklearn") endif() if(NOT GUDHI_PYTHON_PATH) -- cgit v1.2.3 From d02296df9ca6ebea31345a89a6070258c0ed91a9 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sun, 27 Oct 2019 12:59:55 +0100 Subject: Mention POT in installation instructions --- src/python/doc/installation.rst | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src') diff --git a/src/python/doc/installation.rst b/src/python/doc/installation.rst index 5a6ad9f4..4778b3d9 100644 --- a/src/python/doc/installation.rst +++ b/src/python/doc/installation.rst @@ -215,6 +215,13 @@ The following examples require the `Matplotlib `_: * :download:`euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py <../example/euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py>` * :download:`euclidean_witness_complex_diagram_persistence_from_off_file_example.py <../example/euclidean_witness_complex_diagram_persistence_from_off_file_example.py>` +Python Optimal Transport +======================== + +The :doc:`Wasserstein distance ` +module requires `POT `_, a library that provides +several solvers for optimization problems related to Optimal Transport. + SciPy ===== -- cgit v1.2.3 From 3c76f73a530daacd48d476cd96bd946e4ab6d78a Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sun, 27 Oct 2019 13:40:23 +0100 Subject: Wasserstein also uses SciPy --- src/python/doc/installation.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/python/doc/installation.rst b/src/python/doc/installation.rst index 4778b3d9..3711ca8e 100644 --- a/src/python/doc/installation.rst +++ b/src/python/doc/installation.rst @@ -225,9 +225,10 @@ several solvers for optimization problems related to Optimal Transport. SciPy ===== -The :doc:`persistence graphical tools ` -module requires `SciPy `_, a Python-based ecosystem of -open-source software for mathematics, science, and engineering. +The :doc:`persistence graphical tools ` and +:doc:`Wasserstein distance ` modules require `SciPy +`_, a Python-based ecosystem of open-source software for +mathematics, science, and engineering. Threading Building Blocks ========================= -- cgit v1.2.3 From b23f5dca9a0805d655ee46fac49e5b971a2cf9cd Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sun, 27 Oct 2019 14:02:07 +0100 Subject: Print sklearn version next to the other modules --- src/python/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 26cd6ec1..2756d547 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -65,6 +65,9 @@ if(PYTHONINTERP_FOUND) if(SCIPY_FOUND) add_gudhi_debug_info("Scipy version ${SCIPY_VERSION}") endif() + if(SKLEARN_FOUND) + add_gudhi_debug_info("Scikit-learn version ${SKLEARN_VERSION}") + endif() set(GUDHI_PYTHON_EXTRA_COMPILE_ARGS "${GUDHI_PYTHON_EXTRA_COMPILE_ARGS}'-DBOOST_RESULT_OF_USE_DECLTYPE', ") set(GUDHI_PYTHON_EXTRA_COMPILE_ARGS "${GUDHI_PYTHON_EXTRA_COMPILE_ARGS}'-DBOOST_ALL_NO_LIB', ") -- cgit v1.2.3 From ee4934750e8c9dbdee4874d56921aeb9bf7b7bb7 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Thu, 31 Oct 2019 08:48:15 +0100 Subject: Increase numItermax in the call to POT. This number is pretty arbitrary... --- src/python/gudhi/wasserstein.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/python/gudhi/wasserstein.py b/src/python/gudhi/wasserstein.py index 445772e4..eba7c6d5 100644 --- a/src/python/gudhi/wasserstein.py +++ b/src/python/gudhi/wasserstein.py @@ -92,7 +92,8 @@ def wasserstein_distance(X, Y, p=2., q=2.): # Comptuation of the otcost using the ot.emd2 library. # Note: it is the squared Wasserstein distance. - ot_cost = (n+m) * ot.emd2(a, b, M) + # The default numItermax=100000 is not sufficient for some examples with 5000 points, what is a good value? + ot_cost = (n+m) * ot.emd2(a, b, M, numItermax=2000000) return ot_cost ** (1./p) -- cgit v1.2.3 From f6a78c1b7ea70b71fdb96f2fc17c44700e4b980a Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 31 Oct 2019 11:48:57 +0100 Subject: Add a get_point method for Alpha_complex_3d. Change Point_3 type to be either Weighted_point_3 or Bare_point_3 (new type). Add some get_point method tests. --- .../benchmark/Alpha_complex_3d_benchmark.cpp | 10 +-- .../example/Alpha_complex_3d_from_points.cpp | 2 +- .../Weighted_alpha_complex_3d_from_points.cpp | 12 ++-- src/Alpha_complex/include/gudhi/Alpha_complex_3d.h | 77 +++++++++++++++------- .../test/Alpha_complex_3d_unit_test.cpp | 53 ++++++++++++++- .../test/Periodic_alpha_complex_3d_unit_test.cpp | 37 +++++++---- .../test/Weighted_alpha_complex_3d_unit_test.cpp | 50 +++++++++----- ...eighted_periodic_alpha_complex_3d_unit_test.cpp | 24 +++---- .../utilities/alpha_complex_3d_persistence.cpp | 4 +- 9 files changed, 189 insertions(+), 80 deletions(-) (limited to 'src') diff --git a/src/Alpha_complex/benchmark/Alpha_complex_3d_benchmark.cpp b/src/Alpha_complex/benchmark/Alpha_complex_3d_benchmark.cpp index 005a712a..e84f5229 100644 --- a/src/Alpha_complex/benchmark/Alpha_complex_3d_benchmark.cpp +++ b/src/Alpha_complex/benchmark/Alpha_complex_3d_benchmark.cpp @@ -71,10 +71,10 @@ void benchmark_points_on_torus_3D(const std::string& msg) { for (int nb_points = 1000; nb_points <= 125000; nb_points *= 5) { std::cout << " Alpha complex 3d on torus with " << nb_points << " points." << std::endl; std::vector points_on_torus = Gudhi::generate_points_on_torus_3D(nb_points, 1.0, 0.5); - std::vector points; + std::vector points; for (auto p : points_on_torus) { - points.push_back(typename Alpha_complex_3d::Point_3(p[0], p[1], p[2])); + points.push_back(typename Alpha_complex_3d::Bare_point_3(p[0], p[1], p[2])); } Gudhi::Clock ac_create_clock(" benchmark_points_on_torus_3D - Alpha complex 3d creation"); @@ -115,7 +115,7 @@ void benchmark_weighted_points_on_torus_3D(const std::string& msg) { std::cout << " Alpha complex 3d on torus with " << nb_points << " points." << std::endl; std::vector points_on_torus = Gudhi::generate_points_on_torus_3D(nb_points, 1.0, 0.5); - using Point = typename Weighted_alpha_complex_3d::Point_3; + using Point = typename Weighted_alpha_complex_3d::Bare_point_3; using Weighted_point = typename Weighted_alpha_complex_3d::Weighted_point_3; std::vector points; @@ -158,7 +158,7 @@ void benchmark_periodic_points(const std::string& msg) { for (double nb_points = 10.; nb_points <= 40.; nb_points += 10.) { std::cout << " Periodic alpha complex 3d with " << nb_points * nb_points * nb_points << " points." << std::endl; - using Point = typename Periodic_alpha_complex_3d::Point_3; + using Point = typename Periodic_alpha_complex_3d::Bare_point_3; std::vector points; for (double i = 0; i < nb_points; i++) { @@ -206,7 +206,7 @@ void benchmark_weighted_periodic_points(const std::string& msg) { std::cout << " Weighted periodic alpha complex 3d with " << nb_points * nb_points * nb_points << " points." << std::endl; - using Point = typename Weighted_periodic_alpha_complex_3d::Point_3; + using Point = typename Weighted_periodic_alpha_complex_3d::Bare_point_3; using Weighted_point = typename Weighted_periodic_alpha_complex_3d::Weighted_point_3; std::vector points; diff --git a/src/Alpha_complex/example/Alpha_complex_3d_from_points.cpp b/src/Alpha_complex/example/Alpha_complex_3d_from_points.cpp index 0e359a27..7bd35cc6 100644 --- a/src/Alpha_complex/example/Alpha_complex_3d_from_points.cpp +++ b/src/Alpha_complex/example/Alpha_complex_3d_from_points.cpp @@ -8,7 +8,7 @@ #include // for numeric limits using Alpha_complex_3d = Gudhi::alpha_complex::Alpha_complex_3d; -using Point = Alpha_complex_3d::Point_3; +using Point = Alpha_complex_3d::Bare_point_3; using Vector_of_points = std::vector; int main(int argc, char **argv) { diff --git a/src/Alpha_complex/example/Weighted_alpha_complex_3d_from_points.cpp b/src/Alpha_complex/example/Weighted_alpha_complex_3d_from_points.cpp index ac11b68c..fcf80802 100644 --- a/src/Alpha_complex/example/Weighted_alpha_complex_3d_from_points.cpp +++ b/src/Alpha_complex/example/Weighted_alpha_complex_3d_from_points.cpp @@ -10,7 +10,7 @@ // Complexity = FAST, weighted = true, periodic = false using Weighted_alpha_complex_3d = Gudhi::alpha_complex::Alpha_complex_3d; -using Point = Weighted_alpha_complex_3d::Point_3; +using Bare_point = Weighted_alpha_complex_3d::Bare_point_3; using Weighted_point = Weighted_alpha_complex_3d::Weighted_point_3; int main(int argc, char **argv) { @@ -18,11 +18,11 @@ int main(int argc, char **argv) { // Init of a list of points and weights from a small molecule // ---------------------------------------------------------------------------- std::vector weighted_points; - weighted_points.push_back(Weighted_point(Point(1, -1, -1), 4.)); - weighted_points.push_back(Weighted_point(Point(-1, 1, -1), 4.)); - weighted_points.push_back(Weighted_point(Point(-1, -1, 1), 4.)); - weighted_points.push_back(Weighted_point(Point(1, 1, 1), 4.)); - weighted_points.push_back(Weighted_point(Point(2, 2, 2), 1.)); + weighted_points.push_back(Weighted_point(Bare_point(1, -1, -1), 4.)); + weighted_points.push_back(Weighted_point(Bare_point(-1, 1, -1), 4.)); + weighted_points.push_back(Weighted_point(Bare_point(-1, -1, 1), 4.)); + weighted_points.push_back(Weighted_point(Bare_point(1, 1, 1), 4.)); + weighted_points.push_back(Weighted_point(Bare_point(2, 2, 2), 1.)); // ---------------------------------------------------------------------------- // Init of an alpha complex from the list of points diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h b/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h index 13ebb9c1..c36bed1f 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h @@ -43,7 +43,7 @@ #include #include #include -#include +#include // for std::size_t #include // for std::unique_ptr #include // for std::conditional and std::enable_if #include // for numeric_limits<> @@ -225,23 +225,23 @@ class Alpha_complex_3d { * Must be compatible with double. */ using FT = typename Alpha_shape_3::FT; - /** \brief Gives public access to the Point_3 type. Here is a Point_3 constructor example: + /** \brief Gives public access to the Bare_point_3 (bare aka. unweighed) type. + * Here is a Bare_point_3 constructor example: \code{.cpp} using Alpha_complex_3d = Gudhi::alpha_complex::Alpha_complex_3d; // x0 = 1., y0 = -1.1, z0 = -1.. -Alpha_complex_3d::Point_3 p0(1., -1.1, -1.); +Alpha_complex_3d::Bare_point_3 p0(1., -1.1, -1.); \endcode * */ - using Point_3 = typename Kernel::Point_3; + using Bare_point_3 = typename Kernel::Point_3; /** \brief Gives public access to the Weighted_point_3 type. A Weighted point can be constructed as follows: \code{.cpp} -using Weighted_alpha_complex_3d = - Gudhi::alpha_complex::Alpha_complex_3d; +using Weighted_alpha_complex_3d = Gudhi::alpha_complex::Alpha_complex_3d; // x0 = 1., y0 = -1.1, z0 = -1., weight = 4. -Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point_3(1., -1.1, -1.), 4.); +Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Bare_point_3(1., -1.1, -1.), 4.); \endcode * * Note: This type is defined to void if Alpha complex is not weighted. @@ -249,6 +249,11 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point * */ using Weighted_point_3 = typename Triangulation_3::Weighted_point_3; + /** \brief `Alpha_complex_3d::Point_3` type is either a `Alpha_complex_3d::Bare_point_3` (Weighted = false) or a + * `Alpha_complex_3d::Weighted_point_3` (Weighted = true). + */ + using Point_3 = typename std::conditional::type; + private: using Dispatch = CGAL::Dispatch_output_iterator, @@ -264,13 +269,13 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point public: /** \brief Alpha_complex constructor from a list of points. * - * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Point_3` or + * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Bare_point_3` or * `Alpha_complex_3d::Weighted_point_3`. * * @pre Available if Alpha_complex_3d is not Periodic. * * The type InputPointRange must be a range for which std::begin and std::end return input iterators on a - * `Alpha_complex_3d::Point_3` or a `Alpha_complex_3d::Weighted_point_3`. + * `Alpha_complex_3d::Bare_point_3` or a `Alpha_complex_3d::Weighted_point_3`. */ template Alpha_complex_3d(const InputPointRange& points) { @@ -284,13 +289,13 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point * * @exception std::invalid_argument In debug mode, if points and weights do not have the same size. * - * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Point_3`. + * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Bare_point_3`. * @param[in] weights Range of weights on points. Weights shall be in double. * * @pre Available if Alpha_complex_3d is Weighted and not Periodic. * * The type InputPointRange must be a range for which std::begin and - * std::end return input iterators on a `Alpha_complex_3d::Point_3`. + * std::end return input iterators on a `Alpha_complex_3d::Bare_point_3`. * The type WeightRange must be a range for which std::begin and * std::end return an input iterator on a double. */ @@ -318,7 +323,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point * * @exception std::invalid_argument In debug mode, if the size of the cuboid in every directions is not the same. * - * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Point_3` or + * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Bare_point_3` or * `Alpha_complex_3d::Weighted_point_3`. * @param[in] x_min Iso-oriented cuboid x_min. * @param[in] y_min Iso-oriented cuboid y_min. @@ -330,7 +335,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point * @pre Available if Alpha_complex_3d is Periodic. * * The type InputPointRange must be a range for which std::begin and std::end return input iterators on a - * `Alpha_complex_3d::Point_3` or a `Alpha_complex_3d::Weighted_point_3`. + * `Alpha_complex_3d::Bare_point_3` or a `Alpha_complex_3d::Weighted_point_3`. * * @note In weighted version, please check weights are greater than zero, and lower than 1/64*cuboid length * squared. @@ -366,7 +371,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point * @exception std::invalid_argument In debug mode, if a weight is negative, zero, or greater than 1/64*cuboid length * squared. * - * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Point_3`. + * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Bare_point_3`. * @param[in] weights Range of weights on points. Weights shall be in double. * @param[in] x_min Iso-oriented cuboid x_min. * @param[in] y_min Iso-oriented cuboid y_min. @@ -378,7 +383,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point * @pre Available if Alpha_complex_3d is Weighted and Periodic. * * The type InputPointRange must be a range for which std::begin and - * std::end return input iterators on a `Alpha_complex_3d::Point_3`. + * std::end return input iterators on a `Alpha_complex_3d::Bare_point_3`. * The type WeightRange must be a range for which std::begin and * std::end return an input iterator on a double. * The type of x_min, y_min, z_min, x_max, y_max and z_max must be a double. @@ -452,9 +457,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point return false; // ----- >> } - // using Filtration_value = typename SimplicialComplexForAlpha3d::Filtration_value; using Complex_vertex_handle = typename SimplicialComplexForAlpha3d::Vertex_handle; - using Alpha_shape_simplex_tree_map = std::unordered_map; using Simplex_tree_vector_vertex = std::vector; #ifdef DEBUG_TRACES @@ -474,7 +477,6 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point std::cout << "filtration_with_alpha_values returns : " << objects.size() << " objects" << std::endl; #endif // DEBUG_TRACES - Alpha_shape_simplex_tree_map map_cgal_simplex_tree; using Alpha_value_iterator = typename std::vector::const_iterator; Alpha_value_iterator alpha_value_iterator = alpha_values.begin(); for (auto object_iterator : objects) { @@ -484,7 +486,8 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point if (const Cell_handle* cell = CGAL::object_cast(&object_iterator)) { for (auto i = 0; i < 4; i++) { #ifdef DEBUG_TRACES - std::cout << "from cell[" << i << "]=" << (*cell)->vertex(i)->point() << std::endl; + std::cout << "from cell[" << i << "] - Point coordinates (" << (*cell)->vertex(i)->point() << ")" + << std::endl; #endif // DEBUG_TRACES vertex_list.push_back((*cell)->vertex(i)); } @@ -495,7 +498,8 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point for (auto i = 0; i < 4; i++) { if ((*facet).second != i) { #ifdef DEBUG_TRACES - std::cout << "from facet=[" << i << "]" << (*facet).first->vertex(i)->point() << std::endl; + std::cout << "from facet=[" << i << "] - Point coordinates (" << (*facet).first->vertex(i)->point() << ")" + << std::endl; #endif // DEBUG_TRACES vertex_list.push_back((*facet).first->vertex(i)); } @@ -506,7 +510,8 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point } else if (const Edge* edge = CGAL::object_cast(&object_iterator)) { for (auto i : {(*edge).second, (*edge).third}) { #ifdef DEBUG_TRACES - std::cout << "from edge[" << i << "]=" << (*edge).first->vertex(i)->point() << std::endl; + std::cout << "from edge[" << i << "] - Point coordinates (" << (*edge).first->vertex(i)->point() << ")" + << std::endl; #endif // DEBUG_TRACES vertex_list.push_back((*edge).first->vertex(i)); } @@ -516,7 +521,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point } else if (const Alpha_vertex_handle* vertex = CGAL::object_cast(&object_iterator)) { #ifdef DEBUG_TRACES count_vertices++; - std::cout << "from vertex=" << (*vertex)->point() << std::endl; + std::cout << "from vertex - Point coordinates (" << (*vertex)->point() << ")" << std::endl; #endif // DEBUG_TRACES vertex_list.push_back((*vertex)); } @@ -528,7 +533,8 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point // alpha shape not found Complex_vertex_handle vertex = map_cgal_simplex_tree.size(); #ifdef DEBUG_TRACES - std::cout << "vertex [" << the_alpha_shape_vertex->point() << "] not found - insert " << vertex << std::endl; + std::cout << "Point (" << the_alpha_shape_vertex->point() << ") not found - insert new vertex id " << vertex + << std::endl; #endif // DEBUG_TRACES the_simplex.push_back(vertex); map_cgal_simplex_tree.emplace(the_alpha_shape_vertex, vertex); @@ -536,7 +542,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point // alpha shape found Complex_vertex_handle vertex = the_map_iterator->second; #ifdef DEBUG_TRACES - std::cout << "vertex [" << the_alpha_shape_vertex->point() << "] found in " << vertex << std::endl; + std::cout << "Point (" << the_alpha_shape_vertex->point() << ") found as vertex id " << vertex << std::endl; #endif // DEBUG_TRACES the_simplex.push_back(vertex); } @@ -567,9 +573,32 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point return true; } + /** \brief get_point returns the point corresponding to the vertex given as parameter. + * + * @param[in] vertex Vertex handle of the point to retrieve. + * @return The point found. + * @exception std::out_of_range In case vertex is not found (cf. std::vector::at). + */ + const Point_3& get_point(std::size_t vertex) { + if (map_cgal_simplex_tree.size() != cgal_vertex_iterator_vector.size()) { + cgal_vertex_iterator_vector.resize(map_cgal_simplex_tree.size()); + for (auto map_iterator = map_cgal_simplex_tree.begin(); map_iterator != map_cgal_simplex_tree.end(); map_iterator++) { + cgal_vertex_iterator_vector[map_iterator->second] = map_iterator->first; + } + + } + auto cgal_vertex_iterator = cgal_vertex_iterator_vector.at(vertex); + return cgal_vertex_iterator->point(); + } + private: // use of a unique_ptr on cgal Alpha_shape_3, as copy and default constructor is not available - no need to be freed std::unique_ptr alpha_shape_3_ptr_; + + // Map type to switch from CGAL vertex iterator to simplex tree vertex handle. + std::unordered_map map_cgal_simplex_tree; + // Vector type to switch from simplex tree vertex handle to CGAL vertex iterator. + std::vector cgal_vertex_iterator_vector; }; } // namespace alpha_complex diff --git a/src/Alpha_complex/test/Alpha_complex_3d_unit_test.cpp b/src/Alpha_complex/test/Alpha_complex_3d_unit_test.cpp index 1102838a..cd698a27 100644 --- a/src/Alpha_complex/test/Alpha_complex_3d_unit_test.cpp +++ b/src/Alpha_complex/test/Alpha_complex_3d_unit_test.cpp @@ -56,21 +56,52 @@ BOOST_AUTO_TEST_CASE(Alpha_complex_3d_from_points) { // ----------------- std::cout << "Fast alpha complex 3d" << std::endl; - Fast_alpha_complex_3d alpha_complex(get_points()); + std::vector points = get_points(); + Fast_alpha_complex_3d alpha_complex(points); Gudhi::Simplex_tree<> stree; alpha_complex.create_complex(stree); + for (std::size_t index = 0; index < points.size(); index++) { + bool found = false; + for (auto point : points) { + if (point == alpha_complex.get_point(index)) { + found = true; + break; + } + } + // Check all points from alpha complex are found in the input point cloud + BOOST_CHECK(found); + } + // Exception if we go out of range + BOOST_CHECK_THROW(alpha_complex.get_point(points.size()), std::out_of_range); + // ----------------- // Exact version // ----------------- std::cout << "Exact alpha complex 3d" << std::endl; - Exact_alpha_complex_3d exact_alpha_complex(get_points()); + std::vector exact_points = get_points(); + Exact_alpha_complex_3d exact_alpha_complex(exact_points); Gudhi::Simplex_tree<> exact_stree; exact_alpha_complex.create_complex(exact_stree); + for (std::size_t index = 0; index < exact_points.size(); index++) { + bool found = false; + Exact_alpha_complex_3d::Bare_point_3 ap = exact_alpha_complex.get_point(index); + for (auto point : points) { + if ((point.x() == ap.x()) && (point.y() == ap.y()) && (point.z() == ap.z())) { + found = true; + break; + } + } + // Check all points from alpha complex are found in the input point cloud + BOOST_CHECK(found); + } + // Exception if we go out of range + BOOST_CHECK_THROW(exact_alpha_complex.get_point(exact_points.size()), std::out_of_range); + // --------------------- // Compare both versions // --------------------- @@ -110,11 +141,27 @@ BOOST_AUTO_TEST_CASE(Alpha_complex_3d_from_points) { // ----------------- std::cout << "Safe alpha complex 3d" << std::endl; - Safe_alpha_complex_3d safe_alpha_complex(get_points()); + std::vector safe_points = get_points(); + Safe_alpha_complex_3d safe_alpha_complex(safe_points); Gudhi::Simplex_tree<> safe_stree; safe_alpha_complex.create_complex(safe_stree); + for (std::size_t index = 0; index < safe_points.size(); index++) { + bool found = false; + Safe_alpha_complex_3d::Bare_point_3 ap = safe_alpha_complex.get_point(index); + for (auto point : points) { + if ((point.x() == ap.x()) && (point.y() == ap.y()) && (point.z() == ap.z())) { + found = true; + break; + } + } + // Check all points from alpha complex are found in the input point cloud + BOOST_CHECK(found); + } + // Exception if we go out of range + BOOST_CHECK_THROW(safe_alpha_complex.get_point(safe_points.size()), std::out_of_range); + // --------------------- // Compare both versions // --------------------- diff --git a/src/Alpha_complex/test/Periodic_alpha_complex_3d_unit_test.cpp b/src/Alpha_complex/test/Periodic_alpha_complex_3d_unit_test.cpp index ac3791a4..731763fa 100644 --- a/src/Alpha_complex/test/Periodic_alpha_complex_3d_unit_test.cpp +++ b/src/Alpha_complex/test/Periodic_alpha_complex_3d_unit_test.cpp @@ -44,11 +44,11 @@ typedef boost::mpl::list p_points; + using Bare_point_3 = typename Periodic_alpha_complex_3d::Bare_point_3; + std::vector p_points; // Not important, this is not what we want to check - p_points.push_back(Point_3(0.0, 0.0, 0.0)); + p_points.push_back(Bare_point_3(0.0, 0.0, 0.0)); std::cout << "Check exception throw in debug mode" << std::endl; // Check it throws an exception when the cuboid is not iso @@ -73,13 +73,13 @@ BOOST_AUTO_TEST_CASE(Alpha_complex_periodic) { // --------------------- std::cout << "Fast periodic alpha complex 3d" << std::endl; - using Creator = CGAL::Creator_uniform_3; + using Creator = CGAL::Creator_uniform_3; CGAL::Random random(7); - CGAL::Random_points_in_cube_3 in_cube(1, random); - std::vector p_points; + CGAL::Random_points_in_cube_3 in_cube(1, random); + std::vector p_points; for (int i = 0; i < 50; i++) { - Fast_periodic_alpha_complex_3d::Point_3 p = *in_cube++; + Fast_periodic_alpha_complex_3d::Bare_point_3 p = *in_cube++; p_points.push_back(p); } @@ -88,15 +88,30 @@ BOOST_AUTO_TEST_CASE(Alpha_complex_periodic) { Gudhi::Simplex_tree<> stree; periodic_alpha_complex.create_complex(stree); + for (std::size_t index = 0; index < p_points.size(); index++) { + bool found = false; + Fast_periodic_alpha_complex_3d::Bare_point_3 ap = periodic_alpha_complex.get_point(index); + for (auto point : p_points) { + if ((point.x() == ap.x()) && (point.y() == ap.y()) && (point.z() == ap.z())) { + found = true; + break; + } + } + // Check all points from alpha complex are found in the input point cloud + BOOST_CHECK(found); + } + // Exception if we go out of range + BOOST_CHECK_THROW(periodic_alpha_complex.get_point(p_points.size()), std::out_of_range); + // ---------------------- // Exact periodic version // ---------------------- std::cout << "Exact periodic alpha complex 3d" << std::endl; - std::vector e_p_points; + std::vector e_p_points; for (auto p : p_points) { - e_p_points.push_back(Exact_periodic_alpha_complex_3d::Point_3(p[0], p[1], p[2])); + e_p_points.push_back(Exact_periodic_alpha_complex_3d::Bare_point_3(p[0], p[1], p[2])); } Exact_periodic_alpha_complex_3d exact_alpha_complex(e_p_points, -1., -1., -1., 1., 1., 1.); @@ -142,10 +157,10 @@ BOOST_AUTO_TEST_CASE(Alpha_complex_periodic) { // ---------------------- std::cout << "Safe periodic alpha complex 3d" << std::endl; - std::vector s_p_points; + std::vector s_p_points; for (auto p : p_points) { - s_p_points.push_back(Safe_periodic_alpha_complex_3d::Point_3(p[0], p[1], p[2])); + s_p_points.push_back(Safe_periodic_alpha_complex_3d::Bare_point_3(p[0], p[1], p[2])); } Safe_periodic_alpha_complex_3d safe_alpha_complex(s_p_points, -1., -1., -1., 1., 1., 1.); diff --git a/src/Alpha_complex/test/Weighted_alpha_complex_3d_unit_test.cpp b/src/Alpha_complex/test/Weighted_alpha_complex_3d_unit_test.cpp index 44deb930..8035f6e8 100644 --- a/src/Alpha_complex/test/Weighted_alpha_complex_3d_unit_test.cpp +++ b/src/Alpha_complex/test/Weighted_alpha_complex_3d_unit_test.cpp @@ -43,14 +43,14 @@ typedef boost::mpl::list w_points; - w_points.push_back(Point_3(0.0, 0.0, 0.0)); - w_points.push_back(Point_3(0.0, 0.0, 0.2)); - w_points.push_back(Point_3(0.2, 0.0, 0.2)); - // w_points.push_back(Point_3(0.6, 0.6, 0.0)); - // w_points.push_back(Point_3(0.8, 0.8, 0.2)); - // w_points.push_back(Point_3(0.2, 0.8, 0.6)); + using Bare_point_3 = typename Weighted_alpha_complex_3d::Bare_point_3; + std::vector w_points; + w_points.push_back(Bare_point_3(0.0, 0.0, 0.0)); + w_points.push_back(Bare_point_3(0.0, 0.0, 0.2)); + w_points.push_back(Bare_point_3(0.2, 0.0, 0.2)); + // w_points.push_back(Bare_point_3(0.6, 0.6, 0.0)); + // w_points.push_back(Bare_point_3(0.8, 0.8, 0.2)); + // w_points.push_back(Bare_point_3(0.2, 0.8, 0.6)); // weights size is different from w_points size to make weighted Alpha_complex_3d throw in debug mode std::vector weights = {0.01, 0.005, 0.006, 0.01, 0.009, 0.001}; @@ -62,14 +62,14 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(Alpha_complex_weighted_throw, Weighted_alpha_compl BOOST_AUTO_TEST_CASE_TEMPLATE(Alpha_complex_weighted, Weighted_alpha_complex_3d, weighted_variants_type_list) { std::cout << "Weighted alpha complex 3d from points and weights" << std::endl; - using Point_3 = typename Weighted_alpha_complex_3d::Point_3; - std::vector w_points; - w_points.push_back(Point_3(0.0, 0.0, 0.0)); - w_points.push_back(Point_3(0.0, 0.0, 0.2)); - w_points.push_back(Point_3(0.2, 0.0, 0.2)); - w_points.push_back(Point_3(0.6, 0.6, 0.0)); - w_points.push_back(Point_3(0.8, 0.8, 0.2)); - w_points.push_back(Point_3(0.2, 0.8, 0.6)); + using Bare_point_3 = typename Weighted_alpha_complex_3d::Bare_point_3; + std::vector w_points; + w_points.push_back(Bare_point_3(0.0, 0.0, 0.0)); + w_points.push_back(Bare_point_3(0.0, 0.0, 0.2)); + w_points.push_back(Bare_point_3(0.2, 0.0, 0.2)); + w_points.push_back(Bare_point_3(0.6, 0.6, 0.0)); + w_points.push_back(Bare_point_3(0.8, 0.8, 0.2)); + w_points.push_back(Bare_point_3(0.2, 0.8, 0.6)); // weights size is different from w_points size to make weighted Alpha_complex_3d throw in debug mode std::vector weights = {0.01, 0.005, 0.006, 0.01, 0.009, 0.001}; @@ -91,6 +91,24 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(Alpha_complex_weighted, Weighted_alpha_complex_3d, Gudhi::Simplex_tree<> stree_bis; alpha_complex_w_p.create_complex(stree_bis); + for (std::size_t index = 0; index < weighted_points.size(); index++) { + bool found = false; + Weighted_point_3 awp = alpha_complex_w_p.get_point(index); + for (auto weighted_point : weighted_points) { + if ((weighted_point.weight() == awp.weight()) && + (weighted_point.x() == awp.x()) && + (weighted_point.y() == awp.y()) && + (weighted_point.z() == awp.z())) { + found = true; + break; + } + } + // Check all points from alpha complex are found in the input point cloud + BOOST_CHECK(found); + } + // Exception if we go out of range + BOOST_CHECK_THROW(alpha_complex_w_p.get_point(weighted_points.size()), std::out_of_range); + // --------------------- // Compare both versions // --------------------- diff --git a/src/Alpha_complex/test/Weighted_periodic_alpha_complex_3d_unit_test.cpp b/src/Alpha_complex/test/Weighted_periodic_alpha_complex_3d_unit_test.cpp index 670c7799..b09e92d5 100644 --- a/src/Alpha_complex/test/Weighted_periodic_alpha_complex_3d_unit_test.cpp +++ b/src/Alpha_complex/test/Weighted_periodic_alpha_complex_3d_unit_test.cpp @@ -47,13 +47,13 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(Alpha_complex_weighted_periodic_throw, Weighted_pe wp_variants_type_list) { std::cout << "Weighted periodic alpha complex 3d exception throw" << std::endl; - using Creator = CGAL::Creator_uniform_3; + using Creator = CGAL::Creator_uniform_3; CGAL::Random random(7); - CGAL::Random_points_in_cube_3 in_cube(1, random); - std::vector wp_points; + CGAL::Random_points_in_cube_3 in_cube(1, random); + std::vector wp_points; for (int i = 0; i < 50; i++) { - typename Weighted_periodic_alpha_complex_3d::Point_3 p = *in_cube++; + typename Weighted_periodic_alpha_complex_3d::Bare_point_3 p = *in_cube++; wp_points.push_back(p); } std::vector p_weights; @@ -117,13 +117,13 @@ BOOST_AUTO_TEST_CASE(Alpha_complex_weighted_periodic) { // --------------------- std::cout << "Fast weighted periodic alpha complex 3d" << std::endl; - using Creator = CGAL::Creator_uniform_3; + using Creator = CGAL::Creator_uniform_3; CGAL::Random random(7); - CGAL::Random_points_in_cube_3 in_cube(1, random); - std::vector p_points; + CGAL::Random_points_in_cube_3 in_cube(1, random); + std::vector p_points; for (int i = 0; i < 50; i++) { - Fast_weighted_periodic_alpha_complex_3d::Point_3 p = *in_cube++; + Fast_weighted_periodic_alpha_complex_3d::Bare_point_3 p = *in_cube++; p_points.push_back(p); } std::vector p_weights; @@ -142,10 +142,10 @@ BOOST_AUTO_TEST_CASE(Alpha_complex_weighted_periodic) { // ---------------------- std::cout << "Exact weighted periodic alpha complex 3d" << std::endl; - std::vector e_p_points; + std::vector e_p_points; for (auto p : p_points) { - e_p_points.push_back(Exact_weighted_periodic_alpha_complex_3d::Point_3(p[0], p[1], p[2])); + e_p_points.push_back(Exact_weighted_periodic_alpha_complex_3d::Bare_point_3(p[0], p[1], p[2])); } Exact_weighted_periodic_alpha_complex_3d exact_alpha_complex(e_p_points, p_weights, -1., -1., -1., 1., 1., 1.); @@ -191,10 +191,10 @@ BOOST_AUTO_TEST_CASE(Alpha_complex_weighted_periodic) { // ---------------------- std::cout << "Safe weighted periodic alpha complex 3d" << std::endl; - std::vector s_p_points; + std::vector s_p_points; for (auto p : p_points) { - s_p_points.push_back(Safe_weighted_periodic_alpha_complex_3d::Point_3(p[0], p[1], p[2])); + s_p_points.push_back(Safe_weighted_periodic_alpha_complex_3d::Bare_point_3(p[0], p[1], p[2])); } Safe_weighted_periodic_alpha_complex_3d safe_alpha_complex(s_p_points, p_weights, -1., -1., -1., 1., 1., 1.); diff --git a/src/Alpha_complex/utilities/alpha_complex_3d_persistence.cpp b/src/Alpha_complex/utilities/alpha_complex_3d_persistence.cpp index 2272576e..929fc2e8 100644 --- a/src/Alpha_complex/utilities/alpha_complex_3d_persistence.cpp +++ b/src/Alpha_complex/utilities/alpha_complex_3d_persistence.cpp @@ -62,9 +62,9 @@ bool read_cuboid_file(const std::string &cuboid_file, double &x_min, double &y_m } template -std::vector read_off(const std::string &off_file_points) { +std::vector read_off(const std::string &off_file_points) { // Read the OFF file (input file name given as parameter) and triangulate points - Gudhi::Points_3D_off_reader off_reader(off_file_points); + Gudhi::Points_3D_off_reader off_reader(off_file_points); // Check the read operation was correct if (!off_reader.is_valid()) { std::cerr << "Unable to read OFF file " << off_file_points << std::endl; -- cgit v1.2.3 From 1e42426b292f4bdc40044eedda59bbb51e2ca851 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 31 Oct 2019 15:52:05 +0100 Subject: Fix #98 : set conda link to the correct one (not licensing link) --- src/python/doc/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/python/doc/installation.rst b/src/python/doc/installation.rst index 77d9e8b3..5369efb0 100644 --- a/src/python/doc/installation.rst +++ b/src/python/doc/installation.rst @@ -8,7 +8,7 @@ Installation Conda ***** The easiest way to install the Python version of GUDHI is using -`conda `_. +`conda `_. Compiling ********* -- cgit v1.2.3 From 3e18343985f7d4439fb6acb879547e8fa11369f9 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Fri, 1 Nov 2019 14:52:44 +0100 Subject: Use documented CGAL interfaces for Epick_d (or Epeck_d) --- src/python/include/Alpha_complex_interface.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/python/include/Alpha_complex_interface.h b/src/python/include/Alpha_complex_interface.h index b3553d32..96353cc4 100644 --- a/src/python/include/Alpha_complex_interface.h +++ b/src/python/include/Alpha_complex_interface.h @@ -15,6 +15,8 @@ #include #include +#include + #include "Simplex_tree_interface.h" #include @@ -31,7 +33,10 @@ class Alpha_complex_interface { public: Alpha_complex_interface(const std::vector>& points) { - alpha_complex_ = new Alpha_complex(points); + auto mkpt = [](std::vector const& vec){ + return Point_d(vec.size(), vec.begin(), vec.end()); + }; + alpha_complex_ = new Alpha_complex(boost::adaptors::transform(points, mkpt)); } Alpha_complex_interface(const std::string& off_file_name, bool from_file = true) { @@ -45,9 +50,9 @@ class Alpha_complex_interface { std::vector get_point(int vh) { std::vector vd; try { - Point_d ph = alpha_complex_->get_point(vh); + Point_d const& ph = alpha_complex_->get_point(vh); for (auto coord = ph.cartesian_begin(); coord < ph.cartesian_end(); coord++) - vd.push_back(*coord); + vd.push_back(CGAL::to_double(*coord)); } catch (std::out_of_range const&) { // std::out_of_range is thrown in case not found. Other exceptions must be re-thrown } -- cgit v1.2.3 From f670d1e58ff94fd724c41ff34871b57f6ac95cc0 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 4 Nov 2019 10:51:28 +0100 Subject: Use Point_3 instead of Bare_point_3 in non-weighted cases --- src/Alpha_complex/benchmark/Alpha_complex_3d_benchmark.cpp | 6 +++--- src/Alpha_complex/example/Alpha_complex_3d_from_points.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/Alpha_complex/benchmark/Alpha_complex_3d_benchmark.cpp b/src/Alpha_complex/benchmark/Alpha_complex_3d_benchmark.cpp index e84f5229..99ad94b9 100644 --- a/src/Alpha_complex/benchmark/Alpha_complex_3d_benchmark.cpp +++ b/src/Alpha_complex/benchmark/Alpha_complex_3d_benchmark.cpp @@ -71,10 +71,10 @@ void benchmark_points_on_torus_3D(const std::string& msg) { for (int nb_points = 1000; nb_points <= 125000; nb_points *= 5) { std::cout << " Alpha complex 3d on torus with " << nb_points << " points." << std::endl; std::vector points_on_torus = Gudhi::generate_points_on_torus_3D(nb_points, 1.0, 0.5); - std::vector points; + std::vector points; for (auto p : points_on_torus) { - points.push_back(typename Alpha_complex_3d::Bare_point_3(p[0], p[1], p[2])); + points.push_back(typename Alpha_complex_3d::Point_3(p[0], p[1], p[2])); } Gudhi::Clock ac_create_clock(" benchmark_points_on_torus_3D - Alpha complex 3d creation"); @@ -158,7 +158,7 @@ void benchmark_periodic_points(const std::string& msg) { for (double nb_points = 10.; nb_points <= 40.; nb_points += 10.) { std::cout << " Periodic alpha complex 3d with " << nb_points * nb_points * nb_points << " points." << std::endl; - using Point = typename Periodic_alpha_complex_3d::Bare_point_3; + using Point = typename Periodic_alpha_complex_3d::Point_3; std::vector points; for (double i = 0; i < nb_points; i++) { diff --git a/src/Alpha_complex/example/Alpha_complex_3d_from_points.cpp b/src/Alpha_complex/example/Alpha_complex_3d_from_points.cpp index 7bd35cc6..0e359a27 100644 --- a/src/Alpha_complex/example/Alpha_complex_3d_from_points.cpp +++ b/src/Alpha_complex/example/Alpha_complex_3d_from_points.cpp @@ -8,7 +8,7 @@ #include // for numeric limits using Alpha_complex_3d = Gudhi::alpha_complex::Alpha_complex_3d; -using Point = Alpha_complex_3d::Bare_point_3; +using Point = Alpha_complex_3d::Point_3; using Vector_of_points = std::vector; int main(int argc, char **argv) { -- cgit v1.2.3 From c7d99937affeeac7ef1de05baa42d0392c8d5dc3 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 4 Nov 2019 14:33:50 +0100 Subject: Clarification : Points must be in Bare_point_3 or in Weighted_point_3 *means* points must be in Point_3 --- src/Alpha_complex/include/gudhi/Alpha_complex_3d.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h b/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h index c36bed1f..652a40a4 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h @@ -269,13 +269,12 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Bare_ public: /** \brief Alpha_complex constructor from a list of points. * - * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Bare_point_3` or - * `Alpha_complex_3d::Weighted_point_3`. + * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Point_3`. * * @pre Available if Alpha_complex_3d is not Periodic. * * The type InputPointRange must be a range for which std::begin and std::end return input iterators on a - * `Alpha_complex_3d::Bare_point_3` or a `Alpha_complex_3d::Weighted_point_3`. + * `Alpha_complex_3d::Point_3`. */ template Alpha_complex_3d(const InputPointRange& points) { @@ -323,8 +322,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Bare_ * * @exception std::invalid_argument In debug mode, if the size of the cuboid in every directions is not the same. * - * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Bare_point_3` or - * `Alpha_complex_3d::Weighted_point_3`. + * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Point_3`. * @param[in] x_min Iso-oriented cuboid x_min. * @param[in] y_min Iso-oriented cuboid y_min. * @param[in] z_min Iso-oriented cuboid z_min. @@ -335,7 +333,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Bare_ * @pre Available if Alpha_complex_3d is Periodic. * * The type InputPointRange must be a range for which std::begin and std::end return input iterators on a - * `Alpha_complex_3d::Bare_point_3` or a `Alpha_complex_3d::Weighted_point_3`. + * `Alpha_complex_3d::Point_3`. * * @note In weighted version, please check weights are greater than zero, and lower than 1/64*cuboid length * squared. -- cgit v1.2.3 From b3043e49f09551187e848e0aacf88d1cf5e271e4 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 4 Nov 2019 15:09:28 +0100 Subject: Use range-for loop --- src/Alpha_complex/include/gudhi/Alpha_complex_3d.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h b/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h index 652a40a4..cb9cb926 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h @@ -580,8 +580,8 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Bare_ const Point_3& get_point(std::size_t vertex) { if (map_cgal_simplex_tree.size() != cgal_vertex_iterator_vector.size()) { cgal_vertex_iterator_vector.resize(map_cgal_simplex_tree.size()); - for (auto map_iterator = map_cgal_simplex_tree.begin(); map_iterator != map_cgal_simplex_tree.end(); map_iterator++) { - cgal_vertex_iterator_vector[map_iterator->second] = map_iterator->first; + for (auto map_iterator : map_cgal_simplex_tree) { + cgal_vertex_iterator_vector[map_iterator.second] = map_iterator.first; } } -- cgit v1.2.3 From 7eb50adc342a5d2757cd209b3a8f2f297a4ad2fe Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Mon, 4 Nov 2019 11:27:19 -0500 Subject: fixes of Marc's comments --- src/python/example/ex_diagrams.py | 57 +++++++++++++++++++++++--------- src/python/gudhi/sktda/kernel_methods.py | 8 ++--- src/python/gudhi/sktda/metrics.py | 2 +- src/python/gudhi/sktda/preprocessing.py | 16 +++++---- src/python/gudhi/sktda/vector_methods.py | 8 ++--- 5 files changed, 59 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/src/python/example/ex_diagrams.py b/src/python/example/ex_diagrams.py index a3efbac9..f12304bd 100755 --- a/src/python/example/ex_diagrams.py +++ b/src/python/example/ex_diagrams.py @@ -3,25 +3,33 @@ import matplotlib.pyplot as plt import numpy as np from sklearn.kernel_approximation import RBFSampler +from sklearn.preprocessing import MinMaxScaler -from gudhi.sktda import Landscape, Silhouette, BettiCurve, ComplexPolynomial,\ +from gudhi.sktda import DiagramSelector, Clamping, Landscape, Silhouette, BettiCurve, ComplexPolynomial,\ TopologicalVector, DiagramScaler, BirthPersistenceTransform,\ - PersistenceImage, PersistenceWeightedGaussianKernel,\ + PersistenceImage, PersistenceWeightedGaussianKernel, Entropy, \ PersistenceScaleSpaceKernel, SlicedWassersteinDistance,\ SlicedWassersteinKernel, BottleneckDistance, PersistenceFisherKernel -D = np.array([[0.,4.],[1.,2.],[3.,8.],[6.,8.]]) +D = np.array([[0.,4.],[1.,2.],[3.,8.],[6.,8.], [0., np.inf], [5., np.inf]]) +diags = [D] + +diags = DiagramSelector(use=True, point_type="finite").fit_transform(diags) +diags = DiagramScaler(use=True, scalers=[([0,1], MinMaxScaler())]).fit_transform(diags) +diags = DiagramScaler(use=True, scalers=[([1], Clamping(limit=.9))]).fit_transform(diags) + +D = diags[0] plt.scatter(D[:,0],D[:,1]) -plt.plot([0.,10.],[0.,10.]) +plt.plot([0.,1.],[0.,1.]) +plt.title("Test Persistence Diagram for vector methods") plt.show() -diags = [D] - -LS = Landscape(resolution = 1000) +LS = Landscape(resolution=1000) L = LS.fit_transform(diags) plt.plot(L[0][:1000]) plt.plot(L[0][1000:2000]) plt.plot(L[0][2000:3000]) +plt.title("Landscape") plt.show() def pow(n): @@ -30,11 +38,13 @@ def pow(n): SH = Silhouette(resolution=1000, weight=pow(2)) sh = SH.fit_transform(diags) plt.plot(sh[0]) +plt.title("Silhouette") plt.show() BC = BettiCurve(resolution=1000) bc = BC.fit_transform(diags) plt.plot(bc[0]) +plt.title("Betti Curve") plt.show() CP = ComplexPolynomial(threshold=-1, F="T") @@ -45,20 +55,35 @@ TV = TopologicalVector(threshold=-1) tv = TV.fit_transform(diags) print("Topological vector is " + str(tv[0,:])) -#diagsT = DiagramPreprocessor(use=True, scalers=[([0,1], BirthPersistenceTransform())]).fit_transform(diags) -#PI = PersistenceImage(bandwidth=1., weight=lambda x: x[1], im_range=[0,10,0,10], resolution=[100,100]) -#pi = PI.fit_transform(diagsT) -#plt.imshow(np.flip(np.reshape(pi[0], [100,100]), 0)) -#plt.show() +PI = PersistenceImage(bandwidth=.1, weight=lambda x: x[1], im_range=[0,1,0,1], resolution=[100,100]) +pi = PI.fit_transform(diags) +plt.imshow(np.flip(np.reshape(pi[0], [100,100]), 0)) +plt.title("Persistence Image") +plt.show() -plt.scatter(D[:,0],D[:,1]) -D = np.array([[1.,5.],[3.,6.],[2.,7.]]) -plt.scatter(D[:,0],D[:,1]) -plt.plot([0.,10.],[0.,10.]) +ET = Entropy(mode="scalar") +et = ET.fit_transform(diags) +print("Entropy statistic is " + str(et[0,:])) + +ET = Entropy(mode="vector", normalized=False) +et = ET.fit_transform(diags) +plt.plot(et[0]) +plt.title("Entropy function") plt.show() +D = np.array([[1.,5.],[3.,6.],[2.,7.]]) diags2 = [D] +diags2 = DiagramScaler(use=True, scalers=[([0,1], MinMaxScaler())]).fit_transform(diags2) + +D = diags[0] +plt.scatter(D[:,0],D[:,1]) +D = diags2[0] +plt.scatter(D[:,0],D[:,1]) +plt.plot([0.,1.],[0.,1.]) +plt.title("Test Persistence Diagrams for kernel methods") +plt.show() + def arctan(C,p): return lambda x: C*np.arctan(np.power(x[1], p)) diff --git a/src/python/gudhi/sktda/kernel_methods.py b/src/python/gudhi/sktda/kernel_methods.py index d90bf164..b49bdf60 100644 --- a/src/python/gudhi/sktda/kernel_methods.py +++ b/src/python/gudhi/sktda/kernel_methods.py @@ -18,11 +18,11 @@ class SlicedWassersteinKernel(BaseEstimator, TransformerMixin): """ def __init__(self, num_directions=10, bandwidth=1.0): """ - Constructor for the SlicedWassersteinDistance class. + Constructor for the SlicedWassersteinKernel class. Attributes: bandwidth (double): bandwidth of the Gaussian kernel applied to the sliced Wasserstein distance (default 1.). - num_directions (int): number of lines to sample uniformly from [-pi,pi] in order to approximate and speed up the kernel computation (default 10). If -1, the exact kernel is computed. + num_directions (int): number of lines evenly sampled on [-pi,pi] in order to approximate and speed up the kernel computation (default 10). If -1, the exact kernel is computed. """ self.bandwidth = bandwidth self.sw_ = SlicedWassersteinDistance(num_directions=num_directions) @@ -82,7 +82,7 @@ class PersistenceWeightedGaussianKernel(BaseEstimator, TransformerMixin): def transform(self, X): """ - Compute all sliced Wasserstein kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + Compute all persistence weighted Gaussian kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. Parameters: X (list of n x 2 numpy arrays): input persistence diagrams. @@ -118,7 +118,7 @@ class PersistenceWeightedGaussianKernel(BaseEstimator, TransformerMixin): class PersistenceScaleSpaceKernel(BaseEstimator, TransformerMixin): """ - This is a class for computing the persistence scale space kernel matrix from a list of persistence diagrams. The persistence scale space kernel is computed by adding the symmetric to the diagonal of each point in each persistence diagram, and then convolving the points with a Gaussian kernel. See https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Reininghaus_A_Stable_Multi-Scale_2015_CVPR_paper.pdf for more details. + This is a class for computing the persistence scale space kernel matrix from a list of persistence diagrams. The persistence scale space kernel is computed by adding the symmetric to the diagonal of each point in each persistence diagram, with negative weight, and then convolving the points with a Gaussian kernel. See https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Reininghaus_A_Stable_Multi-Scale_2015_CVPR_paper.pdf for more details. """ def __init__(self, bandwidth=1., kernel_approx=None): """ diff --git a/src/python/gudhi/sktda/metrics.py b/src/python/gudhi/sktda/metrics.py index 8092f7af..816441b6 100644 --- a/src/python/gudhi/sktda/metrics.py +++ b/src/python/gudhi/sktda/metrics.py @@ -34,7 +34,7 @@ class SlicedWassersteinDistance(BaseEstimator, TransformerMixin): def fit(self, X, y=None): """ - Fit the SlicedWassersteinDistance class on a list of persistence diagrams: persistence diagrams are projected onto the different lines. The diagrams themselves and their projections are then stored in a numpy array called **diagrams**. + Fit the SlicedWassersteinDistance class on a list of persistence diagrams: persistence diagrams are projected onto the different lines. The diagrams themselves and their projections are then stored in numpy arrays, called **diagrams_** and **approx_diag_**. Parameters: X (list of n x 2 numpy arrays): input persistence diagrams. diff --git a/src/python/gudhi/sktda/preprocessing.py b/src/python/gudhi/sktda/preprocessing.py index 3c625053..784e300f 100644 --- a/src/python/gudhi/sktda/preprocessing.py +++ b/src/python/gudhi/sktda/preprocessing.py @@ -43,8 +43,9 @@ class BirthPersistenceTransform(BaseEstimator, TransformerMixin): """ Xfit = [] for diag in X: - new_diag = np.empty(diag.shape) - np.copyto(new_diag, diag) + #new_diag = np.empty(diag.shape) + #np.copyto(new_diag, diag) + new_diag = np.copy(diag) new_diag[:,1] = new_diag[:,1] - new_diag[:,0] Xfit.append(new_diag) return Xfit @@ -82,7 +83,8 @@ class Clamping(BaseEstimator, TransformerMixin): Returns: Xfit (numpy array of size n): output list of values. """ - Xfit = np.where(X >= self.limit, self.limit * np.ones(X.shape), X) + Xfit = np.minimum(X, self.limit) + #Xfit = np.where(X >= self.limit, self.limit * np.ones(X.shape), X) return Xfit class DiagramScaler(BaseEstimator, TransformerMixin): @@ -91,7 +93,7 @@ class DiagramScaler(BaseEstimator, TransformerMixin): """ def __init__(self, use=False, scalers=[]): """ - Constructor for the DiagramPreprocessor class. + Constructor for the DiagramScaler class. Attributes: use (bool): whether to use the class or not (default False). @@ -102,7 +104,7 @@ class DiagramScaler(BaseEstimator, TransformerMixin): def fit(self, X, y=None): """ - Fit the DiagramPreprocessor class on a list of persistence diagrams: persistence diagrams are concatenated in a big numpy array, and scalers are fit (by calling their fit() method) on their corresponding coordinates in this big array. + Fit the DiagramScaler class on a list of persistence diagrams: persistence diagrams are concatenated in a big numpy array, and scalers are fit (by calling their fit() method) on their corresponding coordinates in this big array. Parameters: X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. @@ -119,7 +121,7 @@ class DiagramScaler(BaseEstimator, TransformerMixin): def transform(self, X): """ - Apply the DiagramPreprocessor function on the persistence diagrams. The fitted scalers are applied (by calling their transform() method) to their corresponding coordinates in each persistence diagram individually. + Apply the DiagramScaler function on the persistence diagrams. The fitted scalers are applied (by calling their transform() method) to their corresponding coordinates in each persistence diagram individually. Parameters: X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. @@ -293,7 +295,7 @@ class DiagramSelector(BaseEstimator, TransformerMixin): if self.point_type == "finite": Xfit = [ diag[diag[:,1] < self.limit] if diag.shape[0] != 0 else diag for diag in X] else: - Xfit = [ diag[diag[:,1] == self.limit, 0:1] if diag.shape[0] != 0 else diag for diag in X] + Xfit = [ diag[diag[:,1] >= self.limit, 0:1] if diag.shape[0] != 0 else diag for diag in X] else: Xfit = X return Xfit diff --git a/src/python/gudhi/sktda/vector_methods.py b/src/python/gudhi/sktda/vector_methods.py index 3862f815..d767a952 100644 --- a/src/python/gudhi/sktda/vector_methods.py +++ b/src/python/gudhi/sktda/vector_methods.py @@ -69,7 +69,7 @@ class PersistenceImage(BaseEstimator, TransformerMixin): x_values, y_values = np.linspace(self.im_range[0], self.im_range[1], self.resolution[0]), np.linspace(self.im_range[2], self.im_range[3], self.resolution[1]) Xs, Ys = np.tile((diagram[:,0][:,np.newaxis,np.newaxis]-x_values[np.newaxis,np.newaxis,:]),[1,self.resolution[1],1]), np.tile(diagram[:,1][:,np.newaxis,np.newaxis]-y_values[np.newaxis,:,np.newaxis],[1,1,self.resolution[0]]) - image = np.tensordot(w, np.exp((-np.square(Xs)-np.square(Ys))/(2*np.square(self.bandwidth)))/(self.bandwidth*np.sqrt(2*np.pi)), 1) + image = np.tensordot(w, np.exp((-np.square(Xs)-np.square(Ys))/(2*np.square(self.bandwidth)))/(np.square(self.bandwidth)*2*np.pi), 1) Xfit.append(image.flatten()[np.newaxis,:]) @@ -299,7 +299,7 @@ class BettiCurve(BaseEstimator, TransformerMixin): class Entropy(BaseEstimator, TransformerMixin): """ - This is a class for computing persistence entropy. Persistence entropy is a statistic for persistence diagrams inspired from Shannon entropy. This statistic can also be used to compute a feature vector, called the entropy summary function. See https://arxiv.org/pdf/1803.08304.pdf for more details. + This is a class for computing persistence entropy. Persistence entropy is a statistic for persistence diagrams inspired from Shannon entropy. This statistic can also be used to compute a feature vector, called the entropy summary function. See https://arxiv.org/pdf/1803.08304.pdf for more details. Note that a previous implementation was contributed by Manuel Soriano-Trigueros. """ def __init__(self, mode="scalar", normalized=True, resolution=100, sample_range=[np.nan, np.nan]): """ @@ -376,7 +376,7 @@ class TopologicalVector(BaseEstimator, TransformerMixin): Constructor for the TopologicalVector class. Attributes: - threshold (int): number of distances to keep (default 10). This is the dimension of the topological vector. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding topological vector as threshold. + threshold (int): number of distances to keep (default 10). This is the dimension of the topological vector. If , this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding topological vector as threshold. """ self.threshold = threshold @@ -430,7 +430,7 @@ class ComplexPolynomial(BaseEstimator, TransformerMixin): Attributes: F (char): either "R", "S" or "T" (default "R"). Type of complex polynomial that is going to be computed (explained in https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27). - threshold (int): number of coefficients (default 10). This is the dimension of the complex vector of coefficients. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding complex vector of coefficients as threshold. + threshold (int): number of coefficients (default 10). This is the dimension of the complex vector of coefficients, i.e. the number of coefficients corresponding to the largest degree terms of the polynomial. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding complex vector of coefficients as threshold. """ self.threshold, self.F = threshold, F -- cgit v1.2.3 From 73785832fd7df6a300985d1edf3a386b64e0ce19 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 4 Nov 2019 22:44:27 +0100 Subject: Epeck is now the default value. Epick and Epeck in doc. User warning on ctor if Epeck with CGAL < 5.X --- src/Alpha_complex/include/gudhi/Alpha_complex.h | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex.h b/src/Alpha_complex/include/gudhi/Alpha_complex.h index 8919cdb9..0cb4accb 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex.h @@ -20,7 +20,8 @@ #include // isnan, fmax #include -#include +#include // For EXACT or SAFE version +#include // For FAST version #include #include // for CGAL::Identity_property_map #include @@ -39,17 +40,20 @@ // Make compilation fail - required for external projects - https://github.com/GUDHI/gudhi-devel/issues/10 #if CGAL_VERSION_NR < 1041101000 -# error Alpha_complex_3d is only available for CGAL >= 4.11 +# error Alpha_complex is only available for CGAL >= 4.11 #endif #if !EIGEN_VERSION_AT_LEAST(3,1,0) -# error Alpha_complex_3d is only available for Eigen3 >= 3.1.0 installed with CGAL +# error Alpha_complex is only available for Eigen3 >= 3.1.0 installed with CGAL #endif namespace Gudhi { namespace alpha_complex { +template struct Is_Epick_D { static const bool value = false; }; +template struct Is_Epick_D> { static const bool value = true; }; + /** * \class Alpha_complex Alpha_complex.h gudhi/Alpha_complex.h * \brief Alpha complex data structure. @@ -63,17 +67,20 @@ namespace alpha_complex { * * Please refer to \ref alpha_complex for examples. * - * The complex is a template class requiring an Epick_d CGAL::Epeck_d, + * or an CGAL::Epick_d dD Geometry Kernel * \cite cgal:s-gkd-15b from CGAL as template, default value is CGAL::Epick_d + * href="https://doc.cgal.org/latest/Kernel_d/structCGAL_1_1Epeck__d.html">CGAL::Epeck_d * < * CGAL::Dynamic_dimension_tag > * * \remark When Alpha_complex is constructed with an infinite value of alpha, the complex is a Delaunay complex. * */ -template> +template> class Alpha_complex { public: // Add an int in TDS to save point index in the structure @@ -184,6 +191,12 @@ class Alpha_complex { private: template void init_from_range(const InputPointRange& points) { + #if CGAL_VERSION_NR < 1050000000 + if (Is_Epick_D::value) + std::cerr << "It is strongly advised to use a CGAL version from 5.0 for performance reasons." + << "Your CGAL version is " << CGAL_VERSION << std::endl; + #endif + auto first = std::begin(points); auto last = std::end(points); -- cgit v1.2.3 From 77e7ee6e197aa8f0cf0fc0065c8d12e7c543e21f Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Mon, 4 Nov 2019 17:41:24 -0500 Subject: fixed typos --- src/python/gudhi/sktda/kernel_methods.py | 2 +- src/python/gudhi/sktda/vector_methods.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/sktda/kernel_methods.py b/src/python/gudhi/sktda/kernel_methods.py index b49bdf60..20cda49b 100644 --- a/src/python/gudhi/sktda/kernel_methods.py +++ b/src/python/gudhi/sktda/kernel_methods.py @@ -22,7 +22,7 @@ class SlicedWassersteinKernel(BaseEstimator, TransformerMixin): Attributes: bandwidth (double): bandwidth of the Gaussian kernel applied to the sliced Wasserstein distance (default 1.). - num_directions (int): number of lines evenly sampled on [-pi,pi] in order to approximate and speed up the kernel computation (default 10). If -1, the exact kernel is computed. + num_directions (int): number of lines evenly sampled on [-pi/2,pi/2] in order to approximate and speed up the kernel computation (default 10). If -1, the exact kernel is computed. """ self.bandwidth = bandwidth self.sw_ = SlicedWassersteinDistance(num_directions=num_directions) diff --git a/src/python/gudhi/sktda/vector_methods.py b/src/python/gudhi/sktda/vector_methods.py index d767a952..1f304eaf 100644 --- a/src/python/gudhi/sktda/vector_methods.py +++ b/src/python/gudhi/sktda/vector_methods.py @@ -376,7 +376,7 @@ class TopologicalVector(BaseEstimator, TransformerMixin): Constructor for the TopologicalVector class. Attributes: - threshold (int): number of distances to keep (default 10). This is the dimension of the topological vector. If , this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding topological vector as threshold. + threshold (int): number of distances to keep (default 10). This is the dimension of the topological vector. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding topological vector as threshold. """ self.threshold = threshold -- cgit v1.2.3 From 38135d576d92cea7b4e355e2e70a4fe322d4b6e7 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Mon, 4 Nov 2019 18:18:10 -0500 Subject: fixes to Marc's comments --- src/python/example/ex_diagrams.py | 2 +- src/python/gudhi/sktda/kernel_methods.py | 2 +- src/python/gudhi/sktda/metrics.py | 2 +- src/python/gudhi/sktda/vector_methods.py | 12 ++++++------ 4 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/python/example/ex_diagrams.py b/src/python/example/ex_diagrams.py index f12304bd..a6a36b7c 100755 --- a/src/python/example/ex_diagrams.py +++ b/src/python/example/ex_diagrams.py @@ -47,7 +47,7 @@ plt.plot(bc[0]) plt.title("Betti Curve") plt.show() -CP = ComplexPolynomial(threshold=-1, F="T") +CP = ComplexPolynomial(threshold=-1, polynomial_type="T") cp = CP.fit_transform(diags) print("Complex polynomial is " + str(cp[0,:])) diff --git a/src/python/gudhi/sktda/kernel_methods.py b/src/python/gudhi/sktda/kernel_methods.py index 20cda49b..b8d4ab3a 100644 --- a/src/python/gudhi/sktda/kernel_methods.py +++ b/src/python/gudhi/sktda/kernel_methods.py @@ -22,7 +22,7 @@ class SlicedWassersteinKernel(BaseEstimator, TransformerMixin): Attributes: bandwidth (double): bandwidth of the Gaussian kernel applied to the sliced Wasserstein distance (default 1.). - num_directions (int): number of lines evenly sampled on [-pi/2,pi/2] in order to approximate and speed up the kernel computation (default 10). If -1, the exact kernel is computed. + num_directions (int): number of lines evenly sampled from [-pi/2,pi/2] in order to approximate and speed up the kernel computation (default 10). If -1, the exact kernel is computed. """ self.bandwidth = bandwidth self.sw_ = SlicedWassersteinDistance(num_directions=num_directions) diff --git a/src/python/gudhi/sktda/metrics.py b/src/python/gudhi/sktda/metrics.py index 816441b6..e85fd14d 100644 --- a/src/python/gudhi/sktda/metrics.py +++ b/src/python/gudhi/sktda/metrics.py @@ -26,7 +26,7 @@ class SlicedWassersteinDistance(BaseEstimator, TransformerMixin): Constructor for the SlicedWassersteinDistance class. Attributes: - num_directions (int): number of lines to sample uniformly from [-pi,pi] in order to approximate and speed up the distance computation (default 10). + num_directions (int): number of lines evenly sampled from [-pi/2,pi/2] in order to approximate and speed up the distance computation (default 10). """ self.num_directions = num_directions thetas = np.linspace(-np.pi/2, np.pi/2, num=self.num_directions+1)[np.newaxis,:-1] diff --git a/src/python/gudhi/sktda/vector_methods.py b/src/python/gudhi/sktda/vector_methods.py index 1f304eaf..42ded45f 100644 --- a/src/python/gudhi/sktda/vector_methods.py +++ b/src/python/gudhi/sktda/vector_methods.py @@ -424,15 +424,15 @@ class ComplexPolynomial(BaseEstimator, TransformerMixin): """ This is a class for computing complex polynomials from a list of persistence diagrams. The persistence diagram points are seen as the roots of some complex polynomial, whose coefficients are returned in a complex vector. See https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27 for more details. """ - def __init__(self, F="R", threshold=10): + def __init__(self, polynomial_type="R", threshold=10): """ Constructor for the ComplexPolynomial class. Attributes: - F (char): either "R", "S" or "T" (default "R"). Type of complex polynomial that is going to be computed (explained in https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27). + polynomial_type (char): either "R", "S" or "T" (default "R"). Type of complex polynomial that is going to be computed (explained in https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27). threshold (int): number of coefficients (default 10). This is the dimension of the complex vector of coefficients, i.e. the number of coefficients corresponding to the largest degree terms of the polynomial. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding complex vector of coefficients as threshold. """ - self.threshold, self.F = threshold, F + self.threshold, self.polynomial_type = threshold, polynomial_type def fit(self, X, y=None): """ @@ -462,13 +462,13 @@ class ComplexPolynomial(BaseEstimator, TransformerMixin): Xfit = np.zeros([len(X), thresh]) + 1j * np.zeros([len(X), thresh]) for d in range(len(X)): D, N = X[d], X[d].shape[0] - if self.F == "R": + if self.polynomial_type == "R": roots = D[:,0] + 1j * D[:,1] - elif self.F == "S": + elif self.polynomial_type == "S": alpha = np.linalg.norm(D, axis=1) alpha = np.where(alpha==0, np.ones(N), alpha) roots = np.multiply( np.multiply( (D[:,0]+1j*D[:,1]), (D[:,1]-D[:,0]) ), 1./(np.sqrt(2)*alpha) ) - elif self.F == "T": + elif self.polynomial_type == "T": alpha = np.linalg.norm(D, axis=1) roots = np.multiply( (D[:,1]-D[:,0])/2, np.cos(alpha) - np.sin(alpha) + 1j * (np.cos(alpha) + np.sin(alpha)) ) coeff = [0] * (N+1) -- cgit v1.2.3 From c9c023d407cd2f8e0d2202531d8cc64ed40fd3e5 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 5 Nov 2019 09:21:30 +0100 Subject: Do not print #define variable --- src/Alpha_complex/include/gudhi/Alpha_complex.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex.h b/src/Alpha_complex/include/gudhi/Alpha_complex.h index 0cb4accb..27a4ba04 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex.h @@ -193,8 +193,7 @@ class Alpha_complex { void init_from_range(const InputPointRange& points) { #if CGAL_VERSION_NR < 1050000000 if (Is_Epick_D::value) - std::cerr << "It is strongly advised to use a CGAL version from 5.0 for performance reasons." - << "Your CGAL version is " << CGAL_VERSION << std::endl; + std::cerr << "It is strongly advised to use a CGAL version from 5.0 for performance reasons." << std::endl; #endif auto first = std::begin(points); -- cgit v1.2.3 From 2f2027474924f3c14bc1384e3b704f27c338e09b Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 5 Nov 2019 09:30:39 +0100 Subject: Add Alpha complex tests with Epeck_d - the new default one --- src/Alpha_complex/test/Alpha_complex_unit_test.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/Alpha_complex/test/Alpha_complex_unit_test.cpp b/src/Alpha_complex/test/Alpha_complex_unit_test.cpp index 01e4cee3..40b3fe09 100644 --- a/src/Alpha_complex/test/Alpha_complex_unit_test.cpp +++ b/src/Alpha_complex/test/Alpha_complex_unit_test.cpp @@ -15,6 +15,7 @@ #include #include +#include #include // float comparison #include @@ -28,12 +29,16 @@ #include // Use dynamic_dimension_tag for the user to be able to set dimension -typedef CGAL::Epick_d< CGAL::Dynamic_dimension_tag > Kernel_d; +typedef CGAL::Epeck_d< CGAL::Dynamic_dimension_tag > Exact_kernel_d; // Use static dimension_tag for the user not to be able to set dimension -typedef CGAL::Epick_d< CGAL::Dimension_tag<3> > Kernel_s; +typedef CGAL::Epeck_d< CGAL::Dimension_tag<3> > Exact_kernel_s; +// Use dynamic_dimension_tag for the user to be able to set dimension +typedef CGAL::Epick_d< CGAL::Dynamic_dimension_tag > Inexact_kernel_d; +// Use static dimension_tag for the user not to be able to set dimension +typedef CGAL::Epick_d< CGAL::Dimension_tag<3> > Inexact_kernel_s; // The triangulation uses the default instantiation of the TriangulationDataStructure template parameter -typedef boost::mpl::list list_of_kernel_variants; +typedef boost::mpl::list list_of_kernel_variants; BOOST_AUTO_TEST_CASE_TEMPLATE(Alpha_complex_from_OFF_file, TestedKernel, list_of_kernel_variants) { // ---------------------------------------------------------------------------- @@ -86,7 +91,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(Alpha_complex_from_OFF_file, TestedKernel, list_of } // Use static dimension_tag for the user not to be able to set dimension -typedef CGAL::Epick_d< CGAL::Dimension_tag<4> > Kernel_4; +typedef CGAL::Epeck_d< CGAL::Dimension_tag<4> > Kernel_4; typedef Kernel_4::Point_d Point_4; typedef std::vector Vector_4_Points; -- cgit v1.2.3 From caa4fc8d87c2842442a3ae78bc5d7ed4124253b0 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 5 Nov 2019 15:41:09 +0100 Subject: Use Epeck_d for examples. Add an Epick_d (fast version) example and test it --- .../example/Alpha_complex_from_off.cpp | 5 +- .../example/Alpha_complex_from_points.cpp | 5 +- src/Alpha_complex/example/CMakeLists.txt | 16 +++++- .../example/Fast_alpha_complex_from_off.cpp | 65 ++++++++++++++++++++++ src/Alpha_complex/include/gudhi/Alpha_complex.h | 15 +++-- 5 files changed, 94 insertions(+), 12 deletions(-) create mode 100644 src/Alpha_complex/example/Fast_alpha_complex_from_off.cpp (limited to 'src') diff --git a/src/Alpha_complex/example/Alpha_complex_from_off.cpp b/src/Alpha_complex/example/Alpha_complex_from_off.cpp index d411e90a..6b5f1a74 100644 --- a/src/Alpha_complex/example/Alpha_complex_from_off.cpp +++ b/src/Alpha_complex/example/Alpha_complex_from_off.cpp @@ -2,8 +2,6 @@ // to construct a simplex_tree from alpha complex #include -#include - #include #include @@ -23,8 +21,7 @@ int main(int argc, char **argv) { // ---------------------------------------------------------------------------- // Init of an alpha complex from an OFF file // ---------------------------------------------------------------------------- - using Kernel = CGAL::Epick_d< CGAL::Dynamic_dimension_tag >; - Gudhi::alpha_complex::Alpha_complex alpha_complex_from_file(off_file_name); + Gudhi::alpha_complex::Alpha_complex<> alpha_complex_from_file(off_file_name); std::streambuf* streambufffer; std::ofstream ouput_file_stream; diff --git a/src/Alpha_complex/example/Alpha_complex_from_points.cpp b/src/Alpha_complex/example/Alpha_complex_from_points.cpp index 981aa470..6526ca3a 100644 --- a/src/Alpha_complex/example/Alpha_complex_from_points.cpp +++ b/src/Alpha_complex/example/Alpha_complex_from_points.cpp @@ -2,12 +2,13 @@ // to construct a simplex_tree from alpha complex #include -#include +#include #include #include -using Kernel = CGAL::Epick_d< CGAL::Dimension_tag<2> >; +// Explicit dimension 2 Epeck_d kernel +using Kernel = CGAL::Epeck_d< CGAL::Dimension_tag<2> >; using Point = Kernel::Point_d; using Vector_of_points = std::vector; diff --git a/src/Alpha_complex/example/CMakeLists.txt b/src/Alpha_complex/example/CMakeLists.txt index b069b443..b0337934 100644 --- a/src/Alpha_complex/example/CMakeLists.txt +++ b/src/Alpha_complex/example/CMakeLists.txt @@ -5,9 +5,12 @@ if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) target_link_libraries(Alpha_complex_example_from_points ${CGAL_LIBRARY}) add_executable ( Alpha_complex_example_from_off Alpha_complex_from_off.cpp ) target_link_libraries(Alpha_complex_example_from_off ${CGAL_LIBRARY}) + add_executable ( Alpha_complex_example_fast_from_off Fast_alpha_complex_from_off.cpp ) + target_link_libraries(Alpha_complex_example_fast_from_off ${CGAL_LIBRARY}) if (TBB_FOUND) target_link_libraries(Alpha_complex_example_from_points ${TBB_LIBRARIES}) target_link_libraries(Alpha_complex_example_from_off ${TBB_LIBRARIES}) + target_link_libraries(Alpha_complex_example_fast_from_off ${TBB_LIBRARIES}) endif() add_test(NAME Alpha_complex_example_from_points COMMAND $) @@ -16,7 +19,13 @@ if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) "${CMAKE_SOURCE_DIR}/data/points/alphacomplexdoc.off" "60.0" "${CMAKE_CURRENT_BINARY_DIR}/alphaoffreader_result_60.txt") add_test(NAME Alpha_complex_example_from_off_32 COMMAND $ "${CMAKE_SOURCE_DIR}/data/points/alphacomplexdoc.off" "32.0" "${CMAKE_CURRENT_BINARY_DIR}/alphaoffreader_result_32.txt") - if (DIFF_PATH) + + add_test(NAME Alpha_complex_example_fast_from_off_60 COMMAND $ + "${CMAKE_SOURCE_DIR}/data/points/alphacomplexdoc.off" "60.0" "${CMAKE_CURRENT_BINARY_DIR}/fastalphaoffreader_result_60.txt") + add_test(NAME Alpha_complex_example_fast_from_off_32 COMMAND $ + "${CMAKE_SOURCE_DIR}/data/points/alphacomplexdoc.off" "32.0" "${CMAKE_CURRENT_BINARY_DIR}/fastalphaoffreader_result_32.txt") + +if (DIFF_PATH) # Do not forget to copy test results files in current binary dir file(COPY "alphaoffreader_for_doc_32.txt" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) file(COPY "alphaoffreader_for_doc_60.txt" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) @@ -25,6 +34,11 @@ if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) ${CMAKE_CURRENT_BINARY_DIR}/alphaoffreader_result_60.txt ${CMAKE_CURRENT_BINARY_DIR}/alphaoffreader_for_doc_60.txt) add_test(Alpha_complex_example_from_off_32_diff_files ${DIFF_PATH} ${CMAKE_CURRENT_BINARY_DIR}/alphaoffreader_result_32.txt ${CMAKE_CURRENT_BINARY_DIR}/alphaoffreader_for_doc_32.txt) + + add_test(Alpha_complex_example_fast_from_off_60_diff_files ${DIFF_PATH} + ${CMAKE_CURRENT_BINARY_DIR}/fastalphaoffreader_result_60.txt ${CMAKE_CURRENT_BINARY_DIR}/alphaoffreader_for_doc_60.txt) + add_test(Alpha_complex_example_fast_from_off_32_diff_files ${DIFF_PATH} + ${CMAKE_CURRENT_BINARY_DIR}/fastalphaoffreader_result_32.txt ${CMAKE_CURRENT_BINARY_DIR}/alphaoffreader_for_doc_32.txt) endif() add_executable ( Alpha_complex_example_weighted_3d_from_points Weighted_alpha_complex_3d_from_points.cpp ) diff --git a/src/Alpha_complex/example/Fast_alpha_complex_from_off.cpp b/src/Alpha_complex/example/Fast_alpha_complex_from_off.cpp new file mode 100644 index 00000000..d6a39a76 --- /dev/null +++ b/src/Alpha_complex/example/Fast_alpha_complex_from_off.cpp @@ -0,0 +1,65 @@ +#include +// to construct a simplex_tree from alpha complex +#include + +#include + +#include +#include + +void usage(int nbArgs, char * const progName) { + std::cerr << "Error: Number of arguments (" << nbArgs << ") is not correct\n"; + std::cerr << "Usage: " << progName << " filename.off alpha_square_max_value [ouput_file.txt]\n"; + std::cerr << " i.e.: " << progName << " ../../data/points/alphacomplexdoc.off 60.0\n"; + exit(-1); // ----- >> +} + +int main(int argc, char **argv) { + if ((argc != 3) && (argc != 4)) usage(argc, (argv[0] - 1)); + + std::string off_file_name {argv[1]}; + double alpha_square_max_value {atof(argv[2])}; + + // WARNING : CGAL::Epick_d is fast but not safe (unlike CGAL::Epeck_d) + // (i.e. when the points are on a grid) + using Fast_kernel = CGAL::Epick_d; + // ---------------------------------------------------------------------------- + // Init of an alpha complex from an OFF file + // ---------------------------------------------------------------------------- + Gudhi::alpha_complex::Alpha_complex alpha_complex_from_file(off_file_name); + + std::streambuf* streambufffer; + std::ofstream ouput_file_stream; + + if (argc == 4) { + ouput_file_stream.open(std::string(argv[3])); + streambufffer = ouput_file_stream.rdbuf(); + } else { + streambufffer = std::cout.rdbuf(); + } + + Gudhi::Simplex_tree<> simplex; + if (alpha_complex_from_file.create_complex(simplex, alpha_square_max_value)) { + std::ostream output_stream(streambufffer); + + // ---------------------------------------------------------------------------- + // Display information about the alpha complex + // ---------------------------------------------------------------------------- + output_stream << "Alpha complex is of dimension " << simplex.dimension() << + " - " << simplex.num_simplices() << " simplices - " << + simplex.num_vertices() << " vertices." << std::endl; + + output_stream << "Iterator on alpha complex simplices in the filtration order, with [filtration value]:" << + std::endl; + for (auto f_simplex : simplex.filtration_simplex_range()) { + output_stream << " ( "; + for (auto vertex : simplex.simplex_vertex_range(f_simplex)) { + output_stream << vertex << " "; + } + output_stream << ") -> " << "[" << simplex.filtration(f_simplex) << "] "; + output_stream << std::endl; + } + } + ouput_file_stream.close(); + return 0; +} diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex.h b/src/Alpha_complex/include/gudhi/Alpha_complex.h index 27a4ba04..4e0e5159 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex.h @@ -24,7 +24,6 @@ #include // For FAST version #include #include // for CGAL::Identity_property_map -#include #include // for CGAL_VERSION_NR #include // for EIGEN_VERSION_AT_LEAST @@ -249,6 +248,8 @@ class Alpha_complex { * @param[in] complex SimplicialComplexForAlpha to be created. * @param[in] max_alpha_square maximum for alpha square value. Default value is +\f$\infty\f$, and there is very * little point using anything else since it does not save time. + * @param[in] exact Exact filtration values computation. Not exact if `Kernel` is not CGAL::Epeck_d. * * @return true if creation succeeds, false otherwise. * @@ -260,7 +261,8 @@ class Alpha_complex { template bool create_complex(SimplicialComplexForAlpha& complex, - Filtration_value max_alpha_square = std::numeric_limits::infinity()) { + Filtration_value max_alpha_square = std::numeric_limits::infinity(), + bool exact = false) { // From SimplicialComplexForAlpha type required to insert into a simplicial complex (with or without subfaces). typedef typename SimplicialComplexForAlpha::Vertex_handle Vertex_handle; typedef typename SimplicialComplexForAlpha::Simplex_handle Simplex_handle; @@ -336,9 +338,12 @@ class Alpha_complex { if (f_simplex_dim > 0) { // squared_radius function initialization Squared_Radius squared_radius = kernel_.compute_squared_radius_d_object(); - CGAL::NT_converter cv; - - alpha_complex_filtration = cv(squared_radius(pointVector.begin(), pointVector.end())); + + if (exact) { + alpha_complex_filtration = CGAL::to_double(CGAL::exact(squared_radius(pointVector.begin(), pointVector.end()))); + } else { + alpha_complex_filtration = CGAL::to_double(squared_radius(pointVector.begin(), pointVector.end())); + } } complex.assign_filtration(f_simplex, alpha_complex_filtration); #ifdef DEBUG_TRACES -- cgit v1.2.3 From f8bc102a7ec525d61e836892df6e1bfef862635f Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 5 Nov 2019 16:46:14 +0100 Subject: Modify alpha_lcomplex_persistence utility for fast, safe and exact versions. Add some tests --- src/Alpha_complex/utilities/CMakeLists.txt | 30 ++++-- .../utilities/alpha_complex_persistence.cpp | 107 +++++++++++++-------- 2 files changed, 86 insertions(+), 51 deletions(-) (limited to 'src') diff --git a/src/Alpha_complex/utilities/CMakeLists.txt b/src/Alpha_complex/utilities/CMakeLists.txt index 5295f3cd..57b92942 100644 --- a/src/Alpha_complex/utilities/CMakeLists.txt +++ b/src/Alpha_complex/utilities/CMakeLists.txt @@ -7,9 +7,19 @@ if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) if (TBB_FOUND) target_link_libraries(alpha_complex_persistence ${TBB_LIBRARIES}) endif(TBB_FOUND) - add_test(NAME Alpha_complex_utilities_alpha_complex_persistence COMMAND $ - "${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off" "-p" "2" "-m" "0.45") - + add_test(NAME Alpha_complex_utilities_safe_alpha_complex_persistence COMMAND $ + "${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off" "-p" "2" "-m" "0.45" "-o" "safe.pers") + add_test(NAME Alpha_complex_utilities_fast_alpha_complex_persistence COMMAND $ + "${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off" "-p" "2" "-m" "0.45" "-o" "fast.pers" "-f") + add_test(NAME Alpha_complex_utilities_exact_alpha_complex_persistence COMMAND $ + "${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off" "-p" "2" "-m" "0.45" "-o" "exact.pers" "-e") + if (DIFF_PATH) + add_test(Alpha_complex_utilities_diff_exact_alpha_complex ${DIFF_PATH} + "exact.pers" "safe.pers") + add_test(Alpha_complex_utilities_diff_fast_alpha_complex ${DIFF_PATH} + "fast.pers" "safe.pers") + endif() + install(TARGETS alpha_complex_persistence DESTINATION bin) add_executable(alpha_complex_3d_persistence alpha_complex_3d_persistence.cpp) @@ -20,21 +30,21 @@ if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) add_test(NAME Alpha_complex_utilities_alpha_complex_3d COMMAND $ "${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off" - "-p" "2" "-m" "0.45" "-o" "safe.pers") + "-p" "2" "-m" "0.45" "-o" "safe_3d.pers") add_test(NAME Alpha_complex_utilities_exact_alpha_complex_3d COMMAND $ "${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off" - "-p" "2" "-m" "0.45" "-o" "exact.pers" "-e") + "-p" "2" "-m" "0.45" "-o" "exact_3d.pers" "-e") add_test(NAME Alpha_complex_utilities_safe_alpha_complex_3d COMMAND $ "${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off" - "-p" "2" "-m" "0.45" "-o" "fast.pers" "-f") + "-p" "2" "-m" "0.45" "-o" "fast_3d.pers" "-f") if (DIFF_PATH) - add_test(Alpha_complex_utilities_diff_alpha_complex_3d ${DIFF_PATH} - "exact.pers" "safe.pers") - add_test(Alpha_complex_utilities_diff_alpha_complex_3d ${DIFF_PATH} - "fast.pers" "safe.pers") + add_test(Alpha_complex_utilities_diff_exact_alpha_complex_3d ${DIFF_PATH} + "exact_3d.pers" "safe_3d.pers") + add_test(Alpha_complex_utilities_diff_fast_alpha_complex_3d ${DIFF_PATH} + "fast_3d.pers" "safe_3d.pers") endif() add_test(NAME Alpha_complex_utilities_periodic_alpha_complex_3d_persistence COMMAND $ diff --git a/src/Alpha_complex/utilities/alpha_complex_persistence.cpp b/src/Alpha_complex/utilities/alpha_complex_persistence.cpp index fab7bd30..486347cc 100644 --- a/src/Alpha_complex/utilities/alpha_complex_persistence.cpp +++ b/src/Alpha_complex/utilities/alpha_complex_persistence.cpp @@ -24,63 +24,84 @@ using Simplex_tree = Gudhi::Simplex_tree<>; using Filtration_value = Simplex_tree::Filtration_value; -void program_options(int argc, char *argv[], std::string &off_file_points, std::string &output_file_diag, - Filtration_value &alpha_square_max_value, int &coeff_field_characteristic, - Filtration_value &min_persistence); +void program_options(int argc, char *argv[], std::string &off_file_points, bool &exact, bool &fast, + std::string &output_file_diag, Filtration_value &alpha_square_max_value, + int &coeff_field_characteristic, Filtration_value &min_persistence); int main(int argc, char **argv) { std::string off_file_points; std::string output_file_diag; + bool exact_version = false; + bool fast_version = false; Filtration_value alpha_square_max_value; int coeff_field_characteristic; Filtration_value min_persistence; - program_options(argc, argv, off_file_points, output_file_diag, alpha_square_max_value, coeff_field_characteristic, - min_persistence); + program_options(argc, argv, off_file_points, exact_version, fast_version, output_file_diag, alpha_square_max_value, + coeff_field_characteristic, min_persistence); - // ---------------------------------------------------------------------------- - // Init of an alpha complex from an OFF file - // ---------------------------------------------------------------------------- - using Kernel = CGAL::Epick_d; - Gudhi::alpha_complex::Alpha_complex alpha_complex_from_file(off_file_points); + if ((exact_version) && (fast_version)) { + std::cerr << "You cannot set the exact and the fast version." << std::endl; + exit(-1); + } Simplex_tree simplex; - if (alpha_complex_from_file.create_complex(simplex, alpha_square_max_value)) { - // ---------------------------------------------------------------------------- - // Display information about the alpha complex - // ---------------------------------------------------------------------------- - std::cout << "Simplicial complex is of dimension " << simplex.dimension() << " - " << simplex.num_simplices() - << " simplices - " << simplex.num_vertices() << " vertices." << std::endl; - - // Sort the simplices in the order of the filtration - simplex.initialize_filtration(); - - std::cout << "Simplex_tree dim: " << simplex.dimension() << std::endl; - // Compute the persistence diagram of the complex - Gudhi::persistent_cohomology::Persistent_cohomology pcoh( - simplex); - // initializes the coefficient field for homology - pcoh.init_coefficients(coeff_field_characteristic); - - pcoh.compute_persistent_cohomology(min_persistence); - - // Output the diagram in filediag - if (output_file_diag.empty()) { - pcoh.output_diagram(); - } else { - std::cout << "Result in file: " << output_file_diag << std::endl; - std::ofstream out(output_file_diag); - pcoh.output_diagram(out); - out.close(); + if (fast_version) { + // WARNING : CGAL::Epick_d is fast but not safe (unlike CGAL::Epeck_d) + // (i.e. when the points are on a grid) + using Fast_kernel = CGAL::Epick_d; + + // Init of an alpha complex from an OFF file + Gudhi::alpha_complex::Alpha_complex alpha_complex_from_file(off_file_points); + + if (!alpha_complex_from_file.create_complex(simplex, alpha_square_max_value)) { + std::cerr << "Fast Alpha complex simplicial complex creation failed." << std::endl; + exit(-1); } - } + } else { + using Kernel = CGAL::Epeck_d; + // Init of an alpha complex from an OFF file + Gudhi::alpha_complex::Alpha_complex alpha_complex_from_file(off_file_points); + + if (!alpha_complex_from_file.create_complex(simplex, alpha_square_max_value, exact_version)) { + std::cerr << "Alpha complex simplicial complex creation failed." << std::endl; + exit(-1); + } + } + // ---------------------------------------------------------------------------- + // Display information about the alpha complex + // ---------------------------------------------------------------------------- + std::cout << "Simplicial complex is of dimension " << simplex.dimension() << " - " << simplex.num_simplices() + << " simplices - " << simplex.num_vertices() << " vertices." << std::endl; + + // Sort the simplices in the order of the filtration + simplex.initialize_filtration(); + + std::cout << "Simplex_tree dim: " << simplex.dimension() << std::endl; + // Compute the persistence diagram of the complex + Gudhi::persistent_cohomology::Persistent_cohomology pcoh( + simplex); + // initializes the coefficient field for homology + pcoh.init_coefficients(coeff_field_characteristic); + + pcoh.compute_persistent_cohomology(min_persistence); + + // Output the diagram in filediag + if (output_file_diag.empty()) { + pcoh.output_diagram(); + } else { + std::cout << "Result in file: " << output_file_diag << std::endl; + std::ofstream out(output_file_diag); + pcoh.output_diagram(out); + out.close(); + } return 0; } -void program_options(int argc, char *argv[], std::string &off_file_points, std::string &output_file_diag, - Filtration_value &alpha_square_max_value, int &coeff_field_characteristic, - Filtration_value &min_persistence) { +void program_options(int argc, char *argv[], std::string &off_file_points, bool &exact, bool &fast, + std::string &output_file_diag, Filtration_value &alpha_square_max_value, + int &coeff_field_characteristic, Filtration_value &min_persistence) { namespace po = boost::program_options; po::options_description hidden("Hidden options"); hidden.add_options()("input-file", po::value(&off_file_points), @@ -88,6 +109,10 @@ void program_options(int argc, char *argv[], std::string &off_file_points, std:: po::options_description visible("Allowed options", 100); visible.add_options()("help,h", "produce help message")( + "exact,e", po::bool_switch(&exact), + "To activate exact version of Alpha complex (default is false, not available if fast is set)")( + "fast,f", po::bool_switch(&fast), + "To activate fast version of Alpha complex (default is false, not available if exact is set)")( "output-file,o", po::value(&output_file_diag)->default_value(std::string()), "Name of file in which the persistence diagram is written. Default print in std::cout")( "max-alpha-square-value,r", po::value(&alpha_square_max_value) -- cgit v1.2.3 From 16d84f8c33fe5bdb3b9b5ac14ed13004332a76de Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 5 Nov 2019 16:47:55 +0100 Subject: Modify alpha_complex_persistence utility documentation accordingly to code change --- src/Alpha_complex/utilities/alphacomplex.md | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/Alpha_complex/utilities/alphacomplex.md b/src/Alpha_complex/utilities/alphacomplex.md index fcd16a3b..527598a9 100644 --- a/src/Alpha_complex/utilities/alphacomplex.md +++ b/src/Alpha_complex/utilities/alphacomplex.md @@ -46,6 +46,8 @@ for the Alpha complex construction. coefficient field Z/pZ for computing homology. * `-m [ --min-persistence ]` (default = 0) Minimal lifetime of homology feature to be recorded. Enter a negative value to see zero length intervals. +* `-e [ --exact ]` for the exact computation version. +* `-f [ --fast ]` for the fast computation version. **Example** -- cgit v1.2.3 From 0f6714990421d967a5e76676920ab190acc61364 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 5 Nov 2019 20:49:14 +0100 Subject: Exact version only for CGAL < 5.X --- src/Alpha_complex/include/gudhi/Alpha_complex.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex.h b/src/Alpha_complex/include/gudhi/Alpha_complex.h index 4e0e5159..22e17226 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex.h @@ -338,12 +338,18 @@ class Alpha_complex { if (f_simplex_dim > 0) { // squared_radius function initialization Squared_Radius squared_radius = kernel_.compute_squared_radius_d_object(); - + +#if CGAL_VERSION_NR < 1050000000 + // With CGAL >= 4.11 and < 5.X, CGAL::exact do not work as it is always exact values + // This is why it is slow and 5.X is advised + alpha_complex_filtration = CGAL::to_double(squared_radius(pointVector.begin(), pointVector.end())); +#else // CGAL_VERSION_NR < 1050000000 if (exact) { alpha_complex_filtration = CGAL::to_double(CGAL::exact(squared_radius(pointVector.begin(), pointVector.end()))); } else { alpha_complex_filtration = CGAL::to_double(squared_radius(pointVector.begin(), pointVector.end())); } +#endif //CGAL_VERSION_NR < 1050000000 } complex.assign_filtration(f_simplex, alpha_complex_filtration); #ifdef DEBUG_TRACES -- cgit v1.2.3 From b6174ec368be398e5f4bb647120f5e5905b933e6 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Tue, 5 Nov 2019 22:30:13 +0100 Subject: Fix size of underlines for sphinx --- src/python/doc/index.rst | 4 ++-- src/python/doc/wasserstein_distance_user.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/python/doc/index.rst b/src/python/doc/index.rst index 16d918bc..8f27da0d 100644 --- a/src/python/doc/index.rst +++ b/src/python/doc/index.rst @@ -23,7 +23,7 @@ Alpha complex .. include:: alpha_complex_sum.inc Rips complex -------------- +------------ .. include:: rips_complex_sum.inc @@ -74,7 +74,7 @@ Bottleneck distance .. include:: bottleneck_distance_sum.inc Wasserstein distance -=================== +==================== .. include:: wasserstein_distance_sum.inc diff --git a/src/python/doc/wasserstein_distance_user.rst b/src/python/doc/wasserstein_distance_user.rst index bcb7f19d..9ec64e93 100644 --- a/src/python/doc/wasserstein_distance_user.rst +++ b/src/python/doc/wasserstein_distance_user.rst @@ -3,7 +3,7 @@ .. To get rid of WARNING: document isn't included in any toctree Wasserstein distance user manual -=============================== +================================ Definition ---------- -- cgit v1.2.3 From 94391b1cc232c5f66ae3cdadf865554c57f1308a Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Tue, 5 Nov 2019 23:01:31 +0100 Subject: Create GUDHI_PYTHON_MODULES_EXTRA without auto-import Put Wasserstein in it. --- src/python/CMakeLists.txt | 3 ++- src/python/doc/wasserstein_distance_user.rst | 6 ++--- src/python/gudhi/__init__.py.in | 4 +-- src/python/test/test_wasserstein_distance.py | 38 +++++++++++++--------------- 4 files changed, 25 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 1b1684e1..dbef7183 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -49,7 +49,8 @@ if(PYTHONINTERP_FOUND) set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'alpha_complex', ") set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'euclidean_witness_complex', ") set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'euclidean_strong_witness_complex', ") - set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'wasserstein', ") + # Modules that should not be auto-imported in __init__.py + set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'wasserstein', ") add_gudhi_debug_info("Python version ${PYTHON_VERSION_STRING}") add_gudhi_debug_info("Cython version ${CYTHON_VERSION}") diff --git a/src/python/doc/wasserstein_distance_user.rst b/src/python/doc/wasserstein_distance_user.rst index bcb7f19d..fc7bd82c 100644 --- a/src/python/doc/wasserstein_distance_user.rst +++ b/src/python/doc/wasserstein_distance_user.rst @@ -13,7 +13,7 @@ This implementation is based on ideas from "Large Scale Computation of Means and Function -------- -.. autofunction:: gudhi.wasserstein_distance +.. autofunction:: gudhi.wasserstein.wasserstein_distance Basic example @@ -24,13 +24,13 @@ Note that persistence diagrams must be submitted as (n x 2) numpy arrays and mus .. testcode:: - import gudhi + import gudhi.wasserstein import numpy as np diag1 = np.array([[2.7, 3.7],[9.6, 14.],[34.2, 34.974]]) diag2 = np.array([[2.8, 4.45],[9.5, 14.1]]) - message = "Wasserstein distance value = " + '%.2f' % gudhi.wasserstein_distance(diag1, diag2, q=2., p=1.) + message = "Wasserstein distance value = " + '%.2f' % gudhi.wasserstein.wasserstein_distance(diag1, diag2, q=2., p=1.) print(message) The output is: diff --git a/src/python/gudhi/__init__.py.in b/src/python/gudhi/__init__.py.in index 28bab0e1..4039c08f 100644 --- a/src/python/gudhi/__init__.py.in +++ b/src/python/gudhi/__init__.py.in @@ -21,13 +21,13 @@ __debug_info__ = @GUDHI_PYTHON_DEBUG_INFO@ from sys import exc_info from importlib import import_module -__all__ = [@GUDHI_PYTHON_MODULES@] +__all__ = [@GUDHI_PYTHON_MODULES@ @GUDHI_PYTHON_MODULES_EXTRA@] __available_modules = '' __missing_modules = '' # try to import * from gudhi.__module_name -for __module_name in __all__: +for __module_name in [@GUDHI_PYTHON_MODULES@]: try: __module = import_module('gudhi.' + __module_name) try: diff --git a/src/python/test/test_wasserstein_distance.py b/src/python/test/test_wasserstein_distance.py index c1b568e2..a6bf9901 100755 --- a/src/python/test/test_wasserstein_distance.py +++ b/src/python/test/test_wasserstein_distance.py @@ -1,4 +1,4 @@ -import gudhi +from gudhi.wasserstein import wasserstein_distance import numpy as np """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. @@ -23,28 +23,26 @@ def test_basic_wasserstein(): diag4 = np.array([[0, 3], [4, 8]]) emptydiag = np.array([[]]) - assert gudhi.wasserstein_distance(emptydiag, emptydiag, q=2., p=1.) == 0. - assert gudhi.wasserstein_distance(emptydiag, emptydiag, q=np.inf, p=1.) == 0. - assert gudhi.wasserstein_distance(emptydiag, emptydiag, q=np.inf, p=2.) == 0. - assert gudhi.wasserstein_distance(emptydiag, emptydiag, q=2., p=2.) == 0. + assert wasserstein_distance(emptydiag, emptydiag, q=2., p=1.) == 0. + assert wasserstein_distance(emptydiag, emptydiag, q=np.inf, p=1.) == 0. + assert wasserstein_distance(emptydiag, emptydiag, q=np.inf, p=2.) == 0. + assert wasserstein_distance(emptydiag, emptydiag, q=2., p=2.) == 0. - assert gudhi.wasserstein_distance(diag3, emptydiag, q=np.inf, p=1.) == 2. - assert gudhi.wasserstein_distance(diag3, emptydiag, q=1., p=1.) == 4. + assert wasserstein_distance(diag3, emptydiag, q=np.inf, p=1.) == 2. + assert wasserstein_distance(diag3, emptydiag, q=1., p=1.) == 4. - assert gudhi.wasserstein_distance(diag4, emptydiag, q=1., p=2.) == 5. # thank you Pythagorician triplets - assert gudhi.wasserstein_distance(diag4, emptydiag, q=np.inf, p=2.) == 2.5 - assert gudhi.wasserstein_distance(diag4, emptydiag, q=2., p=2.) == 3.5355339059327378 + assert wasserstein_distance(diag4, emptydiag, q=1., p=2.) == 5. # thank you Pythagorician triplets + assert wasserstein_distance(diag4, emptydiag, q=np.inf, p=2.) == 2.5 + assert wasserstein_distance(diag4, emptydiag, q=2., p=2.) == 3.5355339059327378 - assert gudhi.wasserstein_distance(diag1, diag2, q=2., p=1.) == 1.4453593023967701 - assert gudhi.wasserstein_distance(diag1, diag2, q=2.35, p=1.74) == 0.9772734057168739 + assert wasserstein_distance(diag1, diag2, q=2., p=1.) == 1.4453593023967701 + assert wasserstein_distance(diag1, diag2, q=2.35, p=1.74) == 0.9772734057168739 - assert gudhi.wasserstein_distance(diag1, emptydiag, q=2.35, p=1.7863) == 3.141592214572228 + assert wasserstein_distance(diag1, emptydiag, q=2.35, p=1.7863) == 3.141592214572228 - assert gudhi.wasserstein_distance(diag3, diag4, q=1., p=1.) == 3. - assert gudhi.wasserstein_distance(diag3, diag4, q=np.inf, p=1.) == 3. # no diag matching here - assert gudhi.wasserstein_distance(diag3, diag4, q=np.inf, p=2.) == np.sqrt(5) - assert gudhi.wasserstein_distance(diag3, diag4, q=1., p=2.) == np.sqrt(5) - assert gudhi.wasserstein_distance(diag3, diag4, q=4.5, p=2.) == np.sqrt(5) - - + assert wasserstein_distance(diag3, diag4, q=1., p=1.) == 3. + assert wasserstein_distance(diag3, diag4, q=np.inf, p=1.) == 3. # no diag matching here + assert wasserstein_distance(diag3, diag4, q=np.inf, p=2.) == np.sqrt(5) + assert wasserstein_distance(diag3, diag4, q=1., p=2.) == np.sqrt(5) + assert wasserstein_distance(diag3, diag4, q=4.5, p=2.) == np.sqrt(5) -- cgit v1.2.3 From b9bdb2fe42e377a598abceadd8cc4ba4eea8b862 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Tue, 5 Nov 2019 23:54:05 +0100 Subject: Fix documentation indentation --- src/python/gudhi/simplex_tree.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/python/gudhi/simplex_tree.pyx b/src/python/gudhi/simplex_tree.pyx index 9f490271..90ddf6dd 100644 --- a/src/python/gudhi/simplex_tree.pyx +++ b/src/python/gudhi/simplex_tree.pyx @@ -362,7 +362,7 @@ cdef class SimplexTree: value than its faces by increasing the filtration values. :returns: True if any filtration value was modified, - False if the filtration was already non-decreasing. + False if the filtration was already non-decreasing. :rtype: bool -- cgit v1.2.3 From 8732cd18079a12ab813e863839c48b7c9e504fef Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Wed, 6 Nov 2019 07:22:06 +0100 Subject: Add doc warnings --- src/Alpha_complex/doc/Intro_alpha_complex.h | 1 + src/common/doc/main_page.md | 1 + 2 files changed, 2 insertions(+) (limited to 'src') diff --git a/src/Alpha_complex/doc/Intro_alpha_complex.h b/src/Alpha_complex/doc/Intro_alpha_complex.h index b075d1fc..fd6f4081 100644 --- a/src/Alpha_complex/doc/Intro_alpha_complex.h +++ b/src/Alpha_complex/doc/Intro_alpha_complex.h @@ -51,6 +51,7 @@ namespace alpha_complex { * - For people only interested in the topology of the \ref alpha_complex (for instance persistence), * \ref alpha_complex is equivalent to the \ref cech_complex and much smaller if you do not bound the radii. * \ref cech_complex can still make sense in higher dimension precisely because you can bound the radii. + * - For performances reasons, it is advised to use `Alpha_complex` with \ref cgal ≥ 5.0.0. * * \section pointsexample Example from points * diff --git a/src/common/doc/main_page.md b/src/common/doc/main_page.md index d8cbf97f..e8d11fdf 100644 --- a/src/common/doc/main_page.md +++ b/src/common/doc/main_page.md @@ -45,6 +45,7 @@ values of the codimension 1 cofaces that make it not Gabriel otherwise. All simplices that have a filtration value strictly greater than a given alpha squared value are not inserted into the complex.
+ For performances reasons, it is advised to use \ref cgal ≥ 5.0.0. Author: Vincent Rouvreau
-- cgit v1.2.3 From acd8a5a49083cd588aaa859dc4af104ce9090b22 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Wed, 6 Nov 2019 13:39:07 +0100 Subject: Update comment for GUDHI_PYTHON_MODULES_EXTRA --- src/python/gudhi/__init__.py.in | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/python/gudhi/__init__.py.in b/src/python/gudhi/__init__.py.in index 4039c08f..02888fff 100644 --- a/src/python/gudhi/__init__.py.in +++ b/src/python/gudhi/__init__.py.in @@ -26,7 +26,10 @@ __all__ = [@GUDHI_PYTHON_MODULES@ @GUDHI_PYTHON_MODULES_EXTRA@] __available_modules = '' __missing_modules = '' -# try to import * from gudhi.__module_name +# Try to import * from gudhi.__module_name for default modules. +# Extra modules require an explicit import by the user (mostly because of +# unusual dependencies, but also to avoid cluttering namespace gudhi and +# speed up the basic import) for __module_name in [@GUDHI_PYTHON_MODULES@]: try: __module = import_module('gudhi.' + __module_name) -- cgit v1.2.3 From 64cd8ab00b5868626137d4f7838741cdeb2c5d08 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Wed, 6 Nov 2019 16:03:00 +0100 Subject: Also document __init__ --- src/python/doc/sktda.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/python/doc/sktda.rst b/src/python/doc/sktda.rst index d36d9ab4..9acec616 100644 --- a/src/python/doc/sktda.rst +++ b/src/python/doc/sktda.rst @@ -10,26 +10,26 @@ Preprocessing ------------- .. automodule:: gudhi.sktda.preprocessing :members: - :undoc-members: + :special-members: :show-inheritance: Vector methods -------------- .. automodule:: gudhi.sktda.vector_methods :members: - :undoc-members: + :special-members: :show-inheritance: Kernel methods -------------- .. automodule:: gudhi.sktda.kernel_methods :members: - :undoc-members: + :special-members: :show-inheritance: Metrics ------- .. automodule:: gudhi.sktda.metrics :members: - :undoc-members: + :special-members: :show-inheritance: -- cgit v1.2.3 From 4ee6ed3caa55eb8e2aec9e4bd69bb8b4561dde7e Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 7 Nov 2019 16:31:16 +0100 Subject: Doc review : This remark is only available for the dD case, not for the 3d case. --- src/Alpha_complex/include/gudhi/Alpha_complex_3d.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h b/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h index cb9cb926..70e864e6 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h @@ -97,7 +97,7 @@ struct Value_from_iterator { * \details * The data structure is constructing a CGAL 3D Alpha * Shapes from a range of points (can be read from an OFF file, cf. Points_off_reader). - * Duplicate points are inserted once in the Alpha_complex. This is the reason why the vertices may be not contiguous. + * Duplicate points are inserted once in the Alpha_complex. * * \tparam Complexity shall be `Gudhi::alpha_complex::complexity` type. Default value is * `Gudhi::alpha_complex::complexity::SAFE`. -- cgit v1.2.3 From 98469d9e80a881ab4dae6358b7a7e64ce90784b2 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 7 Nov 2019 16:39:36 +0100 Subject: Code review: replace streambufffer with streambuffer - was copied/ pasted from several places --- src/Alpha_complex/example/Alpha_complex_from_off.cpp | 8 ++++---- src/Alpha_complex/example/Fast_alpha_complex_from_off.cpp | 8 ++++---- .../example_rips_complex_from_csv_distance_matrix_file.cpp | 8 ++++---- src/Rips_complex/example/example_rips_complex_from_off_file.cpp | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/Alpha_complex/example/Alpha_complex_from_off.cpp b/src/Alpha_complex/example/Alpha_complex_from_off.cpp index 6b5f1a74..220a66de 100644 --- a/src/Alpha_complex/example/Alpha_complex_from_off.cpp +++ b/src/Alpha_complex/example/Alpha_complex_from_off.cpp @@ -23,19 +23,19 @@ int main(int argc, char **argv) { // ---------------------------------------------------------------------------- Gudhi::alpha_complex::Alpha_complex<> alpha_complex_from_file(off_file_name); - std::streambuf* streambufffer; + std::streambuf* streambuffer; std::ofstream ouput_file_stream; if (argc == 4) { ouput_file_stream.open(std::string(argv[3])); - streambufffer = ouput_file_stream.rdbuf(); + streambuffer = ouput_file_stream.rdbuf(); } else { - streambufffer = std::cout.rdbuf(); + streambuffer = std::cout.rdbuf(); } Gudhi::Simplex_tree<> simplex; if (alpha_complex_from_file.create_complex(simplex, alpha_square_max_value)) { - std::ostream output_stream(streambufffer); + std::ostream output_stream(streambuffer); // ---------------------------------------------------------------------------- // Display information about the alpha complex diff --git a/src/Alpha_complex/example/Fast_alpha_complex_from_off.cpp b/src/Alpha_complex/example/Fast_alpha_complex_from_off.cpp index d6a39a76..f181005a 100644 --- a/src/Alpha_complex/example/Fast_alpha_complex_from_off.cpp +++ b/src/Alpha_complex/example/Fast_alpha_complex_from_off.cpp @@ -28,19 +28,19 @@ int main(int argc, char **argv) { // ---------------------------------------------------------------------------- Gudhi::alpha_complex::Alpha_complex alpha_complex_from_file(off_file_name); - std::streambuf* streambufffer; + std::streambuf* streambuffer; std::ofstream ouput_file_stream; if (argc == 4) { ouput_file_stream.open(std::string(argv[3])); - streambufffer = ouput_file_stream.rdbuf(); + streambuffer = ouput_file_stream.rdbuf(); } else { - streambufffer = std::cout.rdbuf(); + streambuffer = std::cout.rdbuf(); } Gudhi::Simplex_tree<> simplex; if (alpha_complex_from_file.create_complex(simplex, alpha_square_max_value)) { - std::ostream output_stream(streambufffer); + std::ostream output_stream(streambuffer); // ---------------------------------------------------------------------------- // Display information about the alpha complex diff --git a/src/Rips_complex/example/example_rips_complex_from_csv_distance_matrix_file.cpp b/src/Rips_complex/example/example_rips_complex_from_csv_distance_matrix_file.cpp index 9e182f1e..b7040453 100644 --- a/src/Rips_complex/example/example_rips_complex_from_csv_distance_matrix_file.cpp +++ b/src/Rips_complex/example/example_rips_complex_from_csv_distance_matrix_file.cpp @@ -35,19 +35,19 @@ int main(int argc, char **argv) { Distance_matrix distances = Gudhi::read_lower_triangular_matrix_from_csv_file(csv_file_name); Rips_complex rips_complex_from_file(distances, threshold); - std::streambuf* streambufffer; + std::streambuf* streambuffer; std::ofstream ouput_file_stream; if (argc == 5) { ouput_file_stream.open(std::string(argv[4])); - streambufffer = ouput_file_stream.rdbuf(); + streambuffer = ouput_file_stream.rdbuf(); } else { - streambufffer = std::cout.rdbuf(); + streambuffer = std::cout.rdbuf(); } Simplex_tree stree; rips_complex_from_file.create_complex(stree, dim_max); - std::ostream output_stream(streambufffer); + std::ostream output_stream(streambuffer); // ---------------------------------------------------------------------------- // Display information about the Rips complex diff --git a/src/Rips_complex/example/example_rips_complex_from_off_file.cpp b/src/Rips_complex/example/example_rips_complex_from_off_file.cpp index de2e4ea4..36b468a7 100644 --- a/src/Rips_complex/example/example_rips_complex_from_off_file.cpp +++ b/src/Rips_complex/example/example_rips_complex_from_off_file.cpp @@ -34,19 +34,19 @@ int main(int argc, char **argv) { Gudhi::Points_off_reader off_reader(off_file_name); Rips_complex rips_complex_from_file(off_reader.get_point_cloud(), threshold, Gudhi::Euclidean_distance()); - std::streambuf* streambufffer; + std::streambuf* streambuffer; std::ofstream ouput_file_stream; if (argc == 5) { ouput_file_stream.open(std::string(argv[4])); - streambufffer = ouput_file_stream.rdbuf(); + streambuffer = ouput_file_stream.rdbuf(); } else { - streambufffer = std::cout.rdbuf(); + streambuffer = std::cout.rdbuf(); } Simplex_tree stree; rips_complex_from_file.create_complex(stree, dim_max); - std::ostream output_stream(streambufffer); + std::ostream output_stream(streambuffer); // ---------------------------------------------------------------------------- // Display information about the Rips complex -- cgit v1.2.3 From ea296b4a93c37b0e19d4605c1be3e950ed8cd3c2 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 7 Nov 2019 16:45:28 +0100 Subject: Code review: it is with Epeck_d that it is advised to have CGAL >= 5.0. Error message clarification --- src/Alpha_complex/include/gudhi/Alpha_complex.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex.h b/src/Alpha_complex/include/gudhi/Alpha_complex.h index 22e17226..9aa30383 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex.h @@ -50,8 +50,8 @@ namespace Gudhi { namespace alpha_complex { -template struct Is_Epick_D { static const bool value = false; }; -template struct Is_Epick_D> { static const bool value = true; }; +template struct Is_Epeck_D { static const bool value = false; }; +template struct Is_Epeck_D> { static const bool value = true; }; /** * \class Alpha_complex Alpha_complex.h gudhi/Alpha_complex.h @@ -191,8 +191,9 @@ class Alpha_complex { template void init_from_range(const InputPointRange& points) { #if CGAL_VERSION_NR < 1050000000 - if (Is_Epick_D::value) - std::cerr << "It is strongly advised to use a CGAL version from 5.0 for performance reasons." << std::endl; + if (Is_Epeck_D::value) + std::cerr << "It is strongly advised to use a CGAL version >= 5.0 with Epeck_d Kernel for performance reasons." + << std::endl; #endif auto first = std::begin(points); -- cgit v1.2.3 From 5a2c3559da581136edb7facf5578bd1443080ef3 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 7 Nov 2019 17:02:38 +0100 Subject: Code review: Add suggested code using NT_converter to simplify the different alpha_complex_filtration computations cases --- src/Alpha_complex/include/gudhi/Alpha_complex.h | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex.h b/src/Alpha_complex/include/gudhi/Alpha_complex.h index 9aa30383..b3d1e6ea 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex.h @@ -25,6 +25,7 @@ #include #include // for CGAL::Identity_property_map #include // for CGAL_VERSION_NR +#include #include // for EIGEN_VERSION_AT_LEAST @@ -340,17 +341,12 @@ class Alpha_complex { // squared_radius function initialization Squared_Radius squared_radius = kernel_.compute_squared_radius_d_object(); -#if CGAL_VERSION_NR < 1050000000 - // With CGAL >= 4.11 and < 5.X, CGAL::exact do not work as it is always exact values - // This is why it is slow and 5.X is advised - alpha_complex_filtration = CGAL::to_double(squared_radius(pointVector.begin(), pointVector.end())); -#else // CGAL_VERSION_NR < 1050000000 - if (exact) { - alpha_complex_filtration = CGAL::to_double(CGAL::exact(squared_radius(pointVector.begin(), pointVector.end()))); - } else { - alpha_complex_filtration = CGAL::to_double(squared_radius(pointVector.begin(), pointVector.end())); - } -#endif //CGAL_VERSION_NR < 1050000000 + CGAL::NT_converter cv; + auto sqrad = squared_radius(pointVector.begin(), pointVector.end()); +#if CGAL_VERSION_NR >= 1050000000 + if(exact) CGAL::exact(sqrad); +#endif + alpha_complex_filtration = cv(sqrad); } complex.assign_filtration(f_simplex, alpha_complex_filtration); #ifdef DEBUG_TRACES -- cgit v1.2.3 From 7c80dd28eb16e70316e6acc0bde8f698f79b2003 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Fri, 8 Nov 2019 21:01:20 +0100 Subject: Enable the napoleon extension for sphinx. --- src/python/doc/conf.py | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/python/doc/conf.py b/src/python/doc/conf.py index e4c718c3..64d9cba1 100755 --- a/src/python/doc/conf.py +++ b/src/python/doc/conf.py @@ -39,6 +39,7 @@ extensions = [ 'sphinx.ext.mathjax', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode', + 'sphinx.ext.napoleon', 'sphinxcontrib.bibtex', ] -- cgit v1.2.3 From dc9771161ec5155030dad9301cd9af79f13005b3 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Fri, 8 Nov 2019 21:19:58 +0100 Subject: Move sktda to GUDHI_PYTHON_MODULES_EXTRA. --- src/python/CMakeLists.txt | 66 ++++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 2cc578a6..5e2207f0 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -49,8 +49,8 @@ if(PYTHONINTERP_FOUND) set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'alpha_complex', ") set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'euclidean_witness_complex', ") set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'euclidean_strong_witness_complex', ") - set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'sktda', ") # Modules that should not be auto-imported in __init__.py + set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'sktda', ") set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'wasserstein', ") add_gudhi_debug_info("Python version ${PYTHON_VERSION_STRING}") @@ -69,6 +69,7 @@ if(PYTHONINTERP_FOUND) endif() if(SKLEARN_FOUND) add_gudhi_debug_info("Scikit-learn version ${SKLEARN_VERSION}") + endif() if(OT_FOUND) add_gudhi_debug_info("POT version ${OT_VERSION}") endif() @@ -391,37 +392,42 @@ if(PYTHONINTERP_FOUND) if(MATPLOTLIB_FOUND) if(NUMPY_FOUND) if(SCIPY_FOUND) - if(OT_FOUND) - if(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) - set (GUDHI_SPHINX_MESSAGE "Generating API documentation with Sphinx in ${CMAKE_CURRENT_BINARY_DIR}/sphinx/") - # User warning - Sphinx is a static pages generator, and configured to work fine with user_version - # Images and biblio warnings because not found on developper version - if (GUDHI_PYTHON_PATH STREQUAL "src/python") - set (GUDHI_SPHINX_MESSAGE "${GUDHI_SPHINX_MESSAGE} \n WARNING : Sphinx is configured for user version, you run it on developper version. Images and biblio will miss") - endif() - # sphinx target requires gudhi.so, because conf.py reads gudhi version from it - add_custom_target(sphinx - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/doc - COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" - ${SPHINX_PATH} -b html ${CMAKE_CURRENT_SOURCE_DIR}/doc ${CMAKE_CURRENT_BINARY_DIR}/sphinx - DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/gudhi.so" - COMMENT "${GUDHI_SPHINX_MESSAGE}" VERBATIM) - - add_test(NAME sphinx_py_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" - ${SPHINX_PATH} -b doctest ${CMAKE_CURRENT_SOURCE_DIR}/doc ${CMAKE_CURRENT_BINARY_DIR}/doctest) - - # Set missing or not modules - set(GUDHI_MODULES ${GUDHI_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MODULES") - else(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) - message("++ Python documentation module will not be compiled because it requires a Eigen3 and CGAL version >= 4.11.0") + if(SKLEARN_FOUND) + if(OT_FOUND) + if(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) + set (GUDHI_SPHINX_MESSAGE "Generating API documentation with Sphinx in ${CMAKE_CURRENT_BINARY_DIR}/sphinx/") + # User warning - Sphinx is a static pages generator, and configured to work fine with user_version + # Images and biblio warnings because not found on developper version + if (GUDHI_PYTHON_PATH STREQUAL "src/python") + set (GUDHI_SPHINX_MESSAGE "${GUDHI_SPHINX_MESSAGE} \n WARNING : Sphinx is configured for user version, you run it on developper version. Images and biblio will miss") + endif() + # sphinx target requires gudhi.so, because conf.py reads gudhi version from it + add_custom_target(sphinx + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/doc + COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" + ${SPHINX_PATH} -b html ${CMAKE_CURRENT_SOURCE_DIR}/doc ${CMAKE_CURRENT_BINARY_DIR}/sphinx + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/gudhi.so" + COMMENT "${GUDHI_SPHINX_MESSAGE}" VERBATIM) + + add_test(NAME sphinx_py_test + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" + ${SPHINX_PATH} -b doctest ${CMAKE_CURRENT_SOURCE_DIR}/doc ${CMAKE_CURRENT_BINARY_DIR}/doctest) + + # Set missing or not modules + set(GUDHI_MODULES ${GUDHI_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MODULES") + else(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) + message("++ Python documentation module will not be compiled because it requires a Eigen3 and CGAL version >= 4.11.0") + set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MISSING_MODULES") + endif(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) + else(OT_FOUND) + message("++ Python documentation module will not be compiled because POT was not found") set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MISSING_MODULES") - endif(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) - else(OT_FOUND) - message("++ Python documentation module will not be compiled because POT was not found") + endif(OT_FOUND) + else(SKLEARN_FOUND) + message("++ Python documentation module will not be compiled because scikit-learn was not found") set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MISSING_MODULES") - endif(OT_FOUND) + endif(SKLEARN_FOUND) else(SCIPY_FOUND) message("++ Python documentation module will not be compiled because scipy was not found") set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MISSING_MODULES") -- cgit v1.2.3 From d091cf5369c2eba0bb301942842d56606a45af76 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sat, 9 Nov 2019 21:03:13 +0100 Subject: Replace 'is' with == or similar. Related to issue #113, but I did not touch persistence_graphical_tools.py which is handled in PR #119. I don't know if I got them all, I guess we'll start seeing the warnings with python-3.8 soon enough. --- src/python/gudhi/euclidean_strong_witness_complex.pyx | 2 +- src/python/gudhi/euclidean_witness_complex.pyx | 2 +- src/python/gudhi/off_reader.pyx | 2 +- src/python/gudhi/periodic_cubical_complex.pyx | 4 ++-- src/python/gudhi/reader_utils.pyx | 6 +++--- src/python/gudhi/strong_witness_complex.pyx | 2 +- src/python/gudhi/subsampling.pyx | 14 +++++++------- src/python/gudhi/tangential_complex.pyx | 2 +- src/python/gudhi/witness_complex.pyx | 2 +- 9 files changed, 18 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/euclidean_strong_witness_complex.pyx b/src/python/gudhi/euclidean_strong_witness_complex.pyx index 5d6e4fb9..0e5b544a 100644 --- a/src/python/gudhi/euclidean_strong_witness_complex.pyx +++ b/src/python/gudhi/euclidean_strong_witness_complex.pyx @@ -71,7 +71,7 @@ cdef class EuclideanStrongWitnessComplex: """ stree = SimplexTree() cdef intptr_t stree_int_ptr=stree.thisptr - if limit_dimension is not -1: + if limit_dimension != -1: self.thisptr.create_simplex_tree(stree_int_ptr, max_alpha_square, limit_dimension) else: diff --git a/src/python/gudhi/euclidean_witness_complex.pyx b/src/python/gudhi/euclidean_witness_complex.pyx index 2531919b..86d2d996 100644 --- a/src/python/gudhi/euclidean_witness_complex.pyx +++ b/src/python/gudhi/euclidean_witness_complex.pyx @@ -71,7 +71,7 @@ cdef class EuclideanWitnessComplex: """ stree = SimplexTree() cdef intptr_t stree_int_ptr=stree.thisptr - if limit_dimension is not -1: + if limit_dimension != -1: self.thisptr.create_simplex_tree(stree_int_ptr, max_alpha_square, limit_dimension) else: diff --git a/src/python/gudhi/off_reader.pyx b/src/python/gudhi/off_reader.pyx index 9efd97ff..27a89870 100644 --- a/src/python/gudhi/off_reader.pyx +++ b/src/python/gudhi/off_reader.pyx @@ -29,7 +29,7 @@ def read_off(off_file=''): :returns: The point set. :rtype: vector[vector[double]] """ - if off_file is not '': + if off_file: if os.path.isfile(off_file): return read_points_from_OFF_file(str.encode(off_file)) else: diff --git a/src/python/gudhi/periodic_cubical_complex.pyx b/src/python/gudhi/periodic_cubical_complex.pyx index 724fadd4..88223afb 100644 --- a/src/python/gudhi/periodic_cubical_complex.pyx +++ b/src/python/gudhi/periodic_cubical_complex.pyx @@ -68,9 +68,9 @@ cdef class PeriodicCubicalComplex: # The real cython constructor def __cinit__(self, dimensions=None, top_dimensional_cells=None, periodic_dimensions=None, perseus_file=''): - if (dimensions is not None) and (top_dimensional_cells is not None) and (periodic_dimensions is not None) and (perseus_file is ''): + if (dimensions is not None) and (top_dimensional_cells is not None) and (periodic_dimensions is not None) and (perseus_file == ''): self.thisptr = new Periodic_cubical_complex_base_interface(dimensions, top_dimensional_cells, periodic_dimensions) - elif (dimensions is None) and (top_dimensional_cells is None) and (periodic_dimensions is None) and (perseus_file is not ''): + elif (dimensions is None) and (top_dimensional_cells is None) and (periodic_dimensions is None) and (perseus_file != ''): if os.path.isfile(perseus_file): self.thisptr = new Periodic_cubical_complex_base_interface(str.encode(perseus_file)) else: diff --git a/src/python/gudhi/reader_utils.pyx b/src/python/gudhi/reader_utils.pyx index 147fae71..b266c783 100644 --- a/src/python/gudhi/reader_utils.pyx +++ b/src/python/gudhi/reader_utils.pyx @@ -37,7 +37,7 @@ def read_lower_triangular_matrix_from_csv_file(csv_file='', separator=';'): :returns: The lower triangular matrix. :rtype: vector[vector[double]] """ - if csv_file is not '': + if csv_file: if path.isfile(csv_file): return read_matrix_from_csv_file(str.encode(csv_file), ord(separator[0])) print("file " + csv_file + " not set or not found.") @@ -56,7 +56,7 @@ def read_persistence_intervals_grouped_by_dimension(persistence_file=''): :returns: The persistence pairs grouped by dimension. :rtype: map[int, vector[pair[double, double]]] """ - if persistence_file is not '': + if persistence_file: if path.isfile(persistence_file): return read_pers_intervals_grouped_by_dimension(str.encode(persistence_file)) print("file " + persistence_file + " not set or not found.") @@ -79,7 +79,7 @@ def read_persistence_intervals_in_dimension(persistence_file='', only_this_dim=- :returns: The persistence intervals. :rtype: numpy array of dimension 2 """ - if persistence_file is not '': + if persistence_file: if path.isfile(persistence_file): return np_array(read_pers_intervals_in_dimension(str.encode( persistence_file), only_this_dim)) diff --git a/src/python/gudhi/strong_witness_complex.pyx b/src/python/gudhi/strong_witness_complex.pyx index e757abea..e35ba6ad 100644 --- a/src/python/gudhi/strong_witness_complex.pyx +++ b/src/python/gudhi/strong_witness_complex.pyx @@ -69,7 +69,7 @@ cdef class StrongWitnessComplex: """ stree = SimplexTree() cdef intptr_t stree_int_ptr=stree.thisptr - if limit_dimension is not -1: + if limit_dimension != -1: self.thisptr.create_simplex_tree(stree_int_ptr, max_alpha_square, limit_dimension) else: diff --git a/src/python/gudhi/subsampling.pyx b/src/python/gudhi/subsampling.pyx index 1135c1fb..e622ac99 100644 --- a/src/python/gudhi/subsampling.pyx +++ b/src/python/gudhi/subsampling.pyx @@ -44,15 +44,15 @@ def choose_n_farthest_points(points=None, off_file='', nb_points=0, starting_poi :param nb_points: Number of points of the subsample. :type nb_points: unsigned. :param starting_point: The iteration starts with the landmark `starting \ - point`,which is the index of the poit to start with. If not set, this \ - index is choosen randomly. + point`,which is the index of the point to start with. If not set, this \ + index is chosen randomly. :type starting_point: unsigned. :returns: The subsample point set. :rtype: vector[vector[double]] """ - if off_file is not '': + if off_file: if os.path.isfile(off_file): - if starting_point is '': + if starting_point == '': return subsampling_n_farthest_points_from_file(str.encode(off_file), nb_points) else: @@ -65,7 +65,7 @@ def choose_n_farthest_points(points=None, off_file='', nb_points=0, starting_poi if points is None: # Empty points points=[] - if starting_point is '': + if starting_point == '': return subsampling_n_farthest_points(points, nb_points) else: return subsampling_n_farthest_points(points, nb_points, @@ -87,7 +87,7 @@ def pick_n_random_points(points=None, off_file='', nb_points=0): :returns: The subsample point set. :rtype: vector[vector[double]] """ - if off_file is not '': + if off_file: if os.path.isfile(off_file): return subsampling_n_random_points_from_file(str.encode(off_file), nb_points) @@ -117,7 +117,7 @@ def sparsify_point_set(points=None, off_file='', min_squared_dist=0.0): :returns: The subsample point set. :rtype: vector[vector[double]] """ - if off_file is not '': + if off_file: if os.path.isfile(off_file): return subsampling_sparsify_points_from_file(str.encode(off_file), min_squared_dist) diff --git a/src/python/gudhi/tangential_complex.pyx b/src/python/gudhi/tangential_complex.pyx index 3a945fe2..9ef15567 100644 --- a/src/python/gudhi/tangential_complex.pyx +++ b/src/python/gudhi/tangential_complex.pyx @@ -65,7 +65,7 @@ cdef class TangentialComplex: # The real cython constructor def __cinit__(self, intrisic_dim, points=None, off_file=''): - if off_file is not '': + if off_file: if os.path.isfile(off_file): self.thisptr = new Tangential_complex_interface(intrisic_dim, str.encode(off_file), True) else: diff --git a/src/python/gudhi/witness_complex.pyx b/src/python/gudhi/witness_complex.pyx index baa70b7a..c19ccf2b 100644 --- a/src/python/gudhi/witness_complex.pyx +++ b/src/python/gudhi/witness_complex.pyx @@ -69,7 +69,7 @@ cdef class WitnessComplex: """ stree = SimplexTree() cdef intptr_t stree_int_ptr=stree.thisptr - if limit_dimension is not -1: + if limit_dimension != -1: self.thisptr.create_simplex_tree(stree_int_ptr, max_alpha_square, limit_dimension) else: -- cgit v1.2.3 From 1e5d0ca3f84f1e5c132014d3cfc96bfe0aa3d7ee Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sat, 9 Nov 2019 21:41:06 +0100 Subject: Empty complexes have 0 (not -1) Betti numbers --- src/Persistent_cohomology/include/gudhi/Persistent_cohomology.h | 8 ++++++-- src/Persistent_cohomology/test/betti_numbers_unit_test.cpp | 9 +++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/Persistent_cohomology/include/gudhi/Persistent_cohomology.h b/src/Persistent_cohomology/include/gudhi/Persistent_cohomology.h index 944b6d35..0f1876d0 100644 --- a/src/Persistent_cohomology/include/gudhi/Persistent_cohomology.h +++ b/src/Persistent_cohomology/include/gudhi/Persistent_cohomology.h @@ -600,8 +600,10 @@ class Persistent_cohomology { * @return A vector of Betti numbers. */ std::vector betti_numbers() const { + // Don't allocate a vector of negative size for an empty complex + int siz = std::max(dim_max_, 0); // Init Betti numbers vector with zeros until Simplicial complex dimension - std::vector betti_numbers(dim_max_, 0); + std::vector betti_numbers(siz); for (auto pair : persistent_pairs_) { // Count never ended persistence intervals @@ -639,8 +641,10 @@ class Persistent_cohomology { * @return A vector of persistent Betti numbers. */ std::vector persistent_betti_numbers(Filtration_value from, Filtration_value to) const { + // Don't allocate a vector of negative size for an empty complex + int siz = std::max(dim_max_, 0); // Init Betti numbers vector with zeros until Simplicial complex dimension - std::vector betti_numbers(dim_max_, 0); + std::vector betti_numbers(siz); for (auto pair : persistent_pairs_) { // Count persistence intervals that covers the given interval // null_simplex test : if the function is called with to=+infinity, we still get something useful. And it will diff --git a/src/Persistent_cohomology/test/betti_numbers_unit_test.cpp b/src/Persistent_cohomology/test/betti_numbers_unit_test.cpp index 0a08d200..b9f11607 100644 --- a/src/Persistent_cohomology/test/betti_numbers_unit_test.cpp +++ b/src/Persistent_cohomology/test/betti_numbers_unit_test.cpp @@ -284,4 +284,13 @@ BOOST_AUTO_TEST_CASE( betti_numbers ) auto intervals_in_dimension_2 = pcoh.intervals_in_dimension(2); std::cout << "intervals_in_dimension_2.size() = " << intervals_in_dimension_2.size() << std::endl; BOOST_CHECK(intervals_in_dimension_2.size() == 0); + + std::cout << "EMPTY COMPLEX" << std::endl; + Simplex_tree empty; + empty.initialize_filtration(); + St_persistence pcoh_empty(empty, false); + pcoh_empty.init_coefficients(2); + pcoh_empty.compute_persistent_cohomology(); + BOOST_CHECK(pcoh_empty.betti_numbers().size() == 0); + BOOST_CHECK(pcoh_empty.persistent_betti_numbers(0,1).size() == 0); } -- cgit v1.2.3 From 5372648ccc80e9754e1d2057c5bdba9724810da5 Mon Sep 17 00:00:00 2001 From: mathieu Date: Sat, 9 Nov 2019 21:01:44 -0500 Subject: added copyrights --- src/python/gudhi/sktda/kernel_methods.py | 13 +++++++++---- src/python/gudhi/sktda/metrics.py | 11 ++++++++--- src/python/gudhi/sktda/preprocessing.py | 11 ++++++++--- src/python/gudhi/sktda/vector_methods.py | 11 ++++++++--- 4 files changed, 33 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/sktda/kernel_methods.py b/src/python/gudhi/sktda/kernel_methods.py index b8d4ab3a..0a0d333d 100644 --- a/src/python/gudhi/sktda/kernel_methods.py +++ b/src/python/gudhi/sktda/kernel_methods.py @@ -1,6 +1,11 @@ -""" -@author: Mathieu Carriere -All rights reserved +""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + Author(s): Mathieu Carrière + + Copyright (C) 2018-2019 Inria + + Modification(s): + - YYYY/MM Author: Description of the modification """ import numpy as np @@ -140,7 +145,7 @@ class PersistenceScaleSpaceKernel(BaseEstimator, TransformerMixin): """ self.diagrams_ = list(X) for i in range(len(self.diagrams_)): - op_D = np.matmul(self.diagrams_[i], np.array([[0.,1.], [1.,0.]])) + op_D = self.diagrams_[i][:,[1,0]] self.diagrams_[i] = np.concatenate([self.diagrams_[i], op_D], axis=0) self.pwg_.fit(X) return self diff --git a/src/python/gudhi/sktda/metrics.py b/src/python/gudhi/sktda/metrics.py index e85fd14d..cc461cbd 100644 --- a/src/python/gudhi/sktda/metrics.py +++ b/src/python/gudhi/sktda/metrics.py @@ -1,6 +1,11 @@ -""" -@author: Mathieu Carriere -All rights reserved +""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + Author(s): Mathieu Carrière + + Copyright (C) 2018-2019 Inria + + Modification(s): + - YYYY/MM Author: Description of the modification """ import numpy as np diff --git a/src/python/gudhi/sktda/preprocessing.py b/src/python/gudhi/sktda/preprocessing.py index 784e300f..8c0ca2c4 100644 --- a/src/python/gudhi/sktda/preprocessing.py +++ b/src/python/gudhi/sktda/preprocessing.py @@ -1,6 +1,11 @@ -""" -@author: Mathieu Carriere -All rights reserved +""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + Author(s): Mathieu Carrière + + Copyright (C) 2018-2019 Inria + + Modification(s): + - YYYY/MM Author: Description of the modification """ import numpy as np diff --git a/src/python/gudhi/sktda/vector_methods.py b/src/python/gudhi/sktda/vector_methods.py index 42ded45f..2b72b173 100644 --- a/src/python/gudhi/sktda/vector_methods.py +++ b/src/python/gudhi/sktda/vector_methods.py @@ -1,6 +1,11 @@ -""" -@author: Mathieu Carriere -All rights reserved +""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + Author(s): Mathieu Carrière + + Copyright (C) 2018-2019 Inria + + Modification(s): + - YYYY/MM Author: Description of the modification """ import numpy as np -- cgit v1.2.3 From 0dd2d14094075f8b4a10b9f569996e2ad271cd6c Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Thu, 14 Nov 2019 12:00:17 +0100 Subject: Add link from the main doc page. --- src/python/doc/index.rst | 5 +++++ src/python/doc/sktda_sum.inc | 14 ++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 src/python/doc/sktda_sum.inc (limited to 'src') diff --git a/src/python/doc/index.rst b/src/python/doc/index.rst index 8f27da0d..f8dd3c2f 100644 --- a/src/python/doc/index.rst +++ b/src/python/doc/index.rst @@ -78,6 +78,11 @@ Wasserstein distance .. include:: wasserstein_distance_sum.inc +Persistence representations +=========================== + +.. include:: sktda_sum.inc + Persistence graphical tools =========================== diff --git a/src/python/doc/sktda_sum.inc b/src/python/doc/sktda_sum.inc new file mode 100644 index 00000000..a0f77e7e --- /dev/null +++ b/src/python/doc/sktda_sum.inc @@ -0,0 +1,14 @@ +.. table:: + :widths: 30 50 20 + + +------------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------------------+ + | .. figure:: | Vectorizations, distances and kernels that work on persistence | :Author: Mathieu Carrière | + | ../../doc/Persistence_representations/average_landscape.png | diagrams, compatible with scikit-learn. | | + | | | :Introduced in: GUDHI 3.1.0 | + | | | | + | | | :Copyright: MIT | + | | | | + | | | :Requires: scikit-learn | + +------------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------------------+ + | * :doc:`sktda` | + +------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------+ -- cgit v1.2.3 From 0fa77a12509ea6d51043569b592bad855f276a45 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Thu, 14 Nov 2019 13:26:33 +0100 Subject: Make the license a comment instead of a docstring We don't want it in the autogenerated sphinx doc --- src/python/gudhi/sktda/kernel_methods.py | 17 ++++++++--------- src/python/gudhi/sktda/metrics.py | 17 ++++++++--------- src/python/gudhi/sktda/preprocessing.py | 17 ++++++++--------- src/python/gudhi/sktda/vector_methods.py | 17 ++++++++--------- 4 files changed, 32 insertions(+), 36 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/sktda/kernel_methods.py b/src/python/gudhi/sktda/kernel_methods.py index 0a0d333d..24067718 100644 --- a/src/python/gudhi/sktda/kernel_methods.py +++ b/src/python/gudhi/sktda/kernel_methods.py @@ -1,12 +1,11 @@ -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Mathieu Carrière - - Copyright (C) 2018-2019 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Mathieu Carrière +# +# Copyright (C) 2018-2019 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification import numpy as np from sklearn.base import BaseEstimator, TransformerMixin diff --git a/src/python/gudhi/sktda/metrics.py b/src/python/gudhi/sktda/metrics.py index cc461cbd..b8acf261 100644 --- a/src/python/gudhi/sktda/metrics.py +++ b/src/python/gudhi/sktda/metrics.py @@ -1,12 +1,11 @@ -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Mathieu Carrière - - Copyright (C) 2018-2019 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Mathieu Carrière +# +# Copyright (C) 2018-2019 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification import numpy as np from sklearn.base import BaseEstimator, TransformerMixin diff --git a/src/python/gudhi/sktda/preprocessing.py b/src/python/gudhi/sktda/preprocessing.py index 8c0ca2c4..6bc7ec52 100644 --- a/src/python/gudhi/sktda/preprocessing.py +++ b/src/python/gudhi/sktda/preprocessing.py @@ -1,12 +1,11 @@ -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Mathieu Carrière - - Copyright (C) 2018-2019 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Mathieu Carrière +# +# Copyright (C) 2018-2019 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification import numpy as np from sklearn.base import BaseEstimator, TransformerMixin diff --git a/src/python/gudhi/sktda/vector_methods.py b/src/python/gudhi/sktda/vector_methods.py index 2b72b173..c1824ebb 100644 --- a/src/python/gudhi/sktda/vector_methods.py +++ b/src/python/gudhi/sktda/vector_methods.py @@ -1,12 +1,11 @@ -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Mathieu Carrière - - Copyright (C) 2018-2019 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Mathieu Carrière +# +# Copyright (C) 2018-2019 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification import numpy as np from sklearn.base import BaseEstimator, TransformerMixin -- cgit v1.2.3 From 8427713bc748bc040dd696a64d81b3fe6f648a07 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Thu, 14 Nov 2019 13:37:08 +0100 Subject: Remove mentions of the exact sliced Wasserstein (not implemented) --- src/python/gudhi/sktda/kernel_methods.py | 2 +- src/python/gudhi/sktda/metrics.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/sktda/kernel_methods.py b/src/python/gudhi/sktda/kernel_methods.py index 24067718..d409accd 100644 --- a/src/python/gudhi/sktda/kernel_methods.py +++ b/src/python/gudhi/sktda/kernel_methods.py @@ -26,7 +26,7 @@ class SlicedWassersteinKernel(BaseEstimator, TransformerMixin): Attributes: bandwidth (double): bandwidth of the Gaussian kernel applied to the sliced Wasserstein distance (default 1.). - num_directions (int): number of lines evenly sampled from [-pi/2,pi/2] in order to approximate and speed up the kernel computation (default 10). If -1, the exact kernel is computed. + num_directions (int): number of lines evenly sampled from [-pi/2,pi/2] in order to approximate and speed up the kernel computation (default 10). """ self.bandwidth = bandwidth self.sw_ = SlicedWassersteinDistance(num_directions=num_directions) diff --git a/src/python/gudhi/sktda/metrics.py b/src/python/gudhi/sktda/metrics.py index b8acf261..f55f553b 100644 --- a/src/python/gudhi/sktda/metrics.py +++ b/src/python/gudhi/sktda/metrics.py @@ -15,7 +15,7 @@ try: USE_GUDHI = True except ImportError: USE_GUDHI = False - print("Gudhi not found: BottleneckDistance will return null matrix, and exact SlicedWassersteinDistance not available") + print("Gudhi built without CGAL: BottleneckDistance will return a null matrix") ############################################# # Metrics ################################### -- cgit v1.2.3 From 3b58332d4f5849dd05ee08d8a222ca0fe9475832 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Thu, 14 Nov 2019 13:54:35 +0100 Subject: Syntax of return type in docstring --- src/python/gudhi/sktda/kernel_methods.py | 8 ++++---- src/python/gudhi/sktda/metrics.py | 6 +++--- src/python/gudhi/sktda/preprocessing.py | 12 ++++++------ src/python/gudhi/sktda/vector_methods.py | 14 +++++++------- 4 files changed, 20 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/sktda/kernel_methods.py b/src/python/gudhi/sktda/kernel_methods.py index d409accd..e93138e6 100644 --- a/src/python/gudhi/sktda/kernel_methods.py +++ b/src/python/gudhi/sktda/kernel_methods.py @@ -50,7 +50,7 @@ class SlicedWassersteinKernel(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise sliced Wasserstein kernel values. + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise sliced Wasserstein kernel values. """ return np.exp(-self.sw_.transform(X)/self.bandwidth) @@ -92,7 +92,7 @@ class PersistenceWeightedGaussianKernel(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence weighted Gaussian kernel values. + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise persistence weighted Gaussian kernel values. """ Xp = list(X) Xfit = np.zeros((len(Xp), len(self.diagrams_))) @@ -157,7 +157,7 @@ class PersistenceScaleSpaceKernel(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence scale space kernel values. + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise persistence scale space kernel values. """ Xp = list(X) for i in range(len(Xp)): @@ -200,7 +200,7 @@ class PersistenceFisherKernel(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence Fisher kernel values. + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise persistence Fisher kernel values. """ return np.exp(-self.pf_.transform(X)/self.bandwidth) diff --git a/src/python/gudhi/sktda/metrics.py b/src/python/gudhi/sktda/metrics.py index f55f553b..c51b8f3b 100644 --- a/src/python/gudhi/sktda/metrics.py +++ b/src/python/gudhi/sktda/metrics.py @@ -58,7 +58,7 @@ class SlicedWassersteinDistance(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise sliced Wasserstein distances. + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise sliced Wasserstein distances. """ Xfit = np.zeros((len(X), len(self.approx_))) if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): @@ -114,7 +114,7 @@ class BottleneckDistance(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise bottleneck distances. + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise bottleneck distances. """ num_diag1 = len(X) @@ -182,7 +182,7 @@ class PersistenceFisherDistance(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence Fisher distances. + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise persistence Fisher distances. """ Xfit = np.zeros((len(X), len(self.diagrams_))) if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): diff --git a/src/python/gudhi/sktda/preprocessing.py b/src/python/gudhi/sktda/preprocessing.py index 6bc7ec52..294b5aeb 100644 --- a/src/python/gudhi/sktda/preprocessing.py +++ b/src/python/gudhi/sktda/preprocessing.py @@ -43,7 +43,7 @@ class BirthPersistenceTransform(BaseEstimator, TransformerMixin): X (list of n x 2 numpy array): input persistence diagrams. Returns: - Xfit (list of n x 2 numpy array): transformed persistence diagrams. + list of n x 2 numpy array: transformed persistence diagrams. """ Xfit = [] for diag in X: @@ -85,7 +85,7 @@ class Clamping(BaseEstimator, TransformerMixin): X (numpy array of size n): input list of values. Returns: - Xfit (numpy array of size n): output list of values. + numpy array of size n: output list of values. """ Xfit = np.minimum(X, self.limit) #Xfit = np.where(X >= self.limit, self.limit * np.ones(X.shape), X) @@ -131,7 +131,7 @@ class DiagramScaler(BaseEstimator, TransformerMixin): X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. Returns: - Xfit (list of n x 2 or n x 1 numpy arrays): transformed persistence diagrams. + list of n x 2 or n x 1 numpy arrays: transformed persistence diagrams. """ Xfit = [np.copy(d) for d in X] if self.use: @@ -174,7 +174,7 @@ class Padding(BaseEstimator, TransformerMixin): X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. Returns: - Xfit (list of n x 3 or n x 2 numpy arrays): padded persistence diagrams. + list of n x 3 or n x 2 numpy arrays: padded persistence diagrams. """ if self.use: Xfit, num_diag = [], len(X) @@ -223,7 +223,7 @@ class ProminentPoints(BaseEstimator, TransformerMixin): X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. Returns: - Xfit (list of n x 2 or n x 1 numpy arrays): thresholded persistence diagrams. + list of n x 2 or n x 1 numpy arrays: thresholded persistence diagrams. """ if self.use: Xfit, num_diag = [], len(X) @@ -292,7 +292,7 @@ class DiagramSelector(BaseEstimator, TransformerMixin): X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. Returns: - Xfit (list of n x 2 or n x 1 numpy arrays): extracted persistence diagrams. + list of n x 2 or n x 1 numpy arrays: extracted persistence diagrams. """ if self.use: Xfit, num_diag = [], len(X) diff --git a/src/python/gudhi/sktda/vector_methods.py b/src/python/gudhi/sktda/vector_methods.py index c1824ebb..91f1bc31 100644 --- a/src/python/gudhi/sktda/vector_methods.py +++ b/src/python/gudhi/sktda/vector_methods.py @@ -58,7 +58,7 @@ class PersistenceImage(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array with shape (number of diagrams) x (number of pixels = **resolution[0]** x **resolution[1]**)): output persistence images. + numpy array with shape (number of diagrams) x (number of pixels = **resolution[0]** x **resolution[1]**): output persistence images. """ num_diag, Xfit = len(X), [] new_X = BirthPersistenceTransform().fit_transform(X) @@ -118,7 +118,7 @@ class Landscape(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array with shape (number of diagrams) x (number of samples = **num_landscapes** x **resolution**)): output persistence landscapes. + numpy array with shape (number of diagrams) x (number of samples = **num_landscapes** x **resolution**): output persistence landscapes. """ num_diag, Xfit = len(X), [] x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) @@ -200,7 +200,7 @@ class Silhouette(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array with shape (number of diagrams) x (**resolution**): output persistence silhouettes. + numpy array with shape (number of diagrams) x (**resolution**): output persistence silhouettes. """ num_diag, Xfit = len(X), [] x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) @@ -277,7 +277,7 @@ class BettiCurve(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array with shape (number of diagrams) x (**resolution**): output Betti curves. + numpy array with shape (number of diagrams) x (**resolution**): output Betti curves. """ num_diag, Xfit = len(X), [] x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) @@ -339,7 +339,7 @@ class Entropy(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array with shape (number of diagrams) x (1 if **mode** = "scalar" else **resolution**)): output entropy. + numpy array with shape (number of diagrams) x (1 if **mode** = "scalar" else **resolution**): output entropy. """ num_diag, Xfit = len(X), [] x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) @@ -402,7 +402,7 @@ class TopologicalVector(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array with shape (number of diagrams) x (**threshold**): output topological vectors. + numpy array with shape (number of diagrams) x (**threshold**): output topological vectors. """ if self.threshold == -1: thresh = np.array([X[i].shape[0] for i in range(len(X))]).max() @@ -456,7 +456,7 @@ class ComplexPolynomial(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array with shape (number of diagrams) x (**threshold**): output complex vectors of coefficients. + numpy array with shape (number of diagrams) x (**threshold**): output complex vectors of coefficients. """ if self.threshold == -1: thresh = np.array([X[i].shape[0] for i in range(len(X))]).max() -- cgit v1.2.3 From a043bee376c25254bd413c5579e9611aa57dbf47 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Thu, 14 Nov 2019 14:07:06 +0100 Subject: Attributes -> Parameters in __init__ --- src/python/gudhi/sktda/kernel_methods.py | 8 ++++---- src/python/gudhi/sktda/metrics.py | 6 +++--- src/python/gudhi/sktda/preprocessing.py | 10 +++++----- src/python/gudhi/sktda/vector_methods.py | 14 +++++++------- 4 files changed, 19 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/sktda/kernel_methods.py b/src/python/gudhi/sktda/kernel_methods.py index e93138e6..c855d2be 100644 --- a/src/python/gudhi/sktda/kernel_methods.py +++ b/src/python/gudhi/sktda/kernel_methods.py @@ -24,7 +24,7 @@ class SlicedWassersteinKernel(BaseEstimator, TransformerMixin): """ Constructor for the SlicedWassersteinKernel class. - Attributes: + Parameters: bandwidth (double): bandwidth of the Gaussian kernel applied to the sliced Wasserstein distance (default 1.). num_directions (int): number of lines evenly sampled from [-pi/2,pi/2] in order to approximate and speed up the kernel computation (default 10). """ @@ -62,7 +62,7 @@ class PersistenceWeightedGaussianKernel(BaseEstimator, TransformerMixin): """ Constructor for the PersistenceWeightedGaussianKernel class. - Attributes: + Parameters: bandwidth (double): bandwidth of the Gaussian kernel with which persistence diagrams will be convolved (default 1.) weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie lists or numpy arrays of the form [p_x,p_y]. kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). @@ -128,7 +128,7 @@ class PersistenceScaleSpaceKernel(BaseEstimator, TransformerMixin): """ Constructor for the PersistenceScaleSpaceKernel class. - Attributes: + Parameters: bandwidth (double): bandwidth of the Gaussian kernel with which persistence diagrams will be convolved (default 1.) kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). """ @@ -173,7 +173,7 @@ class PersistenceFisherKernel(BaseEstimator, TransformerMixin): """ Constructor for the PersistenceFisherKernel class. - Attributes: + Parameters: bandwidth (double): bandwidth of the Gaussian kernel applied to the persistence Fisher distance (default 1.). bandwidth_fisher (double): bandwidth of the Gaussian kernel used to turn persistence diagrams into probability distributions by PersistenceFisherDistance class (default 1.). kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). diff --git a/src/python/gudhi/sktda/metrics.py b/src/python/gudhi/sktda/metrics.py index c51b8f3b..c512cb82 100644 --- a/src/python/gudhi/sktda/metrics.py +++ b/src/python/gudhi/sktda/metrics.py @@ -29,7 +29,7 @@ class SlicedWassersteinDistance(BaseEstimator, TransformerMixin): """ Constructor for the SlicedWassersteinDistance class. - Attributes: + Parameters: num_directions (int): number of lines evenly sampled from [-pi/2,pi/2] in order to approximate and speed up the distance computation (default 10). """ self.num_directions = num_directions @@ -90,7 +90,7 @@ class BottleneckDistance(BaseEstimator, TransformerMixin): """ Constructor for the BottleneckDistance class. - Attributes: + Parameters: epsilon (double): approximation quality (default 1e-4). """ self.epsilon = epsilon @@ -152,7 +152,7 @@ class PersistenceFisherDistance(BaseEstimator, TransformerMixin): """ Constructor for the PersistenceFisherDistance class. - Attributes: + Parameters: bandwidth (double): bandwidth of the Gaussian kernel used to turn persistence diagrams into probability distributions (default 1.). kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). """ diff --git a/src/python/gudhi/sktda/preprocessing.py b/src/python/gudhi/sktda/preprocessing.py index 294b5aeb..83227ca1 100644 --- a/src/python/gudhi/sktda/preprocessing.py +++ b/src/python/gudhi/sktda/preprocessing.py @@ -62,7 +62,7 @@ class Clamping(BaseEstimator, TransformerMixin): """ Constructor for the Clamping class. - Attributes: + Parameters: limit (double): clamping value (default np.inf). """ self.limit = limit @@ -99,7 +99,7 @@ class DiagramScaler(BaseEstimator, TransformerMixin): """ Constructor for the DiagramScaler class. - Attributes: + Parameters: use (bool): whether to use the class or not (default False). scalers (list of classes): list of scalers to be fit on the persistence diagrams (default []). Each element of the list is a tuple with two elements: the first one is a list of coordinates, and the second one is a scaler (i.e. a class with fit() and transform() methods) that is going to be applied to these coordinates. Common scalers can be found in the scikit-learn library (such as MinMaxScaler for instance). """ @@ -150,7 +150,7 @@ class Padding(BaseEstimator, TransformerMixin): """ Constructor for the Padding class. - Attributes: + Parameters: use (bool): whether to use the class or not (default False). """ self.use = use @@ -194,7 +194,7 @@ class ProminentPoints(BaseEstimator, TransformerMixin): """ Constructor for the ProminentPoints class. - Attributes: + Parameters: use (bool): whether to use the class or not (default False). location (string): either "upper" or "lower" (default "upper"). Whether to keep the points that are far away ("upper") or close ("lower") to the diagonal. num_pts (int): cardinality threshold (default 10). If location == "upper", keep the top **num_pts** points that are the farthest away from the diagonal. If location == "lower", keep the top **num_pts** points that are the closest to the diagonal. @@ -267,7 +267,7 @@ class DiagramSelector(BaseEstimator, TransformerMixin): """ Constructor for the DiagramSelector class. - Attributes: + Parameters: use (bool): whether to use the class or not (default False). limit (double): second coordinate value that is the criterion for being an essential point (default numpy.inf). point_type (string): either "finite" or "essential". The type of the points that are going to be extracted. diff --git a/src/python/gudhi/sktda/vector_methods.py b/src/python/gudhi/sktda/vector_methods.py index 91f1bc31..bf32f18e 100644 --- a/src/python/gudhi/sktda/vector_methods.py +++ b/src/python/gudhi/sktda/vector_methods.py @@ -26,7 +26,7 @@ class PersistenceImage(BaseEstimator, TransformerMixin): """ Constructor for the PersistenceImage class. - Attributes: + Parameters: bandwidth (double): bandwidth of the Gaussian kernel (default 1.). weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie lists or numpy arrays of the form [p_x,p_y]. resolution ([int,int]): size (in pixels) of the persistence image (default [20,20]). @@ -89,7 +89,7 @@ class Landscape(BaseEstimator, TransformerMixin): """ Constructor for the Landscape class. - Attributes: + Parameters: num_landscapes (int): number of piecewise-linear functions to output (default 5). resolution (int): number of sample for all piecewise-linear functions (default 100). sample_range ([double, double]): minimum and maximum of all piecewise-linear function domains, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. @@ -171,7 +171,7 @@ class Silhouette(BaseEstimator, TransformerMixin): """ Constructor for the Silhouette class. - Attributes: + Parameters: weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie on lists or numpy arrays of the form [p_x,p_y]. resolution (int): number of samples for the weighted average (default 100). sample_range ([double, double]): minimum and maximum for the weighted average domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. @@ -249,7 +249,7 @@ class BettiCurve(BaseEstimator, TransformerMixin): """ Constructor for the BettiCurve class. - Attributes: + Parameters: resolution (int): number of sample for the piecewise-constant function (default 100). sample_range ([double, double]): minimum and maximum of the piecewise-constant function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. """ @@ -309,7 +309,7 @@ class Entropy(BaseEstimator, TransformerMixin): """ Constructor for the Entropy class. - Attributes: + Parameters: mode (string): what entropy to compute: either "scalar" for computing the entropy statistics, or "vector" for computing the entropy summary functions (default "scalar"). normalized (bool): whether to normalize the entropy summary function (default True). Used only if **mode** = "vector". resolution (int): number of sample for the entropy summary function (default 100). Used only if **mode** = "vector". @@ -379,7 +379,7 @@ class TopologicalVector(BaseEstimator, TransformerMixin): """ Constructor for the TopologicalVector class. - Attributes: + Parameters: threshold (int): number of distances to keep (default 10). This is the dimension of the topological vector. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding topological vector as threshold. """ self.threshold = threshold @@ -432,7 +432,7 @@ class ComplexPolynomial(BaseEstimator, TransformerMixin): """ Constructor for the ComplexPolynomial class. - Attributes: + Parameters: polynomial_type (char): either "R", "S" or "T" (default "R"). Type of complex polynomial that is going to be computed (explained in https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27). threshold (int): number of coefficients (default 10). This is the dimension of the complex vector of coefficients, i.e. the number of coefficients corresponding to the largest degree terms of the polynomial. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding complex vector of coefficients as threshold. """ -- cgit v1.2.3 From b2d81dd8ee2ed7e1269eb16816f9af6794305046 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Fri, 15 Nov 2019 22:23:21 +0100 Subject: Run example as a test, without graphics --- src/python/CMakeLists.txt | 5 + .../diagram_vectorizations_distances_kernels.py | 133 +++++++++++++++++++++ src/python/example/ex_diagrams.py | 133 --------------------- src/python/test/test_representations.py | 11 ++ 4 files changed, 149 insertions(+), 133 deletions(-) create mode 100755 src/python/example/diagram_vectorizations_distances_kernels.py delete mode 100755 src/python/example/ex_diagrams.py create mode 100755 src/python/test/test_representations.py (limited to 'src') diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 5e2207f0..d131b920 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -387,6 +387,11 @@ if(PYTHONINTERP_FOUND) add_gudhi_py_test(test_wasserstein_distance) endif(OT_FOUND) + # Representations + if(SKLEARN_FOUND) + add_gudhi_py_test(test_representations) + endif(SKLEARN_FOUND) + # Documentation generation is available through sphinx - requires all modules if(SPHINX_PATH) if(MATPLOTLIB_FOUND) diff --git a/src/python/example/diagram_vectorizations_distances_kernels.py b/src/python/example/diagram_vectorizations_distances_kernels.py new file mode 100755 index 00000000..a6a36b7c --- /dev/null +++ b/src/python/example/diagram_vectorizations_distances_kernels.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python + +import matplotlib.pyplot as plt +import numpy as np +from sklearn.kernel_approximation import RBFSampler +from sklearn.preprocessing import MinMaxScaler + +from gudhi.sktda import DiagramSelector, Clamping, Landscape, Silhouette, BettiCurve, ComplexPolynomial,\ + TopologicalVector, DiagramScaler, BirthPersistenceTransform,\ + PersistenceImage, PersistenceWeightedGaussianKernel, Entropy, \ + PersistenceScaleSpaceKernel, SlicedWassersteinDistance,\ + SlicedWassersteinKernel, BottleneckDistance, PersistenceFisherKernel + +D = np.array([[0.,4.],[1.,2.],[3.,8.],[6.,8.], [0., np.inf], [5., np.inf]]) +diags = [D] + +diags = DiagramSelector(use=True, point_type="finite").fit_transform(diags) +diags = DiagramScaler(use=True, scalers=[([0,1], MinMaxScaler())]).fit_transform(diags) +diags = DiagramScaler(use=True, scalers=[([1], Clamping(limit=.9))]).fit_transform(diags) + +D = diags[0] +plt.scatter(D[:,0],D[:,1]) +plt.plot([0.,1.],[0.,1.]) +plt.title("Test Persistence Diagram for vector methods") +plt.show() + +LS = Landscape(resolution=1000) +L = LS.fit_transform(diags) +plt.plot(L[0][:1000]) +plt.plot(L[0][1000:2000]) +plt.plot(L[0][2000:3000]) +plt.title("Landscape") +plt.show() + +def pow(n): + return lambda x: np.power(x[1]-x[0],n) + +SH = Silhouette(resolution=1000, weight=pow(2)) +sh = SH.fit_transform(diags) +plt.plot(sh[0]) +plt.title("Silhouette") +plt.show() + +BC = BettiCurve(resolution=1000) +bc = BC.fit_transform(diags) +plt.plot(bc[0]) +plt.title("Betti Curve") +plt.show() + +CP = ComplexPolynomial(threshold=-1, polynomial_type="T") +cp = CP.fit_transform(diags) +print("Complex polynomial is " + str(cp[0,:])) + +TV = TopologicalVector(threshold=-1) +tv = TV.fit_transform(diags) +print("Topological vector is " + str(tv[0,:])) + +PI = PersistenceImage(bandwidth=.1, weight=lambda x: x[1], im_range=[0,1,0,1], resolution=[100,100]) +pi = PI.fit_transform(diags) +plt.imshow(np.flip(np.reshape(pi[0], [100,100]), 0)) +plt.title("Persistence Image") +plt.show() + +ET = Entropy(mode="scalar") +et = ET.fit_transform(diags) +print("Entropy statistic is " + str(et[0,:])) + +ET = Entropy(mode="vector", normalized=False) +et = ET.fit_transform(diags) +plt.plot(et[0]) +plt.title("Entropy function") +plt.show() + +D = np.array([[1.,5.],[3.,6.],[2.,7.]]) +diags2 = [D] + +diags2 = DiagramScaler(use=True, scalers=[([0,1], MinMaxScaler())]).fit_transform(diags2) + +D = diags[0] +plt.scatter(D[:,0],D[:,1]) +D = diags2[0] +plt.scatter(D[:,0],D[:,1]) +plt.plot([0.,1.],[0.,1.]) +plt.title("Test Persistence Diagrams for kernel methods") +plt.show() + +def arctan(C,p): + return lambda x: C*np.arctan(np.power(x[1], p)) + +PWG = PersistenceWeightedGaussianKernel(bandwidth=1., kernel_approx=None, weight=arctan(1.,1.)) +X = PWG.fit(diags) +Y = PWG.transform(diags2) +print("PWG kernel is " + str(Y[0][0])) + +PWG = PersistenceWeightedGaussianKernel(kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2])), weight=arctan(1.,1.)) +X = PWG.fit(diags) +Y = PWG.transform(diags2) +print("Approximate PWG kernel is " + str(Y[0][0])) + +PSS = PersistenceScaleSpaceKernel(bandwidth=1.) +X = PSS.fit(diags) +Y = PSS.transform(diags2) +print("PSS kernel is " + str(Y[0][0])) + +PSS = PersistenceScaleSpaceKernel(kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2]))) +X = PSS.fit(diags) +Y = PSS.transform(diags2) +print("Approximate PSS kernel is " + str(Y[0][0])) + +sW = SlicedWassersteinDistance(num_directions=100) +X = sW.fit(diags) +Y = sW.transform(diags2) +print("SW distance is " + str(Y[0][0])) + +SW = SlicedWassersteinKernel(num_directions=100, bandwidth=1.) +X = SW.fit(diags) +Y = SW.transform(diags2) +print("SW kernel is " + str(Y[0][0])) + +W = BottleneckDistance(epsilon=.001) +X = W.fit(diags) +Y = W.transform(diags2) +print("Bottleneck distance is " + str(Y[0][0])) + +PF = PersistenceFisherKernel(bandwidth_fisher=1., bandwidth=1.) +X = PF.fit(diags) +Y = PF.transform(diags2) +print("PF kernel is " + str(Y[0][0])) + +PF = PersistenceFisherKernel(bandwidth_fisher=1., bandwidth=1., kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2]))) +X = PF.fit(diags) +Y = PF.transform(diags2) +print("Approximate PF kernel is " + str(Y[0][0])) diff --git a/src/python/example/ex_diagrams.py b/src/python/example/ex_diagrams.py deleted file mode 100755 index a6a36b7c..00000000 --- a/src/python/example/ex_diagrams.py +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/env python - -import matplotlib.pyplot as plt -import numpy as np -from sklearn.kernel_approximation import RBFSampler -from sklearn.preprocessing import MinMaxScaler - -from gudhi.sktda import DiagramSelector, Clamping, Landscape, Silhouette, BettiCurve, ComplexPolynomial,\ - TopologicalVector, DiagramScaler, BirthPersistenceTransform,\ - PersistenceImage, PersistenceWeightedGaussianKernel, Entropy, \ - PersistenceScaleSpaceKernel, SlicedWassersteinDistance,\ - SlicedWassersteinKernel, BottleneckDistance, PersistenceFisherKernel - -D = np.array([[0.,4.],[1.,2.],[3.,8.],[6.,8.], [0., np.inf], [5., np.inf]]) -diags = [D] - -diags = DiagramSelector(use=True, point_type="finite").fit_transform(diags) -diags = DiagramScaler(use=True, scalers=[([0,1], MinMaxScaler())]).fit_transform(diags) -diags = DiagramScaler(use=True, scalers=[([1], Clamping(limit=.9))]).fit_transform(diags) - -D = diags[0] -plt.scatter(D[:,0],D[:,1]) -plt.plot([0.,1.],[0.,1.]) -plt.title("Test Persistence Diagram for vector methods") -plt.show() - -LS = Landscape(resolution=1000) -L = LS.fit_transform(diags) -plt.plot(L[0][:1000]) -plt.plot(L[0][1000:2000]) -plt.plot(L[0][2000:3000]) -plt.title("Landscape") -plt.show() - -def pow(n): - return lambda x: np.power(x[1]-x[0],n) - -SH = Silhouette(resolution=1000, weight=pow(2)) -sh = SH.fit_transform(diags) -plt.plot(sh[0]) -plt.title("Silhouette") -plt.show() - -BC = BettiCurve(resolution=1000) -bc = BC.fit_transform(diags) -plt.plot(bc[0]) -plt.title("Betti Curve") -plt.show() - -CP = ComplexPolynomial(threshold=-1, polynomial_type="T") -cp = CP.fit_transform(diags) -print("Complex polynomial is " + str(cp[0,:])) - -TV = TopologicalVector(threshold=-1) -tv = TV.fit_transform(diags) -print("Topological vector is " + str(tv[0,:])) - -PI = PersistenceImage(bandwidth=.1, weight=lambda x: x[1], im_range=[0,1,0,1], resolution=[100,100]) -pi = PI.fit_transform(diags) -plt.imshow(np.flip(np.reshape(pi[0], [100,100]), 0)) -plt.title("Persistence Image") -plt.show() - -ET = Entropy(mode="scalar") -et = ET.fit_transform(diags) -print("Entropy statistic is " + str(et[0,:])) - -ET = Entropy(mode="vector", normalized=False) -et = ET.fit_transform(diags) -plt.plot(et[0]) -plt.title("Entropy function") -plt.show() - -D = np.array([[1.,5.],[3.,6.],[2.,7.]]) -diags2 = [D] - -diags2 = DiagramScaler(use=True, scalers=[([0,1], MinMaxScaler())]).fit_transform(diags2) - -D = diags[0] -plt.scatter(D[:,0],D[:,1]) -D = diags2[0] -plt.scatter(D[:,0],D[:,1]) -plt.plot([0.,1.],[0.,1.]) -plt.title("Test Persistence Diagrams for kernel methods") -plt.show() - -def arctan(C,p): - return lambda x: C*np.arctan(np.power(x[1], p)) - -PWG = PersistenceWeightedGaussianKernel(bandwidth=1., kernel_approx=None, weight=arctan(1.,1.)) -X = PWG.fit(diags) -Y = PWG.transform(diags2) -print("PWG kernel is " + str(Y[0][0])) - -PWG = PersistenceWeightedGaussianKernel(kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2])), weight=arctan(1.,1.)) -X = PWG.fit(diags) -Y = PWG.transform(diags2) -print("Approximate PWG kernel is " + str(Y[0][0])) - -PSS = PersistenceScaleSpaceKernel(bandwidth=1.) -X = PSS.fit(diags) -Y = PSS.transform(diags2) -print("PSS kernel is " + str(Y[0][0])) - -PSS = PersistenceScaleSpaceKernel(kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2]))) -X = PSS.fit(diags) -Y = PSS.transform(diags2) -print("Approximate PSS kernel is " + str(Y[0][0])) - -sW = SlicedWassersteinDistance(num_directions=100) -X = sW.fit(diags) -Y = sW.transform(diags2) -print("SW distance is " + str(Y[0][0])) - -SW = SlicedWassersteinKernel(num_directions=100, bandwidth=1.) -X = SW.fit(diags) -Y = SW.transform(diags2) -print("SW kernel is " + str(Y[0][0])) - -W = BottleneckDistance(epsilon=.001) -X = W.fit(diags) -Y = W.transform(diags2) -print("Bottleneck distance is " + str(Y[0][0])) - -PF = PersistenceFisherKernel(bandwidth_fisher=1., bandwidth=1.) -X = PF.fit(diags) -Y = PF.transform(diags2) -print("PF kernel is " + str(Y[0][0])) - -PF = PersistenceFisherKernel(bandwidth_fisher=1., bandwidth=1., kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2]))) -X = PF.fit(diags) -Y = PF.transform(diags2) -print("Approximate PF kernel is " + str(Y[0][0])) diff --git a/src/python/test/test_representations.py b/src/python/test/test_representations.py new file mode 100755 index 00000000..4ff65f98 --- /dev/null +++ b/src/python/test/test_representations.py @@ -0,0 +1,11 @@ +import os +import sys +import matplotlib.pyplot as plt +# Disable graphics for testing purposes +plt.show = lambda:None +here = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(here + "/../example") +import diagram_vectorizations_distances_kernels +# pytest is unhappy if there are 0 tests +def test_nothing(): + return None -- cgit v1.2.3 From 908679b72c215d1914d8e3956126fa44367b937f Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Fri, 15 Nov 2019 22:45:15 +0100 Subject: The big rename: sktda -> representations --- src/python/CMakeLists.txt | 4 +- src/python/doc/index.rst | 2 +- src/python/doc/representations.rst | 35 ++ src/python/doc/representations_sum.inc | 14 + src/python/doc/sktda.rst | 35 -- src/python/doc/sktda_sum.inc | 14 - .../diagram_vectorizations_distances_kernels.py | 2 +- src/python/gudhi/representations/__init__.py | 6 + src/python/gudhi/representations/kernel_methods.py | 206 +++++++++ src/python/gudhi/representations/metrics.py | 243 +++++++++++ src/python/gudhi/representations/preprocessing.py | 305 +++++++++++++ src/python/gudhi/representations/vector_methods.py | 485 +++++++++++++++++++++ src/python/gudhi/sktda/__init__.py | 6 - src/python/gudhi/sktda/kernel_methods.py | 206 --------- src/python/gudhi/sktda/metrics.py | 243 ----------- src/python/gudhi/sktda/preprocessing.py | 305 ------------- src/python/gudhi/sktda/vector_methods.py | 485 --------------------- 17 files changed, 1298 insertions(+), 1298 deletions(-) create mode 100644 src/python/doc/representations.rst create mode 100644 src/python/doc/representations_sum.inc delete mode 100644 src/python/doc/sktda.rst delete mode 100644 src/python/doc/sktda_sum.inc create mode 100644 src/python/gudhi/representations/__init__.py create mode 100644 src/python/gudhi/representations/kernel_methods.py create mode 100644 src/python/gudhi/representations/metrics.py create mode 100644 src/python/gudhi/representations/preprocessing.py create mode 100644 src/python/gudhi/representations/vector_methods.py delete mode 100644 src/python/gudhi/sktda/__init__.py delete mode 100644 src/python/gudhi/sktda/kernel_methods.py delete mode 100644 src/python/gudhi/sktda/metrics.py delete mode 100644 src/python/gudhi/sktda/preprocessing.py delete mode 100644 src/python/gudhi/sktda/vector_methods.py (limited to 'src') diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index d131b920..2c568b66 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -50,7 +50,7 @@ if(PYTHONINTERP_FOUND) set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'euclidean_witness_complex', ") set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'euclidean_strong_witness_complex', ") # Modules that should not be auto-imported in __init__.py - set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'sktda', ") + set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'representations', ") set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'wasserstein', ") add_gudhi_debug_info("Python version ${PYTHON_VERSION_STRING}") @@ -208,7 +208,7 @@ if(PYTHONINTERP_FOUND) # Other .py files file(COPY "gudhi/persistence_graphical_tools.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") - file(COPY "gudhi/sktda" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi/") + file(COPY "gudhi/representations" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi/") file(COPY "gudhi/wasserstein.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") add_custom_command( diff --git a/src/python/doc/index.rst b/src/python/doc/index.rst index f8dd3c2f..1ef08096 100644 --- a/src/python/doc/index.rst +++ b/src/python/doc/index.rst @@ -81,7 +81,7 @@ Wasserstein distance Persistence representations =========================== -.. include:: sktda_sum.inc +.. include:: representations_sum.inc Persistence graphical tools =========================== diff --git a/src/python/doc/representations.rst b/src/python/doc/representations.rst new file mode 100644 index 00000000..3db1c95a --- /dev/null +++ b/src/python/doc/representations.rst @@ -0,0 +1,35 @@ +:orphan: + +.. To get rid of WARNING: document isn't included in any toctree + +=================================== +Representations reference manual +=================================== + +Preprocessing +------------- +.. automodule:: gudhi.representations.preprocessing + :members: + :special-members: + :show-inheritance: + +Vector methods +-------------- +.. automodule:: gudhi.representations.vector_methods + :members: + :special-members: + :show-inheritance: + +Kernel methods +-------------- +.. automodule:: gudhi.representations.kernel_methods + :members: + :special-members: + :show-inheritance: + +Metrics +------- +.. automodule:: gudhi.representations.metrics + :members: + :special-members: + :show-inheritance: diff --git a/src/python/doc/representations_sum.inc b/src/python/doc/representations_sum.inc new file mode 100644 index 00000000..7b167a17 --- /dev/null +++ b/src/python/doc/representations_sum.inc @@ -0,0 +1,14 @@ +.. table:: + :widths: 30 50 20 + + +------------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------------------+ + | .. figure:: | Vectorizations, distances and kernels that work on persistence | :Author: Mathieu Carrière | + | ../../doc/Persistence_representations/average_landscape.png | diagrams, compatible with scikit-learn. | | + | | | :Introduced in: GUDHI 3.1.0 | + | | | | + | | | :Copyright: MIT | + | | | | + | | | :Requires: scikit-learn | + +------------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------------------+ + | * :doc:`representations` | + +------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------+ diff --git a/src/python/doc/sktda.rst b/src/python/doc/sktda.rst deleted file mode 100644 index 9acec616..00000000 --- a/src/python/doc/sktda.rst +++ /dev/null @@ -1,35 +0,0 @@ -:orphan: - -.. To get rid of WARNING: document isn't included in any toctree - -=================================== -sktda reference manual -=================================== - -Preprocessing -------------- -.. automodule:: gudhi.sktda.preprocessing - :members: - :special-members: - :show-inheritance: - -Vector methods --------------- -.. automodule:: gudhi.sktda.vector_methods - :members: - :special-members: - :show-inheritance: - -Kernel methods --------------- -.. automodule:: gudhi.sktda.kernel_methods - :members: - :special-members: - :show-inheritance: - -Metrics -------- -.. automodule:: gudhi.sktda.metrics - :members: - :special-members: - :show-inheritance: diff --git a/src/python/doc/sktda_sum.inc b/src/python/doc/sktda_sum.inc deleted file mode 100644 index a0f77e7e..00000000 --- a/src/python/doc/sktda_sum.inc +++ /dev/null @@ -1,14 +0,0 @@ -.. table:: - :widths: 30 50 20 - - +------------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------------------+ - | .. figure:: | Vectorizations, distances and kernels that work on persistence | :Author: Mathieu Carrière | - | ../../doc/Persistence_representations/average_landscape.png | diagrams, compatible with scikit-learn. | | - | | | :Introduced in: GUDHI 3.1.0 | - | | | | - | | | :Copyright: MIT | - | | | | - | | | :Requires: scikit-learn | - +------------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------------------+ - | * :doc:`sktda` | - +------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------+ diff --git a/src/python/example/diagram_vectorizations_distances_kernels.py b/src/python/example/diagram_vectorizations_distances_kernels.py index a6a36b7c..a77bbfdd 100755 --- a/src/python/example/diagram_vectorizations_distances_kernels.py +++ b/src/python/example/diagram_vectorizations_distances_kernels.py @@ -5,7 +5,7 @@ import numpy as np from sklearn.kernel_approximation import RBFSampler from sklearn.preprocessing import MinMaxScaler -from gudhi.sktda import DiagramSelector, Clamping, Landscape, Silhouette, BettiCurve, ComplexPolynomial,\ +from gudhi.representations import DiagramSelector, Clamping, Landscape, Silhouette, BettiCurve, ComplexPolynomial,\ TopologicalVector, DiagramScaler, BirthPersistenceTransform,\ PersistenceImage, PersistenceWeightedGaussianKernel, Entropy, \ PersistenceScaleSpaceKernel, SlicedWassersteinDistance,\ diff --git a/src/python/gudhi/representations/__init__.py b/src/python/gudhi/representations/__init__.py new file mode 100644 index 00000000..f020248d --- /dev/null +++ b/src/python/gudhi/representations/__init__.py @@ -0,0 +1,6 @@ +from .kernel_methods import * +from .metrics import * +from .preprocessing import * +from .vector_methods import * + +__all__ = ["kernel_methods", "metrics", "preprocessing", "vector_methods"] diff --git a/src/python/gudhi/representations/kernel_methods.py b/src/python/gudhi/representations/kernel_methods.py new file mode 100644 index 00000000..c855d2be --- /dev/null +++ b/src/python/gudhi/representations/kernel_methods.py @@ -0,0 +1,206 @@ +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Mathieu Carrière +# +# Copyright (C) 2018-2019 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification + +import numpy as np +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.metrics import pairwise_distances +from .metrics import SlicedWassersteinDistance, PersistenceFisherDistance + +############################################# +# Kernel methods ############################ +############################################# + +class SlicedWassersteinKernel(BaseEstimator, TransformerMixin): + """ + This is a class for computing the sliced Wasserstein kernel matrix from a list of persistence diagrams. The sliced Wasserstein kernel is computed by exponentiating the corresponding sliced Wasserstein distance with a Gaussian kernel. See http://proceedings.mlr.press/v70/carriere17a.html for more details. + """ + def __init__(self, num_directions=10, bandwidth=1.0): + """ + Constructor for the SlicedWassersteinKernel class. + + Parameters: + bandwidth (double): bandwidth of the Gaussian kernel applied to the sliced Wasserstein distance (default 1.). + num_directions (int): number of lines evenly sampled from [-pi/2,pi/2] in order to approximate and speed up the kernel computation (default 10). + """ + self.bandwidth = bandwidth + self.sw_ = SlicedWassersteinDistance(num_directions=num_directions) + + def fit(self, X, y=None): + """ + Fit the SlicedWassersteinKernel class on a list of persistence diagrams: an instance of the SlicedWassersteinDistance class is fitted on the diagrams and then stored. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.sw_.fit(X, y) + return self + + def transform(self, X): + """ + Compute all sliced Wasserstein kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise sliced Wasserstein kernel values. + """ + return np.exp(-self.sw_.transform(X)/self.bandwidth) + +class PersistenceWeightedGaussianKernel(BaseEstimator, TransformerMixin): + """ + This is a class for computing the persistence weighted Gaussian kernel matrix from a list of persistence diagrams. The persistence weighted Gaussian kernel is computed by convolving the persistence diagram points with weighted Gaussian kernels. See http://proceedings.mlr.press/v48/kusano16.html for more details. + """ + def __init__(self, bandwidth=1., weight=lambda x: 1, kernel_approx=None): + """ + Constructor for the PersistenceWeightedGaussianKernel class. + + Parameters: + bandwidth (double): bandwidth of the Gaussian kernel with which persistence diagrams will be convolved (default 1.) + weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie lists or numpy arrays of the form [p_x,p_y]. + kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). + """ + self.bandwidth, self.weight = bandwidth, weight + self.kernel_approx = kernel_approx + + def fit(self, X, y=None): + """ + Fit the PersistenceWeightedGaussianKernel class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams** and the kernel approximation class (if not None) is applied on them. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = list(X) + self.ws_ = [ np.array([self.weight(self.diagrams_[i][j,:]) for j in range(self.diagrams_[i].shape[0])]) for i in range(len(self.diagrams_)) ] + if self.kernel_approx is not None: + self.approx_ = np.concatenate([np.sum(np.multiply(self.ws_[i][:,np.newaxis], self.kernel_approx.transform(self.diagrams_[i])), axis=0)[np.newaxis,:] for i in range(len(self.diagrams_))]) + return self + + def transform(self, X): + """ + Compute all persistence weighted Gaussian kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise persistence weighted Gaussian kernel values. + """ + Xp = list(X) + Xfit = np.zeros((len(Xp), len(self.diagrams_))) + if len(self.diagrams_) == len(Xp) and np.all([np.array_equal(self.diagrams_[i], Xp[i]) for i in range(len(Xp))]): + if self.kernel_approx is not None: + Xfit = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.matmul(self.approx_, self.approx_.T) + else: + for i in range(len(self.diagrams_)): + for j in range(i+1, len(self.diagrams_)): + W = np.matmul(self.ws_[i][:,np.newaxis], self.ws_[j][np.newaxis,:]) + E = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.exp(-np.square(pairwise_distances(self.diagrams_[i], self.diagrams_[j]))/(2*np.square(self.bandwidth))) + Xfit[i,j] = np.sum(np.multiply(W, E)) + Xfit[j,i] = Xfit[i,j] + else: + ws = [ np.array([self.weight(Xp[i][j,:]) for j in range(Xp[i].shape[0])]) for i in range(len(Xp)) ] + if self.kernel_approx is not None: + approx = np.concatenate([np.sum(np.multiply(ws[i][:,np.newaxis], self.kernel_approx.transform(Xp[i])), axis=0)[np.newaxis,:] for i in range(len(Xp))]) + Xfit = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.matmul(approx, self.approx_.T) + else: + for i in range(len(Xp)): + for j in range(len(self.diagrams_)): + W = np.matmul(ws[i][:,np.newaxis], self.ws_[j][np.newaxis,:]) + E = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.exp(-np.square(pairwise_distances(Xp[i], self.diagrams_[j]))/(2*np.square(self.bandwidth))) + Xfit[i,j] = np.sum(np.multiply(W, E)) + + return Xfit + +class PersistenceScaleSpaceKernel(BaseEstimator, TransformerMixin): + """ + This is a class for computing the persistence scale space kernel matrix from a list of persistence diagrams. The persistence scale space kernel is computed by adding the symmetric to the diagonal of each point in each persistence diagram, with negative weight, and then convolving the points with a Gaussian kernel. See https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Reininghaus_A_Stable_Multi-Scale_2015_CVPR_paper.pdf for more details. + """ + def __init__(self, bandwidth=1., kernel_approx=None): + """ + Constructor for the PersistenceScaleSpaceKernel class. + + Parameters: + bandwidth (double): bandwidth of the Gaussian kernel with which persistence diagrams will be convolved (default 1.) + kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). + """ + self.pwg_ = PersistenceWeightedGaussianKernel(bandwidth=bandwidth, weight=lambda x: 1 if x[1] >= x[0] else -1, kernel_approx=kernel_approx) + + def fit(self, X, y=None): + """ + Fit the PersistenceScaleSpaceKernel class on a list of persistence diagrams: symmetric to the diagonal of all points are computed and an instance of the PersistenceWeightedGaussianKernel class is fitted on the diagrams and then stored. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = list(X) + for i in range(len(self.diagrams_)): + op_D = self.diagrams_[i][:,[1,0]] + self.diagrams_[i] = np.concatenate([self.diagrams_[i], op_D], axis=0) + self.pwg_.fit(X) + return self + + def transform(self, X): + """ + Compute all persistence scale space kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise persistence scale space kernel values. + """ + Xp = list(X) + for i in range(len(Xp)): + op_X = np.matmul(Xp[i], np.array([[0.,1.], [1.,0.]])) + Xp[i] = np.concatenate([Xp[i], op_X], axis=0) + return self.pwg_.transform(Xp) + +class PersistenceFisherKernel(BaseEstimator, TransformerMixin): + """ + This is a class for computing the persistence Fisher kernel matrix from a list of persistence diagrams. The persistence Fisher kernel is computed by exponentiating the corresponding persistence Fisher distance with a Gaussian kernel. See papers.nips.cc/paper/8205-persistence-fisher-kernel-a-riemannian-manifold-kernel-for-persistence-diagrams for more details. + """ + def __init__(self, bandwidth_fisher=1., bandwidth=1., kernel_approx=None): + """ + Constructor for the PersistenceFisherKernel class. + + Parameters: + bandwidth (double): bandwidth of the Gaussian kernel applied to the persistence Fisher distance (default 1.). + bandwidth_fisher (double): bandwidth of the Gaussian kernel used to turn persistence diagrams into probability distributions by PersistenceFisherDistance class (default 1.). + kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). + """ + self.bandwidth = bandwidth + self.pf_ = PersistenceFisherDistance(bandwidth=bandwidth_fisher, kernel_approx=kernel_approx) + + def fit(self, X, y=None): + """ + Fit the PersistenceFisherKernel class on a list of persistence diagrams: an instance of the PersistenceFisherDistance class is fitted on the diagrams and then stored. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.pf_.fit(X, y) + return self + + def transform(self, X): + """ + Compute all persistence Fisher kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise persistence Fisher kernel values. + """ + return np.exp(-self.pf_.transform(X)/self.bandwidth) + diff --git a/src/python/gudhi/representations/metrics.py b/src/python/gudhi/representations/metrics.py new file mode 100644 index 00000000..c512cb82 --- /dev/null +++ b/src/python/gudhi/representations/metrics.py @@ -0,0 +1,243 @@ +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Mathieu Carrière +# +# Copyright (C) 2018-2019 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification + +import numpy as np +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.metrics import pairwise_distances +try: + from .. import bottleneck_distance + USE_GUDHI = True +except ImportError: + USE_GUDHI = False + print("Gudhi built without CGAL: BottleneckDistance will return a null matrix") + +############################################# +# Metrics ################################### +############################################# + +class SlicedWassersteinDistance(BaseEstimator, TransformerMixin): + """ + This is a class for computing the sliced Wasserstein distance matrix from a list of persistence diagrams. The Sliced Wasserstein distance is computed by projecting the persistence diagrams onto lines, comparing the projections with the 1-norm, and finally integrating over all possible lines. See http://proceedings.mlr.press/v70/carriere17a.html for more details. + """ + def __init__(self, num_directions=10): + """ + Constructor for the SlicedWassersteinDistance class. + + Parameters: + num_directions (int): number of lines evenly sampled from [-pi/2,pi/2] in order to approximate and speed up the distance computation (default 10). + """ + self.num_directions = num_directions + thetas = np.linspace(-np.pi/2, np.pi/2, num=self.num_directions+1)[np.newaxis,:-1] + self.lines_ = np.concatenate([np.cos(thetas), np.sin(thetas)], axis=0) + + def fit(self, X, y=None): + """ + Fit the SlicedWassersteinDistance class on a list of persistence diagrams: persistence diagrams are projected onto the different lines. The diagrams themselves and their projections are then stored in numpy arrays, called **diagrams_** and **approx_diag_**. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = X + self.approx_ = [np.matmul(X[i], self.lines_) for i in range(len(X))] + diag_proj = (1./2) * np.ones((2,2)) + self.approx_diag_ = [np.matmul(np.matmul(X[i], diag_proj), self.lines_) for i in range(len(X))] + return self + + def transform(self, X): + """ + Compute all sliced Wasserstein distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise sliced Wasserstein distances. + """ + Xfit = np.zeros((len(X), len(self.approx_))) + if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): + for i in range(len(self.approx_)): + for j in range(i+1, len(self.approx_)): + A = np.sort(np.concatenate([self.approx_[i], self.approx_diag_[j]], axis=0), axis=0) + B = np.sort(np.concatenate([self.approx_[j], self.approx_diag_[i]], axis=0), axis=0) + L1 = np.sum(np.abs(A-B), axis=0) + Xfit[i,j] = np.mean(L1) + Xfit[j,i] = Xfit[i,j] + else: + diag_proj = (1./2) * np.ones((2,2)) + approx = [np.matmul(X[i], self.lines_) for i in range(len(X))] + approx_diag = [np.matmul(np.matmul(X[i], diag_proj), self.lines_) for i in range(len(X))] + for i in range(len(approx)): + for j in range(len(self.approx_)): + A = np.sort(np.concatenate([approx[i], self.approx_diag_[j]], axis=0), axis=0) + B = np.sort(np.concatenate([self.approx_[j], approx_diag[i]], axis=0), axis=0) + L1 = np.sum(np.abs(A-B), axis=0) + Xfit[i,j] = np.mean(L1) + + return Xfit + +class BottleneckDistance(BaseEstimator, TransformerMixin): + """ + This is a class for computing the bottleneck distance matrix from a list of persistence diagrams. + """ + def __init__(self, epsilon=1e-3): + """ + Constructor for the BottleneckDistance class. + + Parameters: + epsilon (double): approximation quality (default 1e-4). + """ + self.epsilon = epsilon + + def fit(self, X, y=None): + """ + Fit the BottleneckDistance class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams**. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = X + return self + + def transform(self, X): + """ + Compute all bottleneck distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise bottleneck distances. + """ + num_diag1 = len(X) + + if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): + matrix = np.zeros((num_diag1, num_diag1)) + + if USE_GUDHI: + for i in range(num_diag1): + for j in range(i+1, num_diag1): + matrix[i,j] = bottleneck_distance(X[i], X[j], self.epsilon) + matrix[j,i] = matrix[i,j] + else: + print("Gudhi required---returning null matrix") + + else: + num_diag2 = len(self.diagrams_) + matrix = np.zeros((num_diag1, num_diag2)) + + if USE_GUDHI: + for i in range(num_diag1): + for j in range(num_diag2): + matrix[i,j] = bottleneck_distance(X[i], self.diagrams_[j], self.epsilon) + else: + print("Gudhi required---returning null matrix") + + Xfit = matrix + + return Xfit + +class PersistenceFisherDistance(BaseEstimator, TransformerMixin): + """ + This is a class for computing the persistence Fisher distance matrix from a list of persistence diagrams. The persistence Fisher distance is obtained by computing the original Fisher distance between the probability distributions associated to the persistence diagrams given by convolving them with a Gaussian kernel. See http://papers.nips.cc/paper/8205-persistence-fisher-kernel-a-riemannian-manifold-kernel-for-persistence-diagrams for more details. + """ + def __init__(self, bandwidth=1., kernel_approx=None): + """ + Constructor for the PersistenceFisherDistance class. + + Parameters: + bandwidth (double): bandwidth of the Gaussian kernel used to turn persistence diagrams into probability distributions (default 1.). + kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). + """ + self.bandwidth, self.kernel_approx = bandwidth, kernel_approx + + def fit(self, X, y=None): + """ + Fit the PersistenceFisherDistance class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams** and the kernel approximation class (if not None) is applied on them. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = X + projection = (1./2) * np.ones((2,2)) + self.diagonal_projections_ = [np.matmul(X[i], projection) for i in range(len(X))] + if self.kernel_approx is not None: + self.approx_ = [self.kernel_approx.transform(X[i]) for i in range(len(X))] + self.approx_diagonal_ = [self.kernel_approx.transform(self.diagonal_projections_[i]) for i in range(len(X))] + return self + + def transform(self, X): + """ + Compute all persistence Fisher distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise persistence Fisher distances. + """ + Xfit = np.zeros((len(X), len(self.diagrams_))) + if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): + for i in range(len(self.diagrams_)): + for j in range(i+1, len(self.diagrams_)): + if self.kernel_approx is not None: + Z = np.concatenate([self.approx_[i], self.approx_diagonal_[i], self.approx_[j], self.approx_diagonal_[j]], axis=0) + U, V = np.sum(np.concatenate([self.approx_[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], self.approx_diagonal_[i]], axis=0), axis=0) + vectori, vectorj = np.abs(np.matmul(Z, U.T)), np.abs(np.matmul(Z, V.T)) + vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) + if vectori_sum != 0: + vectori = vectori/vectori_sum + if vectorj_sum != 0: + vectorj = vectorj/vectorj_sum + Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) + Xfit[j,i] = Xfit[i,j] + else: + Z = np.concatenate([self.diagrams_[i], self.diagonal_projections_[i], self.diagrams_[j], self.diagonal_projections_[j]], axis=0) + U, V = np.concatenate([self.diagrams_[i], self.diagonal_projections_[j]], axis=0), np.concatenate([self.diagrams_[j], self.diagonal_projections_[i]], axis=0) + vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) + vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) + vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) + if vectori_sum != 0: + vectori = vectori/vectori_sum + if vectorj_sum != 0: + vectorj = vectorj/vectorj_sum + Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) + Xfit[j,i] = Xfit[i,j] + else: + projection = (1./2) * np.ones((2,2)) + diagonal_projections = [np.matmul(X[i], projection) for i in range(len(X))] + if self.kernel_approx is not None: + approx = [self.kernel_approx.transform(X[i]) for i in range(len(X))] + approx_diagonal = [self.kernel_approx.transform(diagonal_projections[i]) for i in range(len(X))] + for i in range(len(X)): + for j in range(len(self.diagrams_)): + if self.kernel_approx is not None: + Z = np.concatenate([approx[i], approx_diagonal[i], self.approx_[j], self.approx_diagonal_[j]], axis=0) + U, V = np.sum(np.concatenate([approx[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], approx_diagonal[i]], axis=0), axis=0) + vectori, vectorj = np.abs(np.matmul(Z, U.T)), np.abs(np.matmul(Z, V.T)) + vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) + if vectori_sum != 0: + vectori = vectori/vectori_sum + if vectorj_sum != 0: + vectorj = vectorj/vectorj_sum + Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) + else: + Z = np.concatenate([X[i], diagonal_projections[i], self.diagrams_[j], self.diagonal_projections_[j]], axis=0) + U, V = np.concatenate([X[i], self.diagonal_projections_[j]], axis=0), np.concatenate([self.diagrams_[j], diagonal_projections[i]], axis=0) + vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) + vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) + vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) + if vectori_sum != 0: + vectori = vectori/vectori_sum + if vectorj_sum != 0: + vectorj = vectorj/vectorj_sum + Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) + return Xfit diff --git a/src/python/gudhi/representations/preprocessing.py b/src/python/gudhi/representations/preprocessing.py new file mode 100644 index 00000000..83227ca1 --- /dev/null +++ b/src/python/gudhi/representations/preprocessing.py @@ -0,0 +1,305 @@ +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Mathieu Carrière +# +# Copyright (C) 2018-2019 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification + +import numpy as np +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.preprocessing import StandardScaler + +############################################# +# Preprocessing ############################# +############################################# + +class BirthPersistenceTransform(BaseEstimator, TransformerMixin): + """ + This is a class for the affine transformation (x,y) -> (x,y-x) to be applied on persistence diagrams. + """ + def __init__(self): + """ + Constructor for BirthPersistenceTransform class. + """ + return None + + def fit(self, X, y=None): + """ + Fit the BirthPersistenceTransform class on a list of persistence diagrams (this function actually does nothing but is useful when BirthPersistenceTransform is included in a scikit-learn Pipeline). + + Parameters: + X (n x 2 numpy array): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + Apply the BirthPersistenceTransform function on the persistence diagrams. + + Parameters: + X (list of n x 2 numpy array): input persistence diagrams. + + Returns: + list of n x 2 numpy array: transformed persistence diagrams. + """ + Xfit = [] + for diag in X: + #new_diag = np.empty(diag.shape) + #np.copyto(new_diag, diag) + new_diag = np.copy(diag) + new_diag[:,1] = new_diag[:,1] - new_diag[:,0] + Xfit.append(new_diag) + return Xfit + +class Clamping(BaseEstimator, TransformerMixin): + """ + This is a class for clamping values. It can be used as a parameter for the DiagramScaler class, for instance if you want to clamp abscissae or ordinates of persistence diagrams. + """ + def __init__(self, limit=np.inf): + """ + Constructor for the Clamping class. + + Parameters: + limit (double): clamping value (default np.inf). + """ + self.limit = limit + + def fit(self, X, y=None): + """ + Fit the Clamping class on a list of values (this function actually does nothing but is useful when Clamping is included in a scikit-learn Pipeline). + + Parameters: + X (numpy array of size n): input values. + y (n x 1 array): value labels (unused). + """ + return self + + def transform(self, X): + """ + Clamp list of values. + + Parameters: + X (numpy array of size n): input list of values. + + Returns: + numpy array of size n: output list of values. + """ + Xfit = np.minimum(X, self.limit) + #Xfit = np.where(X >= self.limit, self.limit * np.ones(X.shape), X) + return Xfit + +class DiagramScaler(BaseEstimator, TransformerMixin): + """ + This is a class for preprocessing persistence diagrams with a given list of scalers, such as those included in scikit-learn. + """ + def __init__(self, use=False, scalers=[]): + """ + Constructor for the DiagramScaler class. + + Parameters: + use (bool): whether to use the class or not (default False). + scalers (list of classes): list of scalers to be fit on the persistence diagrams (default []). Each element of the list is a tuple with two elements: the first one is a list of coordinates, and the second one is a scaler (i.e. a class with fit() and transform() methods) that is going to be applied to these coordinates. Common scalers can be found in the scikit-learn library (such as MinMaxScaler for instance). + """ + self.scalers = scalers + self.use = use + + def fit(self, X, y=None): + """ + Fit the DiagramScaler class on a list of persistence diagrams: persistence diagrams are concatenated in a big numpy array, and scalers are fit (by calling their fit() method) on their corresponding coordinates in this big array. + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if self.use: + if len(X) == 1: + P = X[0] + else: + P = np.concatenate(X,0) + for (indices, scaler) in self.scalers: + scaler.fit(np.reshape(P[:,indices], [-1, 1])) + return self + + def transform(self, X): + """ + Apply the DiagramScaler function on the persistence diagrams. The fitted scalers are applied (by calling their transform() method) to their corresponding coordinates in each persistence diagram individually. + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + + Returns: + list of n x 2 or n x 1 numpy arrays: transformed persistence diagrams. + """ + Xfit = [np.copy(d) for d in X] + if self.use: + for i in range(len(Xfit)): + if Xfit[i].shape[0] > 0: + for (indices, scaler) in self.scalers: + for I in indices: + Xfit[i][:,I] = np.squeeze(scaler.transform(np.reshape(Xfit[i][:,I], [-1,1]))) + return Xfit + +class Padding(BaseEstimator, TransformerMixin): + """ + This is a class for padding a list of persistence diagrams with dummy points, so that all persistence diagrams end up with the same number of points. + """ + def __init__(self, use=False): + """ + Constructor for the Padding class. + + Parameters: + use (bool): whether to use the class or not (default False). + """ + self.use = use + + def fit(self, X, y=None): + """ + Fit the Padding class on a list of persistence diagrams (this function actually does nothing but is useful when Padding is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.max_pts = max([len(diag) for diag in X]) + return self + + def transform(self, X): + """ + Add dummy points to each persistence diagram so that they all have the same cardinality. All points are given an additional coordinate indicating if the point was added after padding (0) or already present before (1). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + + Returns: + list of n x 3 or n x 2 numpy arrays: padded persistence diagrams. + """ + if self.use: + Xfit, num_diag = [], len(X) + for diag in X: + diag_pad = np.pad(diag, ((0,max(0, self.max_pts - diag.shape[0])), (0,1)), "constant", constant_values=((0,0),(0,0))) + diag_pad[:diag.shape[0],2] = np.ones(diag.shape[0]) + Xfit.append(diag_pad) + else: + Xfit = X + return Xfit + +class ProminentPoints(BaseEstimator, TransformerMixin): + """ + This is a class for removing points that are close or far from the diagonal in persistence diagrams. If persistence diagrams are n x 2 numpy arrays (i.e. persistence diagrams with ordinary features), points are ordered and thresholded by distance-to-diagonal. If persistence diagrams are n x 1 numpy arrays (i.e. persistence diagrams with essential features), points are not ordered and thresholded by first coordinate. + """ + def __init__(self, use=False, num_pts=10, threshold=-1, location="upper"): + """ + Constructor for the ProminentPoints class. + + Parameters: + use (bool): whether to use the class or not (default False). + location (string): either "upper" or "lower" (default "upper"). Whether to keep the points that are far away ("upper") or close ("lower") to the diagonal. + num_pts (int): cardinality threshold (default 10). If location == "upper", keep the top **num_pts** points that are the farthest away from the diagonal. If location == "lower", keep the top **num_pts** points that are the closest to the diagonal. + threshold (double): distance-to-diagonal threshold (default -1). If location == "upper", keep the points that are at least at a distance **threshold** from the diagonal. If location == "lower", keep the points that are at most at a distance **threshold** from the diagonal. + """ + self.num_pts = num_pts + self.threshold = threshold + self.use = use + self.location = location + + def fit(self, X, y=None): + """ + Fit the ProminentPoints class on a list of persistence diagrams (this function actually does nothing but is useful when ProminentPoints is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + If location == "upper", first select the top **num_pts** points that are the farthest away from the diagonal, then select and return from these points the ones that are at least at distance **threshold** from the diagonal for each persistence diagram individually. If location == "lower", first select the top **num_pts** points that are the closest to the diagonal, then select and return from these points the ones that are at most at distance **threshold** from the diagonal for each persistence diagram individually. + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + + Returns: + list of n x 2 or n x 1 numpy arrays: thresholded persistence diagrams. + """ + if self.use: + Xfit, num_diag = [], len(X) + for i in range(num_diag): + diag = X[i] + if diag.shape[1] >= 2: + if diag.shape[0] > 0: + pers = np.abs(diag[:,1] - diag[:,0]) + idx_thresh = pers >= self.threshold + thresh_diag, thresh_pers = diag[idx_thresh], pers[idx_thresh] + sort_index = np.flip(np.argsort(thresh_pers, axis=None), 0) + if self.location == "upper": + new_diag = thresh_diag[sort_index[:min(self.num_pts, thresh_diag.shape[0])],:] + if self.location == "lower": + new_diag = np.concatenate( [ thresh_diag[sort_index[min(self.num_pts, thresh_diag.shape[0]):],:], diag[~idx_thresh] ], axis=0) + else: + new_diag = diag + + else: + if diag.shape[0] > 0: + birth = diag[:,:1] + idx_thresh = birth >= self.threshold + thresh_diag, thresh_birth = diag[idx_thresh], birth[idx_thresh] + if self.location == "upper": + new_diag = thresh_diag[:min(self.num_pts, thresh_diag.shape[0]),:] + if self.location == "lower": + new_diag = np.concatenate( [ thresh_diag[min(self.num_pts, thresh_diag.shape[0]):,:], diag[~idx_thresh] ], axis=0) + else: + new_diag = diag + + Xfit.append(new_diag) + else: + Xfit = X + return Xfit + +class DiagramSelector(BaseEstimator, TransformerMixin): + """ + This is a class for extracting finite or essential points in persistence diagrams. + """ + def __init__(self, use=False, limit=np.inf, point_type="finite"): + """ + Constructor for the DiagramSelector class. + + Parameters: + use (bool): whether to use the class or not (default False). + limit (double): second coordinate value that is the criterion for being an essential point (default numpy.inf). + point_type (string): either "finite" or "essential". The type of the points that are going to be extracted. + """ + self.use, self.limit, self.point_type = use, limit, point_type + + def fit(self, X, y=None): + """ + Fit the DiagramSelector class on a list of persistence diagrams (this function actually does nothing but is useful when DiagramSelector is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + Extract and return the finite or essential points of each persistence diagram individually. + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + + Returns: + list of n x 2 or n x 1 numpy arrays: extracted persistence diagrams. + """ + if self.use: + Xfit, num_diag = [], len(X) + if self.point_type == "finite": + Xfit = [ diag[diag[:,1] < self.limit] if diag.shape[0] != 0 else diag for diag in X] + else: + Xfit = [ diag[diag[:,1] >= self.limit, 0:1] if diag.shape[0] != 0 else diag for diag in X] + else: + Xfit = X + return Xfit diff --git a/src/python/gudhi/representations/vector_methods.py b/src/python/gudhi/representations/vector_methods.py new file mode 100644 index 00000000..bf32f18e --- /dev/null +++ b/src/python/gudhi/representations/vector_methods.py @@ -0,0 +1,485 @@ +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Mathieu Carrière +# +# Copyright (C) 2018-2019 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification + +import numpy as np +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.preprocessing import MinMaxScaler, MaxAbsScaler +from sklearn.neighbors import DistanceMetric + +from .preprocessing import DiagramScaler, BirthPersistenceTransform + +############################################# +# Finite Vectorization methods ############## +############################################# + +class PersistenceImage(BaseEstimator, TransformerMixin): + """ + This is a class for computing persistence images from a list of persistence diagrams. A persistence image is a 2D function computed from a persistence diagram by convolving the diagram points with a weighted Gaussian kernel. The plane is then discretized into an image with pixels, which is flattened and returned as a vector. See http://jmlr.org/papers/v18/16-337.html for more details. + """ + def __init__(self, bandwidth=1., weight=lambda x: 1, resolution=[20,20], im_range=[np.nan, np.nan, np.nan, np.nan]): + """ + Constructor for the PersistenceImage class. + + Parameters: + bandwidth (double): bandwidth of the Gaussian kernel (default 1.). + weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie lists or numpy arrays of the form [p_x,p_y]. + resolution ([int,int]): size (in pixels) of the persistence image (default [20,20]). + im_range ([double,double,double,double]): minimum and maximum of each axis of the persistence image, of the form [x_min, x_max, y_min, y_max] (default [numpy.nan, numpy.nan, numpy.nan, numpyp.nan]). If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + """ + self.bandwidth, self.weight = bandwidth, weight + self.resolution, self.im_range = resolution, im_range + + def fit(self, X, y=None): + """ + Fit the PersistenceImage class on a list of persistence diagrams: if any of the values in **im_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.im_range)).any(): + new_X = BirthPersistenceTransform().fit_transform(X) + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(new_X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.im_range = np.where(np.isnan(np.array(self.im_range)), np.array([mx, Mx, my, My]), np.array(self.im_range)) + return self + + def transform(self, X): + """ + Compute the persistence image for each persistence diagram individually and store the results in a single numpy array. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array with shape (number of diagrams) x (number of pixels = **resolution[0]** x **resolution[1]**): output persistence images. + """ + num_diag, Xfit = len(X), [] + new_X = BirthPersistenceTransform().fit_transform(X) + + for i in range(num_diag): + + diagram, num_pts_in_diag = new_X[i], X[i].shape[0] + + w = np.empty(num_pts_in_diag) + for j in range(num_pts_in_diag): + w[j] = self.weight(diagram[j,:]) + + x_values, y_values = np.linspace(self.im_range[0], self.im_range[1], self.resolution[0]), np.linspace(self.im_range[2], self.im_range[3], self.resolution[1]) + Xs, Ys = np.tile((diagram[:,0][:,np.newaxis,np.newaxis]-x_values[np.newaxis,np.newaxis,:]),[1,self.resolution[1],1]), np.tile(diagram[:,1][:,np.newaxis,np.newaxis]-y_values[np.newaxis,:,np.newaxis],[1,1,self.resolution[0]]) + image = np.tensordot(w, np.exp((-np.square(Xs)-np.square(Ys))/(2*np.square(self.bandwidth)))/(np.square(self.bandwidth)*2*np.pi), 1) + + Xfit.append(image.flatten()[np.newaxis,:]) + + Xfit = np.concatenate(Xfit,0) + + return Xfit + +class Landscape(BaseEstimator, TransformerMixin): + """ + This is a class for computing persistence landscapes from a list of persistence diagrams. A persistence landscape is a collection of 1D piecewise-linear functions computed from the rank function associated to the persistence diagram. These piecewise-linear functions are then sampled uniformly on a given range and the corresponding vectors of samples are concatenated and returned. See http://jmlr.org/papers/v16/bubenik15a.html for more details. + """ + def __init__(self, num_landscapes=5, resolution=100, sample_range=[np.nan, np.nan]): + """ + Constructor for the Landscape class. + + Parameters: + num_landscapes (int): number of piecewise-linear functions to output (default 5). + resolution (int): number of sample for all piecewise-linear functions (default 100). + sample_range ([double, double]): minimum and maximum of all piecewise-linear function domains, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + """ + self.num_landscapes, self.resolution, self.sample_range = num_landscapes, resolution, sample_range + + def fit(self, X, y=None): + """ + Fit the Landscape class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.sample_range)).any(): + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) + return self + + def transform(self, X): + """ + Compute the persistence landscape for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array with shape (number of diagrams) x (number of samples = **num_landscapes** x **resolution**): output persistence landscapes. + """ + num_diag, Xfit = len(X), [] + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) + step_x = x_values[1] - x_values[0] + + for i in range(num_diag): + + diagram, num_pts_in_diag = X[i], X[i].shape[0] + + ls = np.zeros([self.num_landscapes, self.resolution]) + + events = [] + for j in range(self.resolution): + events.append([]) + + for j in range(num_pts_in_diag): + [px,py] = diagram[j,:2] + min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + + if min_idx < self.resolution and max_idx > 0: + + landscape_value = self.sample_range[0] + min_idx * step_x - px + for k in range(min_idx, mid_idx): + events[k].append(landscape_value) + landscape_value += step_x + + landscape_value = py - self.sample_range[0] - mid_idx * step_x + for k in range(mid_idx, max_idx): + events[k].append(landscape_value) + landscape_value -= step_x + + for j in range(self.resolution): + events[j].sort(reverse=True) + for k in range( min(self.num_landscapes, len(events[j])) ): + ls[k,j] = events[j][k] + + Xfit.append(np.sqrt(2)*np.reshape(ls,[1,-1])) + + Xfit = np.concatenate(Xfit,0) + + return Xfit + +class Silhouette(BaseEstimator, TransformerMixin): + """ + This is a class for computing persistence silhouettes from a list of persistence diagrams. A persistence silhouette is computed by taking a weighted average of the collection of 1D piecewise-linear functions given by the persistence landscapes, and then by uniformly sampling this average on a given range. Finally, the corresponding vector of samples is returned. See https://arxiv.org/abs/1312.0308 for more details. + """ + def __init__(self, weight=lambda x: 1, resolution=100, sample_range=[np.nan, np.nan]): + """ + Constructor for the Silhouette class. + + Parameters: + weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie on lists or numpy arrays of the form [p_x,p_y]. + resolution (int): number of samples for the weighted average (default 100). + sample_range ([double, double]): minimum and maximum for the weighted average domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + """ + self.weight, self.resolution, self.sample_range = weight, resolution, sample_range + + def fit(self, X, y=None): + """ + Fit the Silhouette class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.sample_range)).any(): + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) + return self + + def transform(self, X): + """ + Compute the persistence silhouette for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array with shape (number of diagrams) x (**resolution**): output persistence silhouettes. + """ + num_diag, Xfit = len(X), [] + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) + step_x = x_values[1] - x_values[0] + + for i in range(num_diag): + + diagram, num_pts_in_diag = X[i], X[i].shape[0] + + sh, weights = np.zeros(self.resolution), np.zeros(num_pts_in_diag) + for j in range(num_pts_in_diag): + weights[j] = self.weight(diagram[j,:]) + total_weight = np.sum(weights) + + for j in range(num_pts_in_diag): + + [px,py] = diagram[j,:2] + weight = weights[j] / total_weight + min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + + if min_idx < self.resolution and max_idx > 0: + + silhouette_value = self.sample_range[0] + min_idx * step_x - px + for k in range(min_idx, mid_idx): + sh[k] += weight * silhouette_value + silhouette_value += step_x + + silhouette_value = py - self.sample_range[0] - mid_idx * step_x + for k in range(mid_idx, max_idx): + sh[k] += weight * silhouette_value + silhouette_value -= step_x + + Xfit.append(np.reshape(np.sqrt(2) * sh, [1,-1])) + + Xfit = np.concatenate(Xfit, 0) + + return Xfit + +class BettiCurve(BaseEstimator, TransformerMixin): + """ + This is a class for computing Betti curves from a list of persistence diagrams. A Betti curve is a 1D piecewise-constant function obtained from the rank function. It is sampled uniformly on a given range and the vector of samples is returned. See https://www.researchgate.net/publication/316604237_Time_Series_Classification_via_Topological_Data_Analysis for more details. + """ + def __init__(self, resolution=100, sample_range=[np.nan, np.nan]): + """ + Constructor for the BettiCurve class. + + Parameters: + resolution (int): number of sample for the piecewise-constant function (default 100). + sample_range ([double, double]): minimum and maximum of the piecewise-constant function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + """ + self.resolution, self.sample_range = resolution, sample_range + + def fit(self, X, y=None): + """ + Fit the BettiCurve class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.sample_range)).any(): + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) + return self + + def transform(self, X): + """ + Compute the Betti curve for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array with shape (number of diagrams) x (**resolution**): output Betti curves. + """ + num_diag, Xfit = len(X), [] + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) + step_x = x_values[1] - x_values[0] + + for i in range(num_diag): + + diagram, num_pts_in_diag = X[i], X[i].shape[0] + + bc = np.zeros(self.resolution) + for j in range(num_pts_in_diag): + [px,py] = diagram[j,:2] + min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + for k in range(min_idx, max_idx): + bc[k] += 1 + + Xfit.append(np.reshape(bc,[1,-1])) + + Xfit = np.concatenate(Xfit, 0) + + return Xfit + +class Entropy(BaseEstimator, TransformerMixin): + """ + This is a class for computing persistence entropy. Persistence entropy is a statistic for persistence diagrams inspired from Shannon entropy. This statistic can also be used to compute a feature vector, called the entropy summary function. See https://arxiv.org/pdf/1803.08304.pdf for more details. Note that a previous implementation was contributed by Manuel Soriano-Trigueros. + """ + def __init__(self, mode="scalar", normalized=True, resolution=100, sample_range=[np.nan, np.nan]): + """ + Constructor for the Entropy class. + + Parameters: + mode (string): what entropy to compute: either "scalar" for computing the entropy statistics, or "vector" for computing the entropy summary functions (default "scalar"). + normalized (bool): whether to normalize the entropy summary function (default True). Used only if **mode** = "vector". + resolution (int): number of sample for the entropy summary function (default 100). Used only if **mode** = "vector". + sample_range ([double, double]): minimum and maximum of the entropy summary function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. Used only if **mode** = "vector". + """ + self.mode, self.normalized, self.resolution, self.sample_range = mode, normalized, resolution, sample_range + + def fit(self, X, y=None): + """ + Fit the Entropy class on a list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.sample_range)).any(): + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) + return self + + def transform(self, X): + """ + Compute the entropy for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array with shape (number of diagrams) x (1 if **mode** = "scalar" else **resolution**): output entropy. + """ + num_diag, Xfit = len(X), [] + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) + step_x = x_values[1] - x_values[0] + new_X = BirthPersistenceTransform().fit_transform(X) + + for i in range(num_diag): + + orig_diagram, diagram, num_pts_in_diag = X[i], new_X[i], X[i].shape[0] + new_diagram = DiagramScaler(use=True, scalers=[([1], MaxAbsScaler())]).fit_transform([diagram])[0] + + if self.mode == "scalar": + ent = - np.sum( np.multiply(new_diagram[:,1], np.log(new_diagram[:,1])) ) + Xfit.append(np.array([[ent]])) + + else: + ent = np.zeros(self.resolution) + for j in range(num_pts_in_diag): + [px,py] = orig_diagram[j,:2] + min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + for k in range(min_idx, max_idx): + ent[k] += (-1) * new_diagram[j,1] * np.log(new_diagram[j,1]) + if self.normalized: + ent = ent / np.linalg.norm(ent, ord=1) + Xfit.append(np.reshape(ent,[1,-1])) + + Xfit = np.concatenate(Xfit, 0) + + return Xfit + +class TopologicalVector(BaseEstimator, TransformerMixin): + """ + This is a class for computing topological vectors from a list of persistence diagrams. The topological vector associated to a persistence diagram is the sorted vector of a slight modification of the pairwise distances between the persistence diagram points. See https://diglib.eg.org/handle/10.1111/cgf12692 for more details. + """ + def __init__(self, threshold=10): + """ + Constructor for the TopologicalVector class. + + Parameters: + threshold (int): number of distances to keep (default 10). This is the dimension of the topological vector. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding topological vector as threshold. + """ + self.threshold = threshold + + def fit(self, X, y=None): + """ + Fit the TopologicalVector class on a list of persistence diagrams (this function actually does nothing but is useful when TopologicalVector is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + Compute the topological vector for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array with shape (number of diagrams) x (**threshold**): output topological vectors. + """ + if self.threshold == -1: + thresh = np.array([X[i].shape[0] for i in range(len(X))]).max() + else: + thresh = self.threshold + + num_diag = len(X) + Xfit = np.zeros([num_diag, thresh]) + + for i in range(num_diag): + + diagram, num_pts_in_diag = X[i], X[i].shape[0] + pers = 0.5 * (diagram[:,1]-diagram[:,0]) + min_pers = np.minimum(pers,np.transpose(pers)) + distances = DistanceMetric.get_metric("chebyshev").pairwise(diagram) + vect = np.flip(np.sort(np.triu(np.minimum(distances, min_pers)), axis=None), 0) + dim = min(len(vect), thresh) + Xfit[i, :dim] = vect[:dim] + + return Xfit + +class ComplexPolynomial(BaseEstimator, TransformerMixin): + """ + This is a class for computing complex polynomials from a list of persistence diagrams. The persistence diagram points are seen as the roots of some complex polynomial, whose coefficients are returned in a complex vector. See https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27 for more details. + """ + def __init__(self, polynomial_type="R", threshold=10): + """ + Constructor for the ComplexPolynomial class. + + Parameters: + polynomial_type (char): either "R", "S" or "T" (default "R"). Type of complex polynomial that is going to be computed (explained in https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27). + threshold (int): number of coefficients (default 10). This is the dimension of the complex vector of coefficients, i.e. the number of coefficients corresponding to the largest degree terms of the polynomial. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding complex vector of coefficients as threshold. + """ + self.threshold, self.polynomial_type = threshold, polynomial_type + + def fit(self, X, y=None): + """ + Fit the ComplexPolynomial class on a list of persistence diagrams (this function actually does nothing but is useful when ComplexPolynomial is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + Compute the complex vector of coefficients for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array with shape (number of diagrams) x (**threshold**): output complex vectors of coefficients. + """ + if self.threshold == -1: + thresh = np.array([X[i].shape[0] for i in range(len(X))]).max() + else: + thresh = self.threshold + + Xfit = np.zeros([len(X), thresh]) + 1j * np.zeros([len(X), thresh]) + for d in range(len(X)): + D, N = X[d], X[d].shape[0] + if self.polynomial_type == "R": + roots = D[:,0] + 1j * D[:,1] + elif self.polynomial_type == "S": + alpha = np.linalg.norm(D, axis=1) + alpha = np.where(alpha==0, np.ones(N), alpha) + roots = np.multiply( np.multiply( (D[:,0]+1j*D[:,1]), (D[:,1]-D[:,0]) ), 1./(np.sqrt(2)*alpha) ) + elif self.polynomial_type == "T": + alpha = np.linalg.norm(D, axis=1) + roots = np.multiply( (D[:,1]-D[:,0])/2, np.cos(alpha) - np.sin(alpha) + 1j * (np.cos(alpha) + np.sin(alpha)) ) + coeff = [0] * (N+1) + coeff[N] = 1 + for i in range(1, N+1): + for j in range(N-i-1, N): + coeff[j] += ((-1) * roots[i-1] * coeff[j+1]) + coeff = np.array(coeff[::-1])[1:] + Xfit[d, :min(thresh, coeff.shape[0])] = coeff[:min(thresh, coeff.shape[0])] + return Xfit diff --git a/src/python/gudhi/sktda/__init__.py b/src/python/gudhi/sktda/__init__.py deleted file mode 100644 index f020248d..00000000 --- a/src/python/gudhi/sktda/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from .kernel_methods import * -from .metrics import * -from .preprocessing import * -from .vector_methods import * - -__all__ = ["kernel_methods", "metrics", "preprocessing", "vector_methods"] diff --git a/src/python/gudhi/sktda/kernel_methods.py b/src/python/gudhi/sktda/kernel_methods.py deleted file mode 100644 index c855d2be..00000000 --- a/src/python/gudhi/sktda/kernel_methods.py +++ /dev/null @@ -1,206 +0,0 @@ -# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. -# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. -# Author(s): Mathieu Carrière -# -# Copyright (C) 2018-2019 Inria -# -# Modification(s): -# - YYYY/MM Author: Description of the modification - -import numpy as np -from sklearn.base import BaseEstimator, TransformerMixin -from sklearn.metrics import pairwise_distances -from .metrics import SlicedWassersteinDistance, PersistenceFisherDistance - -############################################# -# Kernel methods ############################ -############################################# - -class SlicedWassersteinKernel(BaseEstimator, TransformerMixin): - """ - This is a class for computing the sliced Wasserstein kernel matrix from a list of persistence diagrams. The sliced Wasserstein kernel is computed by exponentiating the corresponding sliced Wasserstein distance with a Gaussian kernel. See http://proceedings.mlr.press/v70/carriere17a.html for more details. - """ - def __init__(self, num_directions=10, bandwidth=1.0): - """ - Constructor for the SlicedWassersteinKernel class. - - Parameters: - bandwidth (double): bandwidth of the Gaussian kernel applied to the sliced Wasserstein distance (default 1.). - num_directions (int): number of lines evenly sampled from [-pi/2,pi/2] in order to approximate and speed up the kernel computation (default 10). - """ - self.bandwidth = bandwidth - self.sw_ = SlicedWassersteinDistance(num_directions=num_directions) - - def fit(self, X, y=None): - """ - Fit the SlicedWassersteinKernel class on a list of persistence diagrams: an instance of the SlicedWassersteinDistance class is fitted on the diagrams and then stored. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.sw_.fit(X, y) - return self - - def transform(self, X): - """ - Compute all sliced Wasserstein kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise sliced Wasserstein kernel values. - """ - return np.exp(-self.sw_.transform(X)/self.bandwidth) - -class PersistenceWeightedGaussianKernel(BaseEstimator, TransformerMixin): - """ - This is a class for computing the persistence weighted Gaussian kernel matrix from a list of persistence diagrams. The persistence weighted Gaussian kernel is computed by convolving the persistence diagram points with weighted Gaussian kernels. See http://proceedings.mlr.press/v48/kusano16.html for more details. - """ - def __init__(self, bandwidth=1., weight=lambda x: 1, kernel_approx=None): - """ - Constructor for the PersistenceWeightedGaussianKernel class. - - Parameters: - bandwidth (double): bandwidth of the Gaussian kernel with which persistence diagrams will be convolved (default 1.) - weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie lists or numpy arrays of the form [p_x,p_y]. - kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). - """ - self.bandwidth, self.weight = bandwidth, weight - self.kernel_approx = kernel_approx - - def fit(self, X, y=None): - """ - Fit the PersistenceWeightedGaussianKernel class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams** and the kernel approximation class (if not None) is applied on them. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.diagrams_ = list(X) - self.ws_ = [ np.array([self.weight(self.diagrams_[i][j,:]) for j in range(self.diagrams_[i].shape[0])]) for i in range(len(self.diagrams_)) ] - if self.kernel_approx is not None: - self.approx_ = np.concatenate([np.sum(np.multiply(self.ws_[i][:,np.newaxis], self.kernel_approx.transform(self.diagrams_[i])), axis=0)[np.newaxis,:] for i in range(len(self.diagrams_))]) - return self - - def transform(self, X): - """ - Compute all persistence weighted Gaussian kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise persistence weighted Gaussian kernel values. - """ - Xp = list(X) - Xfit = np.zeros((len(Xp), len(self.diagrams_))) - if len(self.diagrams_) == len(Xp) and np.all([np.array_equal(self.diagrams_[i], Xp[i]) for i in range(len(Xp))]): - if self.kernel_approx is not None: - Xfit = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.matmul(self.approx_, self.approx_.T) - else: - for i in range(len(self.diagrams_)): - for j in range(i+1, len(self.diagrams_)): - W = np.matmul(self.ws_[i][:,np.newaxis], self.ws_[j][np.newaxis,:]) - E = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.exp(-np.square(pairwise_distances(self.diagrams_[i], self.diagrams_[j]))/(2*np.square(self.bandwidth))) - Xfit[i,j] = np.sum(np.multiply(W, E)) - Xfit[j,i] = Xfit[i,j] - else: - ws = [ np.array([self.weight(Xp[i][j,:]) for j in range(Xp[i].shape[0])]) for i in range(len(Xp)) ] - if self.kernel_approx is not None: - approx = np.concatenate([np.sum(np.multiply(ws[i][:,np.newaxis], self.kernel_approx.transform(Xp[i])), axis=0)[np.newaxis,:] for i in range(len(Xp))]) - Xfit = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.matmul(approx, self.approx_.T) - else: - for i in range(len(Xp)): - for j in range(len(self.diagrams_)): - W = np.matmul(ws[i][:,np.newaxis], self.ws_[j][np.newaxis,:]) - E = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.exp(-np.square(pairwise_distances(Xp[i], self.diagrams_[j]))/(2*np.square(self.bandwidth))) - Xfit[i,j] = np.sum(np.multiply(W, E)) - - return Xfit - -class PersistenceScaleSpaceKernel(BaseEstimator, TransformerMixin): - """ - This is a class for computing the persistence scale space kernel matrix from a list of persistence diagrams. The persistence scale space kernel is computed by adding the symmetric to the diagonal of each point in each persistence diagram, with negative weight, and then convolving the points with a Gaussian kernel. See https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Reininghaus_A_Stable_Multi-Scale_2015_CVPR_paper.pdf for more details. - """ - def __init__(self, bandwidth=1., kernel_approx=None): - """ - Constructor for the PersistenceScaleSpaceKernel class. - - Parameters: - bandwidth (double): bandwidth of the Gaussian kernel with which persistence diagrams will be convolved (default 1.) - kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). - """ - self.pwg_ = PersistenceWeightedGaussianKernel(bandwidth=bandwidth, weight=lambda x: 1 if x[1] >= x[0] else -1, kernel_approx=kernel_approx) - - def fit(self, X, y=None): - """ - Fit the PersistenceScaleSpaceKernel class on a list of persistence diagrams: symmetric to the diagonal of all points are computed and an instance of the PersistenceWeightedGaussianKernel class is fitted on the diagrams and then stored. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.diagrams_ = list(X) - for i in range(len(self.diagrams_)): - op_D = self.diagrams_[i][:,[1,0]] - self.diagrams_[i] = np.concatenate([self.diagrams_[i], op_D], axis=0) - self.pwg_.fit(X) - return self - - def transform(self, X): - """ - Compute all persistence scale space kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise persistence scale space kernel values. - """ - Xp = list(X) - for i in range(len(Xp)): - op_X = np.matmul(Xp[i], np.array([[0.,1.], [1.,0.]])) - Xp[i] = np.concatenate([Xp[i], op_X], axis=0) - return self.pwg_.transform(Xp) - -class PersistenceFisherKernel(BaseEstimator, TransformerMixin): - """ - This is a class for computing the persistence Fisher kernel matrix from a list of persistence diagrams. The persistence Fisher kernel is computed by exponentiating the corresponding persistence Fisher distance with a Gaussian kernel. See papers.nips.cc/paper/8205-persistence-fisher-kernel-a-riemannian-manifold-kernel-for-persistence-diagrams for more details. - """ - def __init__(self, bandwidth_fisher=1., bandwidth=1., kernel_approx=None): - """ - Constructor for the PersistenceFisherKernel class. - - Parameters: - bandwidth (double): bandwidth of the Gaussian kernel applied to the persistence Fisher distance (default 1.). - bandwidth_fisher (double): bandwidth of the Gaussian kernel used to turn persistence diagrams into probability distributions by PersistenceFisherDistance class (default 1.). - kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). - """ - self.bandwidth = bandwidth - self.pf_ = PersistenceFisherDistance(bandwidth=bandwidth_fisher, kernel_approx=kernel_approx) - - def fit(self, X, y=None): - """ - Fit the PersistenceFisherKernel class on a list of persistence diagrams: an instance of the PersistenceFisherDistance class is fitted on the diagrams and then stored. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.pf_.fit(X, y) - return self - - def transform(self, X): - """ - Compute all persistence Fisher kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise persistence Fisher kernel values. - """ - return np.exp(-self.pf_.transform(X)/self.bandwidth) - diff --git a/src/python/gudhi/sktda/metrics.py b/src/python/gudhi/sktda/metrics.py deleted file mode 100644 index c512cb82..00000000 --- a/src/python/gudhi/sktda/metrics.py +++ /dev/null @@ -1,243 +0,0 @@ -# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. -# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. -# Author(s): Mathieu Carrière -# -# Copyright (C) 2018-2019 Inria -# -# Modification(s): -# - YYYY/MM Author: Description of the modification - -import numpy as np -from sklearn.base import BaseEstimator, TransformerMixin -from sklearn.metrics import pairwise_distances -try: - from .. import bottleneck_distance - USE_GUDHI = True -except ImportError: - USE_GUDHI = False - print("Gudhi built without CGAL: BottleneckDistance will return a null matrix") - -############################################# -# Metrics ################################### -############################################# - -class SlicedWassersteinDistance(BaseEstimator, TransformerMixin): - """ - This is a class for computing the sliced Wasserstein distance matrix from a list of persistence diagrams. The Sliced Wasserstein distance is computed by projecting the persistence diagrams onto lines, comparing the projections with the 1-norm, and finally integrating over all possible lines. See http://proceedings.mlr.press/v70/carriere17a.html for more details. - """ - def __init__(self, num_directions=10): - """ - Constructor for the SlicedWassersteinDistance class. - - Parameters: - num_directions (int): number of lines evenly sampled from [-pi/2,pi/2] in order to approximate and speed up the distance computation (default 10). - """ - self.num_directions = num_directions - thetas = np.linspace(-np.pi/2, np.pi/2, num=self.num_directions+1)[np.newaxis,:-1] - self.lines_ = np.concatenate([np.cos(thetas), np.sin(thetas)], axis=0) - - def fit(self, X, y=None): - """ - Fit the SlicedWassersteinDistance class on a list of persistence diagrams: persistence diagrams are projected onto the different lines. The diagrams themselves and their projections are then stored in numpy arrays, called **diagrams_** and **approx_diag_**. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.diagrams_ = X - self.approx_ = [np.matmul(X[i], self.lines_) for i in range(len(X))] - diag_proj = (1./2) * np.ones((2,2)) - self.approx_diag_ = [np.matmul(np.matmul(X[i], diag_proj), self.lines_) for i in range(len(X))] - return self - - def transform(self, X): - """ - Compute all sliced Wasserstein distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise sliced Wasserstein distances. - """ - Xfit = np.zeros((len(X), len(self.approx_))) - if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): - for i in range(len(self.approx_)): - for j in range(i+1, len(self.approx_)): - A = np.sort(np.concatenate([self.approx_[i], self.approx_diag_[j]], axis=0), axis=0) - B = np.sort(np.concatenate([self.approx_[j], self.approx_diag_[i]], axis=0), axis=0) - L1 = np.sum(np.abs(A-B), axis=0) - Xfit[i,j] = np.mean(L1) - Xfit[j,i] = Xfit[i,j] - else: - diag_proj = (1./2) * np.ones((2,2)) - approx = [np.matmul(X[i], self.lines_) for i in range(len(X))] - approx_diag = [np.matmul(np.matmul(X[i], diag_proj), self.lines_) for i in range(len(X))] - for i in range(len(approx)): - for j in range(len(self.approx_)): - A = np.sort(np.concatenate([approx[i], self.approx_diag_[j]], axis=0), axis=0) - B = np.sort(np.concatenate([self.approx_[j], approx_diag[i]], axis=0), axis=0) - L1 = np.sum(np.abs(A-B), axis=0) - Xfit[i,j] = np.mean(L1) - - return Xfit - -class BottleneckDistance(BaseEstimator, TransformerMixin): - """ - This is a class for computing the bottleneck distance matrix from a list of persistence diagrams. - """ - def __init__(self, epsilon=1e-3): - """ - Constructor for the BottleneckDistance class. - - Parameters: - epsilon (double): approximation quality (default 1e-4). - """ - self.epsilon = epsilon - - def fit(self, X, y=None): - """ - Fit the BottleneckDistance class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams**. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.diagrams_ = X - return self - - def transform(self, X): - """ - Compute all bottleneck distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise bottleneck distances. - """ - num_diag1 = len(X) - - if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): - matrix = np.zeros((num_diag1, num_diag1)) - - if USE_GUDHI: - for i in range(num_diag1): - for j in range(i+1, num_diag1): - matrix[i,j] = bottleneck_distance(X[i], X[j], self.epsilon) - matrix[j,i] = matrix[i,j] - else: - print("Gudhi required---returning null matrix") - - else: - num_diag2 = len(self.diagrams_) - matrix = np.zeros((num_diag1, num_diag2)) - - if USE_GUDHI: - for i in range(num_diag1): - for j in range(num_diag2): - matrix[i,j] = bottleneck_distance(X[i], self.diagrams_[j], self.epsilon) - else: - print("Gudhi required---returning null matrix") - - Xfit = matrix - - return Xfit - -class PersistenceFisherDistance(BaseEstimator, TransformerMixin): - """ - This is a class for computing the persistence Fisher distance matrix from a list of persistence diagrams. The persistence Fisher distance is obtained by computing the original Fisher distance between the probability distributions associated to the persistence diagrams given by convolving them with a Gaussian kernel. See http://papers.nips.cc/paper/8205-persistence-fisher-kernel-a-riemannian-manifold-kernel-for-persistence-diagrams for more details. - """ - def __init__(self, bandwidth=1., kernel_approx=None): - """ - Constructor for the PersistenceFisherDistance class. - - Parameters: - bandwidth (double): bandwidth of the Gaussian kernel used to turn persistence diagrams into probability distributions (default 1.). - kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). - """ - self.bandwidth, self.kernel_approx = bandwidth, kernel_approx - - def fit(self, X, y=None): - """ - Fit the PersistenceFisherDistance class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams** and the kernel approximation class (if not None) is applied on them. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.diagrams_ = X - projection = (1./2) * np.ones((2,2)) - self.diagonal_projections_ = [np.matmul(X[i], projection) for i in range(len(X))] - if self.kernel_approx is not None: - self.approx_ = [self.kernel_approx.transform(X[i]) for i in range(len(X))] - self.approx_diagonal_ = [self.kernel_approx.transform(self.diagonal_projections_[i]) for i in range(len(X))] - return self - - def transform(self, X): - """ - Compute all persistence Fisher distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise persistence Fisher distances. - """ - Xfit = np.zeros((len(X), len(self.diagrams_))) - if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): - for i in range(len(self.diagrams_)): - for j in range(i+1, len(self.diagrams_)): - if self.kernel_approx is not None: - Z = np.concatenate([self.approx_[i], self.approx_diagonal_[i], self.approx_[j], self.approx_diagonal_[j]], axis=0) - U, V = np.sum(np.concatenate([self.approx_[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], self.approx_diagonal_[i]], axis=0), axis=0) - vectori, vectorj = np.abs(np.matmul(Z, U.T)), np.abs(np.matmul(Z, V.T)) - vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) - if vectori_sum != 0: - vectori = vectori/vectori_sum - if vectorj_sum != 0: - vectorj = vectorj/vectorj_sum - Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) - Xfit[j,i] = Xfit[i,j] - else: - Z = np.concatenate([self.diagrams_[i], self.diagonal_projections_[i], self.diagrams_[j], self.diagonal_projections_[j]], axis=0) - U, V = np.concatenate([self.diagrams_[i], self.diagonal_projections_[j]], axis=0), np.concatenate([self.diagrams_[j], self.diagonal_projections_[i]], axis=0) - vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) - vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) - vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) - if vectori_sum != 0: - vectori = vectori/vectori_sum - if vectorj_sum != 0: - vectorj = vectorj/vectorj_sum - Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) - Xfit[j,i] = Xfit[i,j] - else: - projection = (1./2) * np.ones((2,2)) - diagonal_projections = [np.matmul(X[i], projection) for i in range(len(X))] - if self.kernel_approx is not None: - approx = [self.kernel_approx.transform(X[i]) for i in range(len(X))] - approx_diagonal = [self.kernel_approx.transform(diagonal_projections[i]) for i in range(len(X))] - for i in range(len(X)): - for j in range(len(self.diagrams_)): - if self.kernel_approx is not None: - Z = np.concatenate([approx[i], approx_diagonal[i], self.approx_[j], self.approx_diagonal_[j]], axis=0) - U, V = np.sum(np.concatenate([approx[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], approx_diagonal[i]], axis=0), axis=0) - vectori, vectorj = np.abs(np.matmul(Z, U.T)), np.abs(np.matmul(Z, V.T)) - vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) - if vectori_sum != 0: - vectori = vectori/vectori_sum - if vectorj_sum != 0: - vectorj = vectorj/vectorj_sum - Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) - else: - Z = np.concatenate([X[i], diagonal_projections[i], self.diagrams_[j], self.diagonal_projections_[j]], axis=0) - U, V = np.concatenate([X[i], self.diagonal_projections_[j]], axis=0), np.concatenate([self.diagrams_[j], diagonal_projections[i]], axis=0) - vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) - vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) - vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) - if vectori_sum != 0: - vectori = vectori/vectori_sum - if vectorj_sum != 0: - vectorj = vectorj/vectorj_sum - Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) - return Xfit diff --git a/src/python/gudhi/sktda/preprocessing.py b/src/python/gudhi/sktda/preprocessing.py deleted file mode 100644 index 83227ca1..00000000 --- a/src/python/gudhi/sktda/preprocessing.py +++ /dev/null @@ -1,305 +0,0 @@ -# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. -# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. -# Author(s): Mathieu Carrière -# -# Copyright (C) 2018-2019 Inria -# -# Modification(s): -# - YYYY/MM Author: Description of the modification - -import numpy as np -from sklearn.base import BaseEstimator, TransformerMixin -from sklearn.preprocessing import StandardScaler - -############################################# -# Preprocessing ############################# -############################################# - -class BirthPersistenceTransform(BaseEstimator, TransformerMixin): - """ - This is a class for the affine transformation (x,y) -> (x,y-x) to be applied on persistence diagrams. - """ - def __init__(self): - """ - Constructor for BirthPersistenceTransform class. - """ - return None - - def fit(self, X, y=None): - """ - Fit the BirthPersistenceTransform class on a list of persistence diagrams (this function actually does nothing but is useful when BirthPersistenceTransform is included in a scikit-learn Pipeline). - - Parameters: - X (n x 2 numpy array): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - return self - - def transform(self, X): - """ - Apply the BirthPersistenceTransform function on the persistence diagrams. - - Parameters: - X (list of n x 2 numpy array): input persistence diagrams. - - Returns: - list of n x 2 numpy array: transformed persistence diagrams. - """ - Xfit = [] - for diag in X: - #new_diag = np.empty(diag.shape) - #np.copyto(new_diag, diag) - new_diag = np.copy(diag) - new_diag[:,1] = new_diag[:,1] - new_diag[:,0] - Xfit.append(new_diag) - return Xfit - -class Clamping(BaseEstimator, TransformerMixin): - """ - This is a class for clamping values. It can be used as a parameter for the DiagramScaler class, for instance if you want to clamp abscissae or ordinates of persistence diagrams. - """ - def __init__(self, limit=np.inf): - """ - Constructor for the Clamping class. - - Parameters: - limit (double): clamping value (default np.inf). - """ - self.limit = limit - - def fit(self, X, y=None): - """ - Fit the Clamping class on a list of values (this function actually does nothing but is useful when Clamping is included in a scikit-learn Pipeline). - - Parameters: - X (numpy array of size n): input values. - y (n x 1 array): value labels (unused). - """ - return self - - def transform(self, X): - """ - Clamp list of values. - - Parameters: - X (numpy array of size n): input list of values. - - Returns: - numpy array of size n: output list of values. - """ - Xfit = np.minimum(X, self.limit) - #Xfit = np.where(X >= self.limit, self.limit * np.ones(X.shape), X) - return Xfit - -class DiagramScaler(BaseEstimator, TransformerMixin): - """ - This is a class for preprocessing persistence diagrams with a given list of scalers, such as those included in scikit-learn. - """ - def __init__(self, use=False, scalers=[]): - """ - Constructor for the DiagramScaler class. - - Parameters: - use (bool): whether to use the class or not (default False). - scalers (list of classes): list of scalers to be fit on the persistence diagrams (default []). Each element of the list is a tuple with two elements: the first one is a list of coordinates, and the second one is a scaler (i.e. a class with fit() and transform() methods) that is going to be applied to these coordinates. Common scalers can be found in the scikit-learn library (such as MinMaxScaler for instance). - """ - self.scalers = scalers - self.use = use - - def fit(self, X, y=None): - """ - Fit the DiagramScaler class on a list of persistence diagrams: persistence diagrams are concatenated in a big numpy array, and scalers are fit (by calling their fit() method) on their corresponding coordinates in this big array. - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - if self.use: - if len(X) == 1: - P = X[0] - else: - P = np.concatenate(X,0) - for (indices, scaler) in self.scalers: - scaler.fit(np.reshape(P[:,indices], [-1, 1])) - return self - - def transform(self, X): - """ - Apply the DiagramScaler function on the persistence diagrams. The fitted scalers are applied (by calling their transform() method) to their corresponding coordinates in each persistence diagram individually. - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - - Returns: - list of n x 2 or n x 1 numpy arrays: transformed persistence diagrams. - """ - Xfit = [np.copy(d) for d in X] - if self.use: - for i in range(len(Xfit)): - if Xfit[i].shape[0] > 0: - for (indices, scaler) in self.scalers: - for I in indices: - Xfit[i][:,I] = np.squeeze(scaler.transform(np.reshape(Xfit[i][:,I], [-1,1]))) - return Xfit - -class Padding(BaseEstimator, TransformerMixin): - """ - This is a class for padding a list of persistence diagrams with dummy points, so that all persistence diagrams end up with the same number of points. - """ - def __init__(self, use=False): - """ - Constructor for the Padding class. - - Parameters: - use (bool): whether to use the class or not (default False). - """ - self.use = use - - def fit(self, X, y=None): - """ - Fit the Padding class on a list of persistence diagrams (this function actually does nothing but is useful when Padding is included in a scikit-learn Pipeline). - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.max_pts = max([len(diag) for diag in X]) - return self - - def transform(self, X): - """ - Add dummy points to each persistence diagram so that they all have the same cardinality. All points are given an additional coordinate indicating if the point was added after padding (0) or already present before (1). - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - - Returns: - list of n x 3 or n x 2 numpy arrays: padded persistence diagrams. - """ - if self.use: - Xfit, num_diag = [], len(X) - for diag in X: - diag_pad = np.pad(diag, ((0,max(0, self.max_pts - diag.shape[0])), (0,1)), "constant", constant_values=((0,0),(0,0))) - diag_pad[:diag.shape[0],2] = np.ones(diag.shape[0]) - Xfit.append(diag_pad) - else: - Xfit = X - return Xfit - -class ProminentPoints(BaseEstimator, TransformerMixin): - """ - This is a class for removing points that are close or far from the diagonal in persistence diagrams. If persistence diagrams are n x 2 numpy arrays (i.e. persistence diagrams with ordinary features), points are ordered and thresholded by distance-to-diagonal. If persistence diagrams are n x 1 numpy arrays (i.e. persistence diagrams with essential features), points are not ordered and thresholded by first coordinate. - """ - def __init__(self, use=False, num_pts=10, threshold=-1, location="upper"): - """ - Constructor for the ProminentPoints class. - - Parameters: - use (bool): whether to use the class or not (default False). - location (string): either "upper" or "lower" (default "upper"). Whether to keep the points that are far away ("upper") or close ("lower") to the diagonal. - num_pts (int): cardinality threshold (default 10). If location == "upper", keep the top **num_pts** points that are the farthest away from the diagonal. If location == "lower", keep the top **num_pts** points that are the closest to the diagonal. - threshold (double): distance-to-diagonal threshold (default -1). If location == "upper", keep the points that are at least at a distance **threshold** from the diagonal. If location == "lower", keep the points that are at most at a distance **threshold** from the diagonal. - """ - self.num_pts = num_pts - self.threshold = threshold - self.use = use - self.location = location - - def fit(self, X, y=None): - """ - Fit the ProminentPoints class on a list of persistence diagrams (this function actually does nothing but is useful when ProminentPoints is included in a scikit-learn Pipeline). - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - return self - - def transform(self, X): - """ - If location == "upper", first select the top **num_pts** points that are the farthest away from the diagonal, then select and return from these points the ones that are at least at distance **threshold** from the diagonal for each persistence diagram individually. If location == "lower", first select the top **num_pts** points that are the closest to the diagonal, then select and return from these points the ones that are at most at distance **threshold** from the diagonal for each persistence diagram individually. - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - - Returns: - list of n x 2 or n x 1 numpy arrays: thresholded persistence diagrams. - """ - if self.use: - Xfit, num_diag = [], len(X) - for i in range(num_diag): - diag = X[i] - if diag.shape[1] >= 2: - if diag.shape[0] > 0: - pers = np.abs(diag[:,1] - diag[:,0]) - idx_thresh = pers >= self.threshold - thresh_diag, thresh_pers = diag[idx_thresh], pers[idx_thresh] - sort_index = np.flip(np.argsort(thresh_pers, axis=None), 0) - if self.location == "upper": - new_diag = thresh_diag[sort_index[:min(self.num_pts, thresh_diag.shape[0])],:] - if self.location == "lower": - new_diag = np.concatenate( [ thresh_diag[sort_index[min(self.num_pts, thresh_diag.shape[0]):],:], diag[~idx_thresh] ], axis=0) - else: - new_diag = diag - - else: - if diag.shape[0] > 0: - birth = diag[:,:1] - idx_thresh = birth >= self.threshold - thresh_diag, thresh_birth = diag[idx_thresh], birth[idx_thresh] - if self.location == "upper": - new_diag = thresh_diag[:min(self.num_pts, thresh_diag.shape[0]),:] - if self.location == "lower": - new_diag = np.concatenate( [ thresh_diag[min(self.num_pts, thresh_diag.shape[0]):,:], diag[~idx_thresh] ], axis=0) - else: - new_diag = diag - - Xfit.append(new_diag) - else: - Xfit = X - return Xfit - -class DiagramSelector(BaseEstimator, TransformerMixin): - """ - This is a class for extracting finite or essential points in persistence diagrams. - """ - def __init__(self, use=False, limit=np.inf, point_type="finite"): - """ - Constructor for the DiagramSelector class. - - Parameters: - use (bool): whether to use the class or not (default False). - limit (double): second coordinate value that is the criterion for being an essential point (default numpy.inf). - point_type (string): either "finite" or "essential". The type of the points that are going to be extracted. - """ - self.use, self.limit, self.point_type = use, limit, point_type - - def fit(self, X, y=None): - """ - Fit the DiagramSelector class on a list of persistence diagrams (this function actually does nothing but is useful when DiagramSelector is included in a scikit-learn Pipeline). - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - return self - - def transform(self, X): - """ - Extract and return the finite or essential points of each persistence diagram individually. - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - - Returns: - list of n x 2 or n x 1 numpy arrays: extracted persistence diagrams. - """ - if self.use: - Xfit, num_diag = [], len(X) - if self.point_type == "finite": - Xfit = [ diag[diag[:,1] < self.limit] if diag.shape[0] != 0 else diag for diag in X] - else: - Xfit = [ diag[diag[:,1] >= self.limit, 0:1] if diag.shape[0] != 0 else diag for diag in X] - else: - Xfit = X - return Xfit diff --git a/src/python/gudhi/sktda/vector_methods.py b/src/python/gudhi/sktda/vector_methods.py deleted file mode 100644 index bf32f18e..00000000 --- a/src/python/gudhi/sktda/vector_methods.py +++ /dev/null @@ -1,485 +0,0 @@ -# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. -# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. -# Author(s): Mathieu Carrière -# -# Copyright (C) 2018-2019 Inria -# -# Modification(s): -# - YYYY/MM Author: Description of the modification - -import numpy as np -from sklearn.base import BaseEstimator, TransformerMixin -from sklearn.preprocessing import MinMaxScaler, MaxAbsScaler -from sklearn.neighbors import DistanceMetric - -from .preprocessing import DiagramScaler, BirthPersistenceTransform - -############################################# -# Finite Vectorization methods ############## -############################################# - -class PersistenceImage(BaseEstimator, TransformerMixin): - """ - This is a class for computing persistence images from a list of persistence diagrams. A persistence image is a 2D function computed from a persistence diagram by convolving the diagram points with a weighted Gaussian kernel. The plane is then discretized into an image with pixels, which is flattened and returned as a vector. See http://jmlr.org/papers/v18/16-337.html for more details. - """ - def __init__(self, bandwidth=1., weight=lambda x: 1, resolution=[20,20], im_range=[np.nan, np.nan, np.nan, np.nan]): - """ - Constructor for the PersistenceImage class. - - Parameters: - bandwidth (double): bandwidth of the Gaussian kernel (default 1.). - weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie lists or numpy arrays of the form [p_x,p_y]. - resolution ([int,int]): size (in pixels) of the persistence image (default [20,20]). - im_range ([double,double,double,double]): minimum and maximum of each axis of the persistence image, of the form [x_min, x_max, y_min, y_max] (default [numpy.nan, numpy.nan, numpy.nan, numpyp.nan]). If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. - """ - self.bandwidth, self.weight = bandwidth, weight - self.resolution, self.im_range = resolution, im_range - - def fit(self, X, y=None): - """ - Fit the PersistenceImage class on a list of persistence diagrams: if any of the values in **im_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - if np.isnan(np.array(self.im_range)).any(): - new_X = BirthPersistenceTransform().fit_transform(X) - pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(new_X,y) - [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] - self.im_range = np.where(np.isnan(np.array(self.im_range)), np.array([mx, Mx, my, My]), np.array(self.im_range)) - return self - - def transform(self, X): - """ - Compute the persistence image for each persistence diagram individually and store the results in a single numpy array. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array with shape (number of diagrams) x (number of pixels = **resolution[0]** x **resolution[1]**): output persistence images. - """ - num_diag, Xfit = len(X), [] - new_X = BirthPersistenceTransform().fit_transform(X) - - for i in range(num_diag): - - diagram, num_pts_in_diag = new_X[i], X[i].shape[0] - - w = np.empty(num_pts_in_diag) - for j in range(num_pts_in_diag): - w[j] = self.weight(diagram[j,:]) - - x_values, y_values = np.linspace(self.im_range[0], self.im_range[1], self.resolution[0]), np.linspace(self.im_range[2], self.im_range[3], self.resolution[1]) - Xs, Ys = np.tile((diagram[:,0][:,np.newaxis,np.newaxis]-x_values[np.newaxis,np.newaxis,:]),[1,self.resolution[1],1]), np.tile(diagram[:,1][:,np.newaxis,np.newaxis]-y_values[np.newaxis,:,np.newaxis],[1,1,self.resolution[0]]) - image = np.tensordot(w, np.exp((-np.square(Xs)-np.square(Ys))/(2*np.square(self.bandwidth)))/(np.square(self.bandwidth)*2*np.pi), 1) - - Xfit.append(image.flatten()[np.newaxis,:]) - - Xfit = np.concatenate(Xfit,0) - - return Xfit - -class Landscape(BaseEstimator, TransformerMixin): - """ - This is a class for computing persistence landscapes from a list of persistence diagrams. A persistence landscape is a collection of 1D piecewise-linear functions computed from the rank function associated to the persistence diagram. These piecewise-linear functions are then sampled uniformly on a given range and the corresponding vectors of samples are concatenated and returned. See http://jmlr.org/papers/v16/bubenik15a.html for more details. - """ - def __init__(self, num_landscapes=5, resolution=100, sample_range=[np.nan, np.nan]): - """ - Constructor for the Landscape class. - - Parameters: - num_landscapes (int): number of piecewise-linear functions to output (default 5). - resolution (int): number of sample for all piecewise-linear functions (default 100). - sample_range ([double, double]): minimum and maximum of all piecewise-linear function domains, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. - """ - self.num_landscapes, self.resolution, self.sample_range = num_landscapes, resolution, sample_range - - def fit(self, X, y=None): - """ - Fit the Landscape class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - if np.isnan(np.array(self.sample_range)).any(): - pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) - [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] - self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) - return self - - def transform(self, X): - """ - Compute the persistence landscape for each persistence diagram individually and concatenate the results. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array with shape (number of diagrams) x (number of samples = **num_landscapes** x **resolution**): output persistence landscapes. - """ - num_diag, Xfit = len(X), [] - x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) - step_x = x_values[1] - x_values[0] - - for i in range(num_diag): - - diagram, num_pts_in_diag = X[i], X[i].shape[0] - - ls = np.zeros([self.num_landscapes, self.resolution]) - - events = [] - for j in range(self.resolution): - events.append([]) - - for j in range(num_pts_in_diag): - [px,py] = diagram[j,:2] - min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - - if min_idx < self.resolution and max_idx > 0: - - landscape_value = self.sample_range[0] + min_idx * step_x - px - for k in range(min_idx, mid_idx): - events[k].append(landscape_value) - landscape_value += step_x - - landscape_value = py - self.sample_range[0] - mid_idx * step_x - for k in range(mid_idx, max_idx): - events[k].append(landscape_value) - landscape_value -= step_x - - for j in range(self.resolution): - events[j].sort(reverse=True) - for k in range( min(self.num_landscapes, len(events[j])) ): - ls[k,j] = events[j][k] - - Xfit.append(np.sqrt(2)*np.reshape(ls,[1,-1])) - - Xfit = np.concatenate(Xfit,0) - - return Xfit - -class Silhouette(BaseEstimator, TransformerMixin): - """ - This is a class for computing persistence silhouettes from a list of persistence diagrams. A persistence silhouette is computed by taking a weighted average of the collection of 1D piecewise-linear functions given by the persistence landscapes, and then by uniformly sampling this average on a given range. Finally, the corresponding vector of samples is returned. See https://arxiv.org/abs/1312.0308 for more details. - """ - def __init__(self, weight=lambda x: 1, resolution=100, sample_range=[np.nan, np.nan]): - """ - Constructor for the Silhouette class. - - Parameters: - weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie on lists or numpy arrays of the form [p_x,p_y]. - resolution (int): number of samples for the weighted average (default 100). - sample_range ([double, double]): minimum and maximum for the weighted average domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. - """ - self.weight, self.resolution, self.sample_range = weight, resolution, sample_range - - def fit(self, X, y=None): - """ - Fit the Silhouette class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - if np.isnan(np.array(self.sample_range)).any(): - pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) - [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] - self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) - return self - - def transform(self, X): - """ - Compute the persistence silhouette for each persistence diagram individually and concatenate the results. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array with shape (number of diagrams) x (**resolution**): output persistence silhouettes. - """ - num_diag, Xfit = len(X), [] - x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) - step_x = x_values[1] - x_values[0] - - for i in range(num_diag): - - diagram, num_pts_in_diag = X[i], X[i].shape[0] - - sh, weights = np.zeros(self.resolution), np.zeros(num_pts_in_diag) - for j in range(num_pts_in_diag): - weights[j] = self.weight(diagram[j,:]) - total_weight = np.sum(weights) - - for j in range(num_pts_in_diag): - - [px,py] = diagram[j,:2] - weight = weights[j] / total_weight - min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - - if min_idx < self.resolution and max_idx > 0: - - silhouette_value = self.sample_range[0] + min_idx * step_x - px - for k in range(min_idx, mid_idx): - sh[k] += weight * silhouette_value - silhouette_value += step_x - - silhouette_value = py - self.sample_range[0] - mid_idx * step_x - for k in range(mid_idx, max_idx): - sh[k] += weight * silhouette_value - silhouette_value -= step_x - - Xfit.append(np.reshape(np.sqrt(2) * sh, [1,-1])) - - Xfit = np.concatenate(Xfit, 0) - - return Xfit - -class BettiCurve(BaseEstimator, TransformerMixin): - """ - This is a class for computing Betti curves from a list of persistence diagrams. A Betti curve is a 1D piecewise-constant function obtained from the rank function. It is sampled uniformly on a given range and the vector of samples is returned. See https://www.researchgate.net/publication/316604237_Time_Series_Classification_via_Topological_Data_Analysis for more details. - """ - def __init__(self, resolution=100, sample_range=[np.nan, np.nan]): - """ - Constructor for the BettiCurve class. - - Parameters: - resolution (int): number of sample for the piecewise-constant function (default 100). - sample_range ([double, double]): minimum and maximum of the piecewise-constant function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. - """ - self.resolution, self.sample_range = resolution, sample_range - - def fit(self, X, y=None): - """ - Fit the BettiCurve class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - if np.isnan(np.array(self.sample_range)).any(): - pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) - [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] - self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) - return self - - def transform(self, X): - """ - Compute the Betti curve for each persistence diagram individually and concatenate the results. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array with shape (number of diagrams) x (**resolution**): output Betti curves. - """ - num_diag, Xfit = len(X), [] - x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) - step_x = x_values[1] - x_values[0] - - for i in range(num_diag): - - diagram, num_pts_in_diag = X[i], X[i].shape[0] - - bc = np.zeros(self.resolution) - for j in range(num_pts_in_diag): - [px,py] = diagram[j,:2] - min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - for k in range(min_idx, max_idx): - bc[k] += 1 - - Xfit.append(np.reshape(bc,[1,-1])) - - Xfit = np.concatenate(Xfit, 0) - - return Xfit - -class Entropy(BaseEstimator, TransformerMixin): - """ - This is a class for computing persistence entropy. Persistence entropy is a statistic for persistence diagrams inspired from Shannon entropy. This statistic can also be used to compute a feature vector, called the entropy summary function. See https://arxiv.org/pdf/1803.08304.pdf for more details. Note that a previous implementation was contributed by Manuel Soriano-Trigueros. - """ - def __init__(self, mode="scalar", normalized=True, resolution=100, sample_range=[np.nan, np.nan]): - """ - Constructor for the Entropy class. - - Parameters: - mode (string): what entropy to compute: either "scalar" for computing the entropy statistics, or "vector" for computing the entropy summary functions (default "scalar"). - normalized (bool): whether to normalize the entropy summary function (default True). Used only if **mode** = "vector". - resolution (int): number of sample for the entropy summary function (default 100). Used only if **mode** = "vector". - sample_range ([double, double]): minimum and maximum of the entropy summary function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. Used only if **mode** = "vector". - """ - self.mode, self.normalized, self.resolution, self.sample_range = mode, normalized, resolution, sample_range - - def fit(self, X, y=None): - """ - Fit the Entropy class on a list of persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - if np.isnan(np.array(self.sample_range)).any(): - pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) - [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] - self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) - return self - - def transform(self, X): - """ - Compute the entropy for each persistence diagram individually and concatenate the results. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array with shape (number of diagrams) x (1 if **mode** = "scalar" else **resolution**): output entropy. - """ - num_diag, Xfit = len(X), [] - x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) - step_x = x_values[1] - x_values[0] - new_X = BirthPersistenceTransform().fit_transform(X) - - for i in range(num_diag): - - orig_diagram, diagram, num_pts_in_diag = X[i], new_X[i], X[i].shape[0] - new_diagram = DiagramScaler(use=True, scalers=[([1], MaxAbsScaler())]).fit_transform([diagram])[0] - - if self.mode == "scalar": - ent = - np.sum( np.multiply(new_diagram[:,1], np.log(new_diagram[:,1])) ) - Xfit.append(np.array([[ent]])) - - else: - ent = np.zeros(self.resolution) - for j in range(num_pts_in_diag): - [px,py] = orig_diagram[j,:2] - min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - for k in range(min_idx, max_idx): - ent[k] += (-1) * new_diagram[j,1] * np.log(new_diagram[j,1]) - if self.normalized: - ent = ent / np.linalg.norm(ent, ord=1) - Xfit.append(np.reshape(ent,[1,-1])) - - Xfit = np.concatenate(Xfit, 0) - - return Xfit - -class TopologicalVector(BaseEstimator, TransformerMixin): - """ - This is a class for computing topological vectors from a list of persistence diagrams. The topological vector associated to a persistence diagram is the sorted vector of a slight modification of the pairwise distances between the persistence diagram points. See https://diglib.eg.org/handle/10.1111/cgf12692 for more details. - """ - def __init__(self, threshold=10): - """ - Constructor for the TopologicalVector class. - - Parameters: - threshold (int): number of distances to keep (default 10). This is the dimension of the topological vector. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding topological vector as threshold. - """ - self.threshold = threshold - - def fit(self, X, y=None): - """ - Fit the TopologicalVector class on a list of persistence diagrams (this function actually does nothing but is useful when TopologicalVector is included in a scikit-learn Pipeline). - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - return self - - def transform(self, X): - """ - Compute the topological vector for each persistence diagram individually and concatenate the results. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array with shape (number of diagrams) x (**threshold**): output topological vectors. - """ - if self.threshold == -1: - thresh = np.array([X[i].shape[0] for i in range(len(X))]).max() - else: - thresh = self.threshold - - num_diag = len(X) - Xfit = np.zeros([num_diag, thresh]) - - for i in range(num_diag): - - diagram, num_pts_in_diag = X[i], X[i].shape[0] - pers = 0.5 * (diagram[:,1]-diagram[:,0]) - min_pers = np.minimum(pers,np.transpose(pers)) - distances = DistanceMetric.get_metric("chebyshev").pairwise(diagram) - vect = np.flip(np.sort(np.triu(np.minimum(distances, min_pers)), axis=None), 0) - dim = min(len(vect), thresh) - Xfit[i, :dim] = vect[:dim] - - return Xfit - -class ComplexPolynomial(BaseEstimator, TransformerMixin): - """ - This is a class for computing complex polynomials from a list of persistence diagrams. The persistence diagram points are seen as the roots of some complex polynomial, whose coefficients are returned in a complex vector. See https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27 for more details. - """ - def __init__(self, polynomial_type="R", threshold=10): - """ - Constructor for the ComplexPolynomial class. - - Parameters: - polynomial_type (char): either "R", "S" or "T" (default "R"). Type of complex polynomial that is going to be computed (explained in https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27). - threshold (int): number of coefficients (default 10). This is the dimension of the complex vector of coefficients, i.e. the number of coefficients corresponding to the largest degree terms of the polynomial. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding complex vector of coefficients as threshold. - """ - self.threshold, self.polynomial_type = threshold, polynomial_type - - def fit(self, X, y=None): - """ - Fit the ComplexPolynomial class on a list of persistence diagrams (this function actually does nothing but is useful when ComplexPolynomial is included in a scikit-learn Pipeline). - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - return self - - def transform(self, X): - """ - Compute the complex vector of coefficients for each persistence diagram individually and concatenate the results. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array with shape (number of diagrams) x (**threshold**): output complex vectors of coefficients. - """ - if self.threshold == -1: - thresh = np.array([X[i].shape[0] for i in range(len(X))]).max() - else: - thresh = self.threshold - - Xfit = np.zeros([len(X), thresh]) + 1j * np.zeros([len(X), thresh]) - for d in range(len(X)): - D, N = X[d], X[d].shape[0] - if self.polynomial_type == "R": - roots = D[:,0] + 1j * D[:,1] - elif self.polynomial_type == "S": - alpha = np.linalg.norm(D, axis=1) - alpha = np.where(alpha==0, np.ones(N), alpha) - roots = np.multiply( np.multiply( (D[:,0]+1j*D[:,1]), (D[:,1]-D[:,0]) ), 1./(np.sqrt(2)*alpha) ) - elif self.polynomial_type == "T": - alpha = np.linalg.norm(D, axis=1) - roots = np.multiply( (D[:,1]-D[:,0])/2, np.cos(alpha) - np.sin(alpha) + 1j * (np.cos(alpha) + np.sin(alpha)) ) - coeff = [0] * (N+1) - coeff[N] = 1 - for i in range(1, N+1): - for j in range(N-i-1, N): - coeff[j] += ((-1) * roots[i-1] * coeff[j+1]) - coeff = np.array(coeff[::-1])[1:] - Xfit[d, :min(thresh, coeff.shape[0])] = coeff[:min(thresh, coeff.shape[0])] - return Xfit -- cgit v1.2.3 From 57b86b2665cd0e35d18b697577b00c604212e369 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Fri, 15 Nov 2019 23:09:29 +0100 Subject: Token documentation --- src/python/doc/representations.rst | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/python/doc/representations.rst b/src/python/doc/representations.rst index 3db1c95a..a137a035 100644 --- a/src/python/doc/representations.rst +++ b/src/python/doc/representations.rst @@ -2,9 +2,22 @@ .. To get rid of WARNING: document isn't included in any toctree -=================================== -Representations reference manual -=================================== +====================== +Representations manual +====================== + +.. include:: representations_sum.inc + +This module, originally named sklearn_tda, aims at bridging the gap between persistence diagrams and machine learning tools, in particular scikit-learn. It provides tools, using the scikit-learn standard interface, to compute distances and kernels on diagrams, and to convert diagrams into vectors. + +A diagram is represented as a numpy array of shape (n,2), as can be obtained from `SimplexTree.persistence_intervals_in_dimension` for instance. Points at infinity are represented as a numpy array of shape (n,1), storing only the birth time. + +A small example is provided + +.. only:: builder_html + + * :download:`diagram_vectorizations_distances_kernels.py <../example/diagram_vectorizations_distances_kernels.py>` + Preprocessing ------------- -- cgit v1.2.3 From 4eb1d8dd6a2da695d989c43679161f57dd940a70 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Fri, 15 Nov 2019 23:25:24 +0100 Subject: Example uses matplotlib --- src/python/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 2c568b66..9af85eac 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -388,9 +388,9 @@ if(PYTHONINTERP_FOUND) endif(OT_FOUND) # Representations - if(SKLEARN_FOUND) + if(SKLEARN_FOUND AND MATPLOTLIB_FOUND) add_gudhi_py_test(test_representations) - endif(SKLEARN_FOUND) + endif() # Documentation generation is available through sphinx - requires all modules if(SPHINX_PATH) -- cgit v1.2.3 From 6d2e5d1cbdecd86ad4d56fc8902542caca2cf2bd Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 15 Nov 2019 23:33:49 +0100 Subject: Code review: Point_3 is an Alpha_shape_3::Point instead of std::conditionnal(...) --- src/Alpha_complex/include/gudhi/Alpha_complex_3d.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h b/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h index 70e864e6..7f96c94c 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h @@ -252,7 +252,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Bare_ /** \brief `Alpha_complex_3d::Point_3` type is either a `Alpha_complex_3d::Bare_point_3` (Weighted = false) or a * `Alpha_complex_3d::Weighted_point_3` (Weighted = true). */ - using Point_3 = typename std::conditional::type; + using Point_3 = typename Alpha_shape_3::Point; private: using Dispatch = -- cgit v1.2.3 From c3414741af6ef98703ba9414803aff5a2d08777d Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sat, 16 Nov 2019 06:34:54 +0100 Subject: More 'is' to '==' --- src/python/gudhi/alpha_complex.pyx | 2 +- src/python/gudhi/cubical_complex.pyx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/alpha_complex.pyx b/src/python/gudhi/alpha_complex.pyx index 6d6309db..a85c47c8 100644 --- a/src/python/gudhi/alpha_complex.pyx +++ b/src/python/gudhi/alpha_complex.pyx @@ -68,7 +68,7 @@ cdef class AlphaComplex: # The real cython constructor def __cinit__(self, points=None, off_file=''): - if off_file is not '': + if off_file: if os.path.isfile(off_file): self.thisptr = new Alpha_complex_interface(str.encode(off_file), True) else: diff --git a/src/python/gudhi/cubical_complex.pyx b/src/python/gudhi/cubical_complex.pyx index 0dc133d1..579c942b 100644 --- a/src/python/gudhi/cubical_complex.pyx +++ b/src/python/gudhi/cubical_complex.pyx @@ -66,9 +66,9 @@ cdef class CubicalComplex: # The real cython constructor def __cinit__(self, dimensions=None, top_dimensional_cells=None, perseus_file=''): - if (dimensions is not None) and (top_dimensional_cells is not None) and (perseus_file is ''): + if (dimensions is not None) and (top_dimensional_cells is not None) and (perseus_file == ''): self.thisptr = new Bitmap_cubical_complex_base_interface(dimensions, top_dimensional_cells) - elif (dimensions is None) and (top_dimensional_cells is None) and (perseus_file is not ''): + elif (dimensions is None) and (top_dimensional_cells is None) and (perseus_file != ''): if os.path.isfile(perseus_file): self.thisptr = new Bitmap_cubical_complex_base_interface(str.encode(perseus_file)) else: -- cgit v1.2.3 From 00985c119ae164477e8493a69f32f103774cf51c Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sat, 16 Nov 2019 14:05:19 +0100 Subject: Examples for plotting a simplicial complex with plotly / matplotlib --- src/python/example/plot_alpha_complex.py | 34 ++++++++++++++++++++++++++++++++ src/python/example/plot_rips_complex.py | 34 ++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100755 src/python/example/plot_alpha_complex.py create mode 100755 src/python/example/plot_rips_complex.py (limited to 'src') diff --git a/src/python/example/plot_alpha_complex.py b/src/python/example/plot_alpha_complex.py new file mode 100755 index 00000000..98931975 --- /dev/null +++ b/src/python/example/plot_alpha_complex.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +import numpy as np +import gudhi +ac = gudhi.AlphaComplex(off_file='../../data/points/tore3D_1307.off') +st = ac.create_simplex_tree() +points = np.array([ac.get_point(i) for i in range(st.num_vertices())]) +# We want to plot the alpha-complex with alpha=0.1. +# We are only going to plot the triangles +triangles = np.array([s[0] for s in st.get_skeleton(2) if len(s[0])==3 and s[1] <= .1]) + +# First possibility: plotly +import plotly.graph_objects as go +fig = go.Figure(data=[ + go.Mesh3d( + x=points[:,0], + y=points[:,1], + z=points[:,2], + i = triangles[:,0], + j = triangles[:,1], + k = triangles[:,2], + ) +]) +fig.show() + +# Second possibility: matplotlib +from mpl_toolkits.mplot3d import Axes3D +import matplotlib.pyplot as plt +fig = plt.figure() +ax = fig.gca(projection='3d') +ax.plot_trisurf(points[:,0], points[:,1], points[:,2], triangles=triangles) +plt.show() + +# Third possibility: mayavi.mlab.triangular_mesh diff --git a/src/python/example/plot_rips_complex.py b/src/python/example/plot_rips_complex.py new file mode 100755 index 00000000..d2637ea8 --- /dev/null +++ b/src/python/example/plot_rips_complex.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +import numpy as np +import gudhi +points = np.array(gudhi.read_off('../../data/points/Kl.off')) +rc = gudhi.RipsComplex(points=points, max_edge_length=.2) +st = rc.create_simplex_tree(max_dimension=2) +# We are only going to plot the triangles +triangles = np.array([s[0] for s in st.get_skeleton(2) if len(s[0])==3]) + +# First possibility: plotly +import plotly.graph_objects as go +fig = go.Figure(data=[ + go.Mesh3d( + # Use the first 3 coordinates, but we could as easily pick others + x=points[:,0], + y=points[:,1], + z=points[:,2], + i = triangles[:,0], + j = triangles[:,1], + k = triangles[:,2], + ) +]) +fig.show() + +# Second possibility: matplotlib +from mpl_toolkits.mplot3d import Axes3D +import matplotlib.pyplot as plt +fig = plt.figure() +ax = fig.gca(projection='3d') +ax.plot_trisurf(points[:,0], points[:,1], points[:,2], triangles=triangles) +plt.show() + +# Third possibility: mayavi.mlab.triangular_mesh -- cgit v1.2.3 From 71ffd4fcbf273289a556ee5ed39a6b8edf8444a1 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sat, 16 Nov 2019 14:27:52 +0100 Subject: Add mayavi to the examples --- src/python/example/plot_alpha_complex.py | 5 ++++- src/python/example/plot_rips_complex.py | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/python/example/plot_alpha_complex.py b/src/python/example/plot_alpha_complex.py index 98931975..99c18a7c 100755 --- a/src/python/example/plot_alpha_complex.py +++ b/src/python/example/plot_alpha_complex.py @@ -31,4 +31,7 @@ ax = fig.gca(projection='3d') ax.plot_trisurf(points[:,0], points[:,1], points[:,2], triangles=triangles) plt.show() -# Third possibility: mayavi.mlab.triangular_mesh +# Third possibility: mayavi +from mayavi import mlab +mlab.triangular_mesh(points[:,0], points[:,1], points[:,2], triangles); +mlab.show() diff --git a/src/python/example/plot_rips_complex.py b/src/python/example/plot_rips_complex.py index d2637ea8..1c878db1 100755 --- a/src/python/example/plot_rips_complex.py +++ b/src/python/example/plot_rips_complex.py @@ -31,4 +31,8 @@ ax = fig.gca(projection='3d') ax.plot_trisurf(points[:,0], points[:,1], points[:,2], triangles=triangles) plt.show() -# Third possibility: mayavi.mlab.triangular_mesh +# Third possibility: mayavi +# (this may take a while) +from mayavi import mlab +mlab.triangular_mesh(points[:,0], points[:,1], points[:,2], triangles); +mlab.show() -- cgit v1.2.3 From 7880c08d7ff7c42d7b089f9d1252c85289bf7596 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sat, 16 Nov 2019 15:11:06 +0100 Subject: Example plot of triangles, edges and vertices with mayavi --- src/python/example/plot_simplex_tree_dim012.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100755 src/python/example/plot_simplex_tree_dim012.py (limited to 'src') diff --git a/src/python/example/plot_simplex_tree_dim012.py b/src/python/example/plot_simplex_tree_dim012.py new file mode 100755 index 00000000..e6a1a4e0 --- /dev/null +++ b/src/python/example/plot_simplex_tree_dim012.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +import numpy as np +import gudhi + +# Coordinates of the points +points=np.array([[0,0,0],[1,0,0],[0,1,0],[0,0,1],[1,1,1],[1,1,0],[0,1,1]]) +# Build the simplicial complex with a tetrahedon, an edge and an isolated vertex +cplx=gudhi.SimplexTree() +cplx.insert([1,2,3,5]) +cplx.insert([4,6]) +cplx.insert([0]) + +from mayavi import mlab +# Plot triangles +triangles = np.array([s[0] for s in cplx.get_skeleton(2) if len(s[0])==3]) +mlab.triangular_mesh(points[:,0], points[:,1], points[:,2], triangles); +# Plot edges +for s in cplx.get_skeleton(1): + e = s[0] + if len(e) == 2: + pts = points[[e[0],e[1]]] + mlab.plot3d(pts[:,0],pts[:,1],pts[:,2],tube_radius=None) +# Plot points +mlab.points3d(points[:,0], points[:,1], points[:,2]) +mlab.show() -- cgit v1.2.3 From eae8673750c74d1f1023862703e350b26cf35912 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sat, 16 Nov 2019 15:33:38 +0100 Subject: Add examples to the list of examples --- src/python/doc/examples.rst | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/python/doc/examples.rst b/src/python/doc/examples.rst index edbc2f72..a42227e3 100644 --- a/src/python/doc/examples.rst +++ b/src/python/doc/examples.rst @@ -16,6 +16,9 @@ Examples * :download:`periodic_cubical_complex_barcode_persistence_from_perseus_file_example.py <../example/periodic_cubical_complex_barcode_persistence_from_perseus_file_example.py>` * :download:`bottleneck_basic_example.py <../example/bottleneck_basic_example.py>` * :download:`gudhi_graphical_tools_example.py <../example/gudhi_graphical_tools_example.py>` + * :download:`plot_simplex_tree_dim012.py <../example/plot_simplex_tree_dim012.py>` + * :download:`plot_rips_complex.py <../example/plot_rips_complex.py>` + * :download:`plot_alpha_complex.py <../example/plot_alpha_complex.py>` * :download:`witness_complex_from_nearest_landmark_table.py <../example/witness_complex_from_nearest_landmark_table.py>` * :download:`euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py <../example/euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py>` * :download:`euclidean_witness_complex_diagram_persistence_from_off_file_example.py <../example/euclidean_witness_complex_diagram_persistence_from_off_file_example.py>` -- cgit v1.2.3 From 4ed9330dc0da5963124cac1b7e7caebc0eed73cd Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sat, 16 Nov 2019 17:50:08 +0100 Subject: Draw vertex/edge/triangle with matplotlib --- src/python/example/plot_simplex_tree_dim012.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/python/example/plot_simplex_tree_dim012.py b/src/python/example/plot_simplex_tree_dim012.py index e6a1a4e0..f79c359b 100755 --- a/src/python/example/plot_simplex_tree_dim012.py +++ b/src/python/example/plot_simplex_tree_dim012.py @@ -9,17 +9,37 @@ cplx=gudhi.SimplexTree() cplx.insert([1,2,3,5]) cplx.insert([4,6]) cplx.insert([0]) +triangles = np.array([s[0] for s in cplx.get_skeleton(2) if len(s[0])==3]) +## With matplotlib +from mpl_toolkits.mplot3d import Axes3D +from mpl_toolkits.mplot3d.art3d import Line3DCollection +import matplotlib.pyplot as plt +fig = plt.figure() +ax = fig.gca(projection='3d') +# Plot triangles +ax.plot_trisurf(points[:,0], points[:,1], points[:,2], triangles=triangles) +# Plot points +ax.scatter3D(points[:,0], points[:,1], points[:,2]) +# Plot edges +edges=[] +for s in cplx.get_skeleton(1): + e = s[0] + if len(e) == 2: + edges.append(points[[e[0],e[1]]]) +ax.add_collection3d(Line3DCollection(segments=edges)) +plt.show() + +## With mayavi from mayavi import mlab # Plot triangles -triangles = np.array([s[0] for s in cplx.get_skeleton(2) if len(s[0])==3]) mlab.triangular_mesh(points[:,0], points[:,1], points[:,2], triangles); +# Plot points +mlab.points3d(points[:,0], points[:,1], points[:,2]) # Plot edges for s in cplx.get_skeleton(1): e = s[0] if len(e) == 2: pts = points[[e[0],e[1]]] mlab.plot3d(pts[:,0],pts[:,1],pts[:,2],tube_radius=None) -# Plot points -mlab.points3d(points[:,0], points[:,1], points[:,2]) mlab.show() -- cgit v1.2.3 From fc4f80aa2ce60fd563bb54a0d082c83fe99fd99d Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sat, 16 Nov 2019 18:35:44 +0100 Subject: Draw vertex/edge/triangle with plotly --- src/python/example/plot_simplex_tree_dim012.py | 41 +++++++++++++++++++------- 1 file changed, 31 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/python/example/plot_simplex_tree_dim012.py b/src/python/example/plot_simplex_tree_dim012.py index f79c359b..5b962131 100755 --- a/src/python/example/plot_simplex_tree_dim012.py +++ b/src/python/example/plot_simplex_tree_dim012.py @@ -9,7 +9,36 @@ cplx=gudhi.SimplexTree() cplx.insert([1,2,3,5]) cplx.insert([4,6]) cplx.insert([0]) +# List of triangles (point indices) triangles = np.array([s[0] for s in cplx.get_skeleton(2) if len(s[0])==3]) +# List of edges (point coordinates) +edges = [] +for s in cplx.get_skeleton(1): + e = s[0] + if len(e) == 2: + edges.append(points[[e[0],e[1]]]) + +## With plotly +import plotly.graph_objects as go +# Plot triangles +f2 = go.Mesh3d( + x=points[:,0], + y=points[:,1], + z=points[:,2], + i = triangles[:,0], + j = triangles[:,1], + k = triangles[:,2], + ) +# Plot points +f0 = go.Scatter3d(x=points[:,0], y=points[:,1], z=points[:,2], mode="markers") +data = [f2, f0] +# Plot edges +for pts in edges: + seg = go.Scatter3d(x=pts[:,0],y=pts[:,1],z=pts[:,2],mode="lines",line=dict(color='green')) + data.append(seg) +fig = go.Figure(data=data,layout=dict(showlegend=False)) +# By default plotly would give each edge its own color and legend, that's too much +fig.show() ## With matplotlib from mpl_toolkits.mplot3d import Axes3D @@ -22,11 +51,6 @@ ax.plot_trisurf(points[:,0], points[:,1], points[:,2], triangles=triangles) # Plot points ax.scatter3D(points[:,0], points[:,1], points[:,2]) # Plot edges -edges=[] -for s in cplx.get_skeleton(1): - e = s[0] - if len(e) == 2: - edges.append(points[[e[0],e[1]]]) ax.add_collection3d(Line3DCollection(segments=edges)) plt.show() @@ -37,9 +61,6 @@ mlab.triangular_mesh(points[:,0], points[:,1], points[:,2], triangles); # Plot points mlab.points3d(points[:,0], points[:,1], points[:,2]) # Plot edges -for s in cplx.get_skeleton(1): - e = s[0] - if len(e) == 2: - pts = points[[e[0],e[1]]] - mlab.plot3d(pts[:,0],pts[:,1],pts[:,2],tube_radius=None) +for pts in edges: + mlab.plot3d(pts[:,0],pts[:,1],pts[:,2],tube_radius=None) mlab.show() -- cgit v1.2.3 From 669039c52bb3e71ad24f95e8a7cf1b2be69a5548 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Sat, 16 Nov 2019 23:46:34 +0100 Subject: Doc review: Add the starting point of the documentation --- src/Alpha_complex/doc/Intro_alpha_complex.h | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src') diff --git a/src/Alpha_complex/doc/Intro_alpha_complex.h b/src/Alpha_complex/doc/Intro_alpha_complex.h index fd6f4081..adc1378f 100644 --- a/src/Alpha_complex/doc/Intro_alpha_complex.h +++ b/src/Alpha_complex/doc/Intro_alpha_complex.h @@ -51,6 +51,16 @@ namespace alpha_complex { * - For people only interested in the topology of the \ref alpha_complex (for instance persistence), * \ref alpha_complex is equivalent to the \ref cech_complex and much smaller if you do not bound the radii. * \ref cech_complex can still make sense in higher dimension precisely because you can bound the radii. + * - Using the default `CGAL::Epeck_d` makes the construction safe. If you pass exact=true to create_complex, the + * filtration values are the exact ones converted to the filtration value type of the simplicial complex. This can be + * very slow. If you pass exact=false (the default), the filtration values are only guaranteed to have a small + * multiplicative error compared to the exact value, see + * CGAL::Lazy_exact_nt::set_relative_precision_of_to_double for details. A drawback, when computing + * persistence, is that an empty exact interval [10^12,10^12] may become a non-empty approximate interval + * [10^12,10^12+10^6]. Using `CGAL::Epick_d` makes the computations slightly faster, and the combinatorics are still + * exact, but the computation of filtration values can exceptionally be arbitrarily bad. In all cases, we still + * guarantee that the output is a valid filtration (faces have a filtration value no larger than their cofaces). * - For performances reasons, it is advised to use `Alpha_complex` with \ref cgal ≥ 5.0.0. * * \section pointsexample Example from points -- cgit v1.2.3 From 63616eba3031334d8e2c267e54f9aa4a801baeff Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Thu, 14 Nov 2019 13:13:38 +0100 Subject: Make the license a comment instead of a string I think this interacts better with sphinx automodule --- src/python/gudhi/__init__.py.in | 17 ++++++++--------- src/python/gudhi/alpha_complex.pyx | 17 ++++++++--------- src/python/gudhi/bottleneck.pyx | 17 ++++++++--------- src/python/gudhi/cubical_complex.pyx | 17 ++++++++--------- src/python/gudhi/euclidean_strong_witness_complex.pyx | 17 ++++++++--------- src/python/gudhi/euclidean_witness_complex.pyx | 17 ++++++++--------- src/python/gudhi/nerve_gic.pyx | 17 ++++++++--------- src/python/gudhi/off_reader.pyx | 17 ++++++++--------- src/python/gudhi/periodic_cubical_complex.pyx | 17 ++++++++--------- src/python/gudhi/persistence_graphical_tools.py | 17 ++++++++--------- src/python/gudhi/reader_utils.pyx | 17 ++++++++--------- src/python/gudhi/rips_complex.pyx | 17 ++++++++--------- src/python/gudhi/simplex_tree.pyx | 17 ++++++++--------- src/python/gudhi/strong_witness_complex.pyx | 17 ++++++++--------- src/python/gudhi/subsampling.pyx | 17 ++++++++--------- src/python/gudhi/tangential_complex.pyx | 17 ++++++++--------- src/python/gudhi/wasserstein.py | 17 ++++++++--------- src/python/gudhi/witness_complex.pyx | 17 ++++++++--------- 18 files changed, 144 insertions(+), 162 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/__init__.py.in b/src/python/gudhi/__init__.py.in index 02888fff..0c462b02 100644 --- a/src/python/gudhi/__init__.py.in +++ b/src/python/gudhi/__init__.py.in @@ -1,14 +1,13 @@ from importlib import import_module -"""This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "GUDHI Editorial Board" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/alpha_complex.pyx b/src/python/gudhi/alpha_complex.pyx index a85c47c8..8f2c98d5 100644 --- a/src/python/gudhi/alpha_complex.pyx +++ b/src/python/gudhi/alpha_complex.pyx @@ -9,15 +9,14 @@ import os from gudhi.simplex_tree cimport * from gudhi.simplex_tree import SimplexTree -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/bottleneck.pyx b/src/python/gudhi/bottleneck.pyx index 4b378cbc..c2361024 100644 --- a/src/python/gudhi/bottleneck.pyx +++ b/src/python/gudhi/bottleneck.pyx @@ -3,15 +3,14 @@ from libcpp.vector cimport vector from libcpp.utility cimport pair import os -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/cubical_complex.pyx b/src/python/gudhi/cubical_complex.pyx index 579c942b..011c407c 100644 --- a/src/python/gudhi/cubical_complex.pyx +++ b/src/python/gudhi/cubical_complex.pyx @@ -7,15 +7,14 @@ import os from numpy import array as np_array -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/euclidean_strong_witness_complex.pyx b/src/python/gudhi/euclidean_strong_witness_complex.pyx index 0e5b544a..e3f451f0 100644 --- a/src/python/gudhi/euclidean_strong_witness_complex.pyx +++ b/src/python/gudhi/euclidean_strong_witness_complex.pyx @@ -6,15 +6,14 @@ from libc.stdint cimport intptr_t from gudhi.simplex_tree cimport * from gudhi.simplex_tree import SimplexTree -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/euclidean_witness_complex.pyx b/src/python/gudhi/euclidean_witness_complex.pyx index 86d2d996..84a8ea1a 100644 --- a/src/python/gudhi/euclidean_witness_complex.pyx +++ b/src/python/gudhi/euclidean_witness_complex.pyx @@ -6,15 +6,14 @@ from libc.stdint cimport intptr_t from gudhi.simplex_tree cimport * from gudhi.simplex_tree import SimplexTree -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/nerve_gic.pyx b/src/python/gudhi/nerve_gic.pyx index 2b230b8c..acb78564 100644 --- a/src/python/gudhi/nerve_gic.pyx +++ b/src/python/gudhi/nerve_gic.pyx @@ -9,15 +9,14 @@ from libc.stdint cimport intptr_t from gudhi.simplex_tree cimport * from gudhi.simplex_tree import SimplexTree -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2018 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2018 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2018 Inria" diff --git a/src/python/gudhi/off_reader.pyx b/src/python/gudhi/off_reader.pyx index 27a89870..225e981c 100644 --- a/src/python/gudhi/off_reader.pyx +++ b/src/python/gudhi/off_reader.pyx @@ -3,15 +3,14 @@ from libcpp.vector cimport vector from libcpp.string cimport string import os -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/periodic_cubical_complex.pyx b/src/python/gudhi/periodic_cubical_complex.pyx index 88223afb..c89055db 100644 --- a/src/python/gudhi/periodic_cubical_complex.pyx +++ b/src/python/gudhi/periodic_cubical_complex.pyx @@ -7,15 +7,14 @@ import os from numpy import array as np_array -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/persistence_graphical_tools.py b/src/python/gudhi/persistence_graphical_tools.py index 181bc8ea..43a00459 100644 --- a/src/python/gudhi/persistence_graphical_tools.py +++ b/src/python/gudhi/persistence_graphical_tools.py @@ -5,15 +5,14 @@ import numpy as np from gudhi.reader_utils import read_persistence_intervals_in_dimension from gudhi.reader_utils import read_persistence_intervals_grouped_by_dimension -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau, Bertrand Michel - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau, Bertrand Michel +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau, Bertrand Michel" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/reader_utils.pyx b/src/python/gudhi/reader_utils.pyx index b266c783..6994c4f9 100644 --- a/src/python/gudhi/reader_utils.pyx +++ b/src/python/gudhi/reader_utils.pyx @@ -7,15 +7,14 @@ from libcpp.pair cimport pair from os import path from numpy import array as np_array -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2017 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2017 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2017 Inria" diff --git a/src/python/gudhi/rips_complex.pyx b/src/python/gudhi/rips_complex.pyx index f2cd6a8d..cbbbab0d 100644 --- a/src/python/gudhi/rips_complex.pyx +++ b/src/python/gudhi/rips_complex.pyx @@ -8,15 +8,14 @@ from libc.stdint cimport intptr_t from gudhi.simplex_tree cimport * from gudhi.simplex_tree import SimplexTree -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/simplex_tree.pyx b/src/python/gudhi/simplex_tree.pyx index 90ddf6dd..ade3bf6c 100644 --- a/src/python/gudhi/simplex_tree.pyx +++ b/src/python/gudhi/simplex_tree.pyx @@ -2,15 +2,14 @@ from libc.stdint cimport intptr_t from numpy import array as np_array cimport simplex_tree -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/strong_witness_complex.pyx b/src/python/gudhi/strong_witness_complex.pyx index e35ba6ad..66d49b49 100644 --- a/src/python/gudhi/strong_witness_complex.pyx +++ b/src/python/gudhi/strong_witness_complex.pyx @@ -6,15 +6,14 @@ from libc.stdint cimport intptr_t from gudhi.simplex_tree cimport * from gudhi.simplex_tree import SimplexTree -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/subsampling.pyx b/src/python/gudhi/subsampling.pyx index e622ac99..e0cd1348 100644 --- a/src/python/gudhi/subsampling.pyx +++ b/src/python/gudhi/subsampling.pyx @@ -4,15 +4,14 @@ from libcpp.string cimport string from libcpp cimport bool import os -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/tangential_complex.pyx b/src/python/gudhi/tangential_complex.pyx index 9ef15567..b7678f4d 100644 --- a/src/python/gudhi/tangential_complex.pyx +++ b/src/python/gudhi/tangential_complex.pyx @@ -9,15 +9,14 @@ import os from gudhi.simplex_tree cimport * from gudhi.simplex_tree import SimplexTree -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/wasserstein.py b/src/python/gudhi/wasserstein.py index eba7c6d5..d8a3104c 100644 --- a/src/python/gudhi/wasserstein.py +++ b/src/python/gudhi/wasserstein.py @@ -5,15 +5,14 @@ try: except ImportError: print("POT (Python Optimal Transport) package is not installed. Try to run $ conda install -c conda-forge pot ; or $ pip install POT") -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Theo Lacombe - - Copyright (C) 2019 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Theo Lacombe +# +# Copyright (C) 2019 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification def _proj_on_diag(X): ''' diff --git a/src/python/gudhi/witness_complex.pyx b/src/python/gudhi/witness_complex.pyx index c19ccf2b..153fc615 100644 --- a/src/python/gudhi/witness_complex.pyx +++ b/src/python/gudhi/witness_complex.pyx @@ -6,15 +6,14 @@ from libc.stdint cimport intptr_t from gudhi.simplex_tree cimport * from gudhi.simplex_tree import SimplexTree -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" -- cgit v1.2.3 From c8be137d15f40f40c4150f0c0fb9776cd680a36c Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 19 Nov 2019 07:33:46 +0100 Subject: Doc review: Add the starting point of the documentation in the Alpha complex reference --- src/Alpha_complex/include/gudhi/Alpha_complex.h | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex.h b/src/Alpha_complex/include/gudhi/Alpha_complex.h index b3d1e6ea..0c2569c8 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex.h @@ -77,8 +77,19 @@ template struct Is_Epeck_D> { static const bool val * < * CGAL::Dynamic_dimension_tag > * - * \remark When Alpha_complex is constructed with an infinite value of alpha, the complex is a Delaunay complex. - * + * \remark + * - When Alpha_complex is constructed with an infinite value of alpha, the complex is a Delaunay complex. + * - Using the default `CGAL::Epeck_d` makes the construction safe. If you pass exact=true to create_complex, the + * filtration values are the exact ones converted to the filtration value type of the simplicial complex. This can be + * very slow. If you pass exact=false (the default), the filtration values are only guaranteed to have a small + * multiplicative error compared to the exact value, see + * CGAL::Lazy_exact_nt::set_relative_precision_of_to_double for details. A drawback, when computing + * persistence, is that an empty exact interval [10^12,10^12] may become a non-empty approximate interval + * [10^12,10^12+10^6]. Using `CGAL::Epick_d` makes the computations slightly faster, and the combinatorics are still + * exact, but the computation of filtration values can exceptionally be arbitrarily bad. In all cases, we still + * guarantee that the output is a valid filtration (faces have a filtration value no larger than their cofaces). + * - For performances reasons, it is advised to use `Alpha_complex` with \ref cgal ≥ 5.0.0. */ template> class Alpha_complex { -- cgit v1.2.3 From 4d44e27f65488964a3c8af7038d3d9b90e6d5a43 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Tue, 19 Nov 2019 17:41:39 +0100 Subject: Add warning in the doc of assign_filtration Apparently some users expect that it will magically "fix" other simplices so it remains a valid filtration. --- src/python/gudhi/simplex_tree.pyx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/simplex_tree.pyx b/src/python/gudhi/simplex_tree.pyx index ade3bf6c..4a3cd9bc 100644 --- a/src/python/gudhi/simplex_tree.pyx +++ b/src/python/gudhi/simplex_tree.pyx @@ -74,13 +74,22 @@ cdef class SimplexTree: return self.get_ptr().simplex_filtration(simplex) def assign_filtration(self, simplex, filtration): - """This function assigns the simplicial complex filtration value for a + """This function assigns a new filtration value to a given N-simplex. :param simplex: The N-simplex, represented by a list of vertex. :type simplex: list of int. - :param filtration: The simplicial complex filtration value. + :param filtration: The new filtration value. :type filtration: float + + .. note:: + Beware that after this operation, the structure may not be a valid + filtration anymore, a simplex could have a lower filtration value + than one of its faces. Callers are responsible for fixing this + (with more :meth:`assign_filtration` or + :meth:`make_filtration_non_decreasing` for instance) before calling + any function that relies on the filtration property, like + :meth:`initialize_filtration`. """ self.get_ptr().assign_simplex_filtration(simplex, filtration) -- cgit v1.2.3 From 57c1f0f4b36b934c8fc54c8691f77b9b6d0eff9d Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Wed, 20 Nov 2019 09:36:17 +0100 Subject: Code review : rollback array init --- src/common/include/gudhi/reader_utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/common/include/gudhi/reader_utils.h b/src/common/include/gudhi/reader_utils.h index ee811967..5e96b5db 100644 --- a/src/common/include/gudhi/reader_utils.h +++ b/src/common/include/gudhi/reader_utils.h @@ -305,7 +305,7 @@ void read_persistence_intervals_and_dimension(std::string const& filename, Outpu std::string line; getline(in, line); if (line.length() != 0 && line[0] != '#') { - double numbers[4] = {0.}; + double numbers[4]; int n = sscanf(line.c_str(), "%lf %lf %lf %lf", &numbers[0], &numbers[1], &numbers[2], &numbers[3]); #ifdef DEBUG_TRACES std::cout << "[" << n << "] = " << numbers[0] << "," << numbers[1] -- cgit v1.2.3 From 6bc1f27bbab03139718db674f98d748a7aeaced3 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Wed, 20 Nov 2019 14:12:01 +0100 Subject: Use matplotlib axes to be able to subplot persistence graphical tools --- .../doc/persistence_graphical_tools_user.rst | 13 ++-- ...ex_diagram_persistence_from_off_file_example.py | 7 +- ...ex_diagram_persistence_from_off_file_example.py | 7 +- ...ex_diagram_persistence_from_off_file_example.py | 7 +- .../example/gudhi_graphical_tools_example.py | 18 ++++- ...arcode_persistence_from_perseus_file_example.py | 4 +- ...istence_from_correlation_matrix_file_example.py | 7 +- ...ersistence_from_distance_matrix_file_example.py | 7 +- ...ex_diagram_persistence_from_off_file_example.py | 7 +- src/python/example/rips_persistence_diagram.py | 5 +- .../example/sparse_rips_persistence_diagram.py | 5 +- ...complex_plain_homology_from_off_file_example.py | 7 +- src/python/gudhi/persistence_graphical_tools.py | 90 +++++++++++++--------- 13 files changed, 113 insertions(+), 71 deletions(-) (limited to 'src') diff --git a/src/python/doc/persistence_graphical_tools_user.rst b/src/python/doc/persistence_graphical_tools_user.rst index b2124fdd..2de99252 100644 --- a/src/python/doc/persistence_graphical_tools_user.rst +++ b/src/python/doc/persistence_graphical_tools_user.rst @@ -20,6 +20,7 @@ This function can display the persistence result as a barcode: .. plot:: :include-source: + import matplotlib.pyplot as plot import gudhi off_file = gudhi.__root_source_dir__ + '/data/points/tore3D_300.off' @@ -29,7 +30,7 @@ This function can display the persistence result as a barcode: simplex_tree = rips_complex.create_simplex_tree(max_dimension=3) diag = simplex_tree.persistence(min_persistence=0.4) - plot = gudhi.plot_persistence_barcode(diag) + gudhi.plot_persistence_barcode(diag) plot.show() Show persistence as a diagram @@ -43,14 +44,15 @@ This function can display the persistence result as a diagram: .. plot:: :include-source: + import matplotlib.pyplot as plot import gudhi # rips_on_tore3D_1307.pers obtained from write_persistence_diagram method persistence_file=gudhi.__root_source_dir__ + \ '/data/persistence_diagram/rips_on_tore3D_1307.pers' - plt = gudhi.plot_persistence_diagram(persistence_file=persistence_file, + gudhi.plot_persistence_diagram(persistence_file=persistence_file, legend=True) - plt.show() + plot.show() Persistence density ------------------- @@ -63,11 +65,12 @@ If you want more information on a specific dimension, for instance: .. plot:: :include-source: + import matplotlib.pyplot as plot import gudhi # rips_on_tore3D_1307.pers obtained from write_persistence_diagram method persistence_file=gudhi.__root_source_dir__ + \ '/data/persistence_diagram/rips_on_tore3D_1307.pers' - plt = gudhi.plot_persistence_density(persistence_file=persistence_file, + gudhi.plot_persistence_density(persistence_file=persistence_file, max_intervals=0, dimension=1, legend=True) - plt.show() + plot.show() diff --git a/src/python/example/alpha_complex_diagram_persistence_from_off_file_example.py b/src/python/example/alpha_complex_diagram_persistence_from_off_file_example.py index b8f283b3..4079a469 100755 --- a/src/python/example/alpha_complex_diagram_persistence_from_off_file_example.py +++ b/src/python/example/alpha_complex_diagram_persistence_from_off_file_example.py @@ -1,7 +1,8 @@ #!/usr/bin/env python -import gudhi import argparse +import matplotlib.pyplot as plot +import gudhi """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. @@ -60,8 +61,8 @@ with open(args.file, "r") as f: print(simplex_tree.betti_numbers()) if args.no_diagram == False: - pplot = gudhi.plot_persistence_diagram(diag, band=args.band) - pplot.show() + gudhi.plot_persistence_diagram(diag, band=args.band) + plot.show() else: print(args.file, "is not a valid OFF file") diff --git a/src/python/example/euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py b/src/python/example/euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py index 610ba44f..0eedd140 100755 --- a/src/python/example/euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py +++ b/src/python/example/euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py @@ -1,7 +1,8 @@ #!/usr/bin/env python -import gudhi import argparse +import matplotlib.pyplot as plot +import gudhi """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. @@ -75,8 +76,8 @@ with open(args.file, "r") as f: print(simplex_tree.betti_numbers()) if args.no_diagram == False: - pplot = gudhi.plot_persistence_diagram(diag, band=args.band) - pplot.show() + gudhi.plot_persistence_diagram(diag, band=args.band) + plot.show() else: print(args.file, "is not a valid OFF file") diff --git a/src/python/example/euclidean_witness_complex_diagram_persistence_from_off_file_example.py b/src/python/example/euclidean_witness_complex_diagram_persistence_from_off_file_example.py index 7587b732..1fe55737 100755 --- a/src/python/example/euclidean_witness_complex_diagram_persistence_from_off_file_example.py +++ b/src/python/example/euclidean_witness_complex_diagram_persistence_from_off_file_example.py @@ -1,7 +1,8 @@ #!/usr/bin/env python -import gudhi import argparse +import matplotlib.pyplot as plot +import gudhi """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. @@ -74,8 +75,8 @@ with open(args.file, "r") as f: print(simplex_tree.betti_numbers()) if args.no_diagram == False: - pplot = gudhi.plot_persistence_diagram(diag, band=args.band) - pplot.show() + gudhi.plot_persistence_diagram(diag, band=args.band) + plot.show() else: print(args.file, "is not a valid OFF file") diff --git a/src/python/example/gudhi_graphical_tools_example.py b/src/python/example/gudhi_graphical_tools_example.py index 3b0ca54d..37ecbf53 100755 --- a/src/python/example/gudhi_graphical_tools_example.py +++ b/src/python/example/gudhi_graphical_tools_example.py @@ -1,5 +1,6 @@ #!/usr/bin/env python +import matplotlib.pyplot as plot import gudhi """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. @@ -29,15 +30,24 @@ persistence = [ (0, (0.0, 1.0)), ] gudhi.plot_persistence_barcode(persistence) +plot.show() print("#####################################################################") print("Show diagram persistence example") -pplot = gudhi.plot_persistence_diagram(persistence) -pplot.show() +gudhi.plot_persistence_diagram(persistence) +plot.show() print("#####################################################################") print("Show diagram persistence example with a confidence band") -pplot = gudhi.plot_persistence_diagram(persistence, band=0.2) -pplot.show() +gudhi.plot_persistence_diagram(persistence, band=0.2) +plot.show() + +print("#####################################################################") +print("Show barcode and diagram persistence side by side example") +fig, axes = plot.subplots(nrows=1, ncols=2) +gudhi.plot_persistence_barcode(persistence, axes = axes[0]) +gudhi.plot_persistence_diagram(persistence, axes = axes[1]) +fig.suptitle("barcode versus diagram") +plot.show() diff --git a/src/python/example/periodic_cubical_complex_barcode_persistence_from_perseus_file_example.py b/src/python/example/periodic_cubical_complex_barcode_persistence_from_perseus_file_example.py index 9cb855cd..c692e66f 100755 --- a/src/python/example/periodic_cubical_complex_barcode_persistence_from_perseus_file_example.py +++ b/src/python/example/periodic_cubical_complex_barcode_persistence_from_perseus_file_example.py @@ -1,7 +1,8 @@ #!/usr/bin/env python -import gudhi import argparse +import matplotlib.pyplot as plot +import gudhi """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. @@ -70,5 +71,6 @@ if is_file_perseus(args.file): print(periodic_cubical_complex.betti_numbers()) if args.no_barcode == False: gudhi.plot_persistence_barcode(diag) + plot.show() else: print(args.file, "is not a valid perseus style file") diff --git a/src/python/example/rips_complex_diagram_persistence_from_correlation_matrix_file_example.py b/src/python/example/rips_complex_diagram_persistence_from_correlation_matrix_file_example.py index 3571580b..1acb187c 100755 --- a/src/python/example/rips_complex_diagram_persistence_from_correlation_matrix_file_example.py +++ b/src/python/example/rips_complex_diagram_persistence_from_correlation_matrix_file_example.py @@ -1,8 +1,9 @@ #!/usr/bin/env python -import gudhi import sys import argparse +import matplotlib.pyplot as plot +import gudhi """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. @@ -83,5 +84,5 @@ invert_diag = [ ] if args.no_diagram == False: - pplot = gudhi.plot_persistence_diagram(invert_diag, band=args.band) - pplot.show() + gudhi.plot_persistence_diagram(invert_diag, band=args.band) + plot.show() diff --git a/src/python/example/rips_complex_diagram_persistence_from_distance_matrix_file_example.py b/src/python/example/rips_complex_diagram_persistence_from_distance_matrix_file_example.py index 0b9a9ba9..79ccca96 100755 --- a/src/python/example/rips_complex_diagram_persistence_from_distance_matrix_file_example.py +++ b/src/python/example/rips_complex_diagram_persistence_from_distance_matrix_file_example.py @@ -1,7 +1,8 @@ #!/usr/bin/env python -import gudhi import argparse +import matplotlib.pyplot as plot +import gudhi """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. @@ -59,5 +60,5 @@ print("betti_numbers()=") print(simplex_tree.betti_numbers()) if args.no_diagram == False: - pplot = gudhi.plot_persistence_diagram(diag, band=args.band) - pplot.show() + gudhi.plot_persistence_diagram(diag, band=args.band) + plot.show() diff --git a/src/python/example/rips_complex_diagram_persistence_from_off_file_example.py b/src/python/example/rips_complex_diagram_persistence_from_off_file_example.py index 2b335bba..b9074cf9 100755 --- a/src/python/example/rips_complex_diagram_persistence_from_off_file_example.py +++ b/src/python/example/rips_complex_diagram_persistence_from_off_file_example.py @@ -1,7 +1,8 @@ #!/usr/bin/env python -import gudhi import argparse +import matplotlib.pyplot as plot +import gudhi """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. @@ -64,8 +65,8 @@ with open(args.file, "r") as f: print(simplex_tree.betti_numbers()) if args.no_diagram == False: - pplot = gudhi.plot_persistence_diagram(diag, band=args.band) - pplot.show() + gudhi.plot_persistence_diagram(diag, band=args.band) + plot.show() else: print(args.file, "is not a valid OFF file") diff --git a/src/python/example/rips_persistence_diagram.py b/src/python/example/rips_persistence_diagram.py index f5897d7b..2a90b4bc 100755 --- a/src/python/example/rips_persistence_diagram.py +++ b/src/python/example/rips_persistence_diagram.py @@ -1,5 +1,6 @@ #!/usr/bin/env python +import matplotlib.pyplot as plot import gudhi """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. @@ -26,5 +27,5 @@ simplex_tree = rips.create_simplex_tree(max_dimension=1) diag = simplex_tree.persistence(homology_coeff_field=2, min_persistence=0) print("diag=", diag) -pplot = gudhi.plot_persistence_diagram(diag) -pplot.show() +gudhi.plot_persistence_diagram(diag) +plot.show() diff --git a/src/python/example/sparse_rips_persistence_diagram.py b/src/python/example/sparse_rips_persistence_diagram.py index 671d5e34..410a6a86 100755 --- a/src/python/example/sparse_rips_persistence_diagram.py +++ b/src/python/example/sparse_rips_persistence_diagram.py @@ -1,5 +1,6 @@ #!/usr/bin/env python +import matplotlib.pyplot as plot import gudhi """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. @@ -28,5 +29,5 @@ simplex_tree = rips.create_simplex_tree(max_dimension=2) diag = simplex_tree.persistence(homology_coeff_field=2, min_persistence=0) print("diag=", diag) -pplot = gudhi.plot_persistence_diagram(diag) -pplot.show() +gudhi.plot_persistence_diagram(diag) +plot.show() diff --git a/src/python/example/tangential_complex_plain_homology_from_off_file_example.py b/src/python/example/tangential_complex_plain_homology_from_off_file_example.py index 456bc9eb..f0df2189 100755 --- a/src/python/example/tangential_complex_plain_homology_from_off_file_example.py +++ b/src/python/example/tangential_complex_plain_homology_from_off_file_example.py @@ -1,7 +1,8 @@ #!/usr/bin/env python -import gudhi import argparse +import matplotlib.pyplot as plot +import gudhi """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. @@ -56,8 +57,8 @@ with open(args.file, "r") as f: print(st.betti_numbers()) if args.no_diagram == False: - pplot = gudhi.plot_persistence_diagram(diag, band=args.band) - pplot.show() + gudhi.plot_persistence_diagram(diag, band=args.band) + plot.show() else: print(args.file, "is not a valid OFF file") diff --git a/src/python/gudhi/persistence_graphical_tools.py b/src/python/gudhi/persistence_graphical_tools.py index 23725ca7..c9dab323 100644 --- a/src/python/gudhi/persistence_graphical_tools.py +++ b/src/python/gudhi/persistence_graphical_tools.py @@ -51,7 +51,8 @@ def plot_persistence_barcode( max_barcodes=1000, inf_delta=0.1, legend=False, - colormap=None + colormap=None, + axes=None ): """This function plots the persistence bar code from persistence values list or from a :doc:`persistence file `. @@ -77,8 +78,10 @@ def plot_persistence_barcode( :param colormap: A matplotlib-like qualitative colormaps. Default is None which means :code:`matplotlib.cm.Set1.colors`. :type colormap: tuple of colors (3-tuple of float between 0. and 1.). - :returns: A matplotlib object containing horizontal bar plot of persistence - (launch `show()` method on it to display it). + :param axes: A matplotlib-like subplot axes. If None, the plot is drawn on + a new set of axes. + :type axes: `matplotlib.axes.Axes` + :returns: (`matplotlib.axes.Axes`): The axes on which the plot was drawn. """ try: import matplotlib.pyplot as plt @@ -112,6 +115,8 @@ def plot_persistence_barcode( if colormap == None: colormap = plt.cm.Set1.colors + if axes == None: + fig, axes = plt.subplots(1, 1) persistence = sorted(persistence, key=lambda birth: birth[1][0]) @@ -126,7 +131,7 @@ def plot_persistence_barcode( for interval in reversed(persistence): if float(interval[1][1]) != float("inf"): # Finite death case - plt.barh( + axes.barh( ind, (interval[1][1] - interval[1][0]), height=0.8, @@ -137,7 +142,7 @@ def plot_persistence_barcode( ) else: # Infinite death case for diagram to be nicer - plt.barh( + axes.barh( ind, (infinity - interval[1][0]), height=0.8, @@ -150,17 +155,19 @@ def plot_persistence_barcode( if legend: dimensions = list(set(item[0] for item in persistence)) - plt.legend( + axes.legend( handles=[ mpatches.Patch(color=colormap[dim], label=str(dim)) for dim in dimensions ], loc="lower right", ) - plt.title("Persistence barcode") + + axes.set_title("Persistence barcode") + # Ends plot on infinity value and starts a little bit before min_birth - plt.axis([axis_start, infinity, 0, ind]) - return plt + axes.axis([axis_start, infinity, 0, ind]) + return axes except ImportError: print("This function is not available, you may be missing matplotlib.") @@ -175,7 +182,8 @@ def plot_persistence_diagram( max_plots=1000, inf_delta=0.1, legend=False, - colormap=None + colormap=None, + axes=None ): """This function plots the persistence diagram from persistence values list or from a :doc:`persistence file `. @@ -203,8 +211,10 @@ def plot_persistence_diagram( :param colormap: A matplotlib-like qualitative colormaps. Default is None which means :code:`matplotlib.cm.Set1.colors`. :type colormap: tuple of colors (3-tuple of float between 0. and 1.). - :returns: A matplotlib object containing diagram plot of persistence - (launch `show()` method on it to display it). + :param axes: A matplotlib-like subplot axes. If None, the plot is drawn on + a new set of axes. + :type axes: `matplotlib.axes.Axes` + :returns: (`matplotlib.axes.Axes`): The axes on which the plot was drawn. """ try: import matplotlib.pyplot as plt @@ -238,6 +248,8 @@ def plot_persistence_diagram( if colormap == None: colormap = plt.cm.Set1.colors + if axes == None: + fig, axes = plt.subplots(1, 1) (min_birth, max_death) = __min_birth_max_death(persistence, band) delta = (max_death - min_birth) * inf_delta @@ -249,18 +261,18 @@ def plot_persistence_diagram( # line display of equation : birth = death x = np.linspace(axis_start, infinity, 1000) # infinity line and text - plt.plot(x, x, color="k", linewidth=1.0) - plt.plot(x, [infinity] * len(x), linewidth=1.0, color="k", alpha=alpha) - plt.text(axis_start, infinity, r"$\infty$", color="k", alpha=alpha) + axes.plot(x, x, color="k", linewidth=1.0) + axes.plot(x, [infinity] * len(x), linewidth=1.0, color="k", alpha=alpha) + axes.text(axis_start, infinity, r"$\infty$", color="k", alpha=alpha) # bootstrap band if band > 0.0: - plt.fill_between(x, x, x + band, alpha=alpha, facecolor="red") + axes.fill_between(x, x, x + band, alpha=alpha, facecolor="red") # Draw points in loop for interval in reversed(persistence): if float(interval[1][1]) != float("inf"): # Finite death case - plt.scatter( + axes.scatter( interval[1][0], interval[1][1], alpha=alpha, @@ -268,25 +280,25 @@ def plot_persistence_diagram( ) else: # Infinite death case for diagram to be nicer - plt.scatter( + axes.scatter( interval[1][0], infinity, alpha=alpha, color=colormap[interval[0]] ) if legend: dimensions = list(set(item[0] for item in persistence)) - plt.legend( + axes.legend( handles=[ mpatches.Patch(color=colormap[dim], label=str(dim)) for dim in dimensions ] ) - plt.title("Persistence diagram") - plt.xlabel("Birth") - plt.ylabel("Death") + axes.set_xlabel("Birth") + axes.set_ylabel("Death") # Ends plot on infinity value and starts a little bit before min_birth - plt.axis([axis_start, infinity, axis_start, infinity + delta]) - return plt + axes.axis([axis_start, infinity, axis_start, infinity + delta]) + axes.set_title("Persistence diagram") + return axes except ImportError: print("This function is not available, you may be missing matplotlib.") @@ -301,6 +313,7 @@ def plot_persistence_density( dimension=None, cmap=None, legend=False, + axes=None ): """This function plots the persistence density from persistence values list or from a :doc:`persistence file `. Be @@ -339,8 +352,10 @@ def plot_persistence_density( :type cmap: cf. matplotlib colormap. :param legend: Display the color bar values (default is False). :type legend: boolean. - :returns: A matplotlib object containing diagram plot of persistence - (launch `show()` method on it to display it). + :param axes: A matplotlib-like subplot axes. If None, the plot is drawn on + a new set of axes. + :type axes: `matplotlib.axes.Axes` + :returns: (`matplotlib.axes.Axes`): The axes on which the plot was drawn. """ try: import matplotlib.pyplot as plt @@ -383,9 +398,15 @@ def plot_persistence_density( birth = persistence_dim[:, 0] death = persistence_dim[:, 1] + # default cmap value cannot be done at argument definition level as matplotlib is not yet defined. + if cmap is None: + cmap = plt.cm.hot_r + if axes == None: + fig, axes = plt.subplots(1, 1) + # line display of equation : birth = death x = np.linspace(death.min(), birth.max(), 1000) - plt.plot(x, x, color="k", linewidth=1.0) + axes.plot(x, x, color="k", linewidth=1.0) # Evaluate a gaussian kde on a regular grid of nbins x nbins over data extents k = kde.gaussian_kde([birth, death], bw_method=bw_method) @@ -395,19 +416,16 @@ def plot_persistence_density( ] zi = k(np.vstack([xi.flatten(), yi.flatten()])) - # default cmap value cannot be done at argument definition level as matplotlib is not yet defined. - if cmap is None: - cmap = plt.cm.hot_r # Make the plot - plt.pcolormesh(xi, yi, zi.reshape(xi.shape), cmap=cmap) + axes.pcolormesh(xi, yi, zi.reshape(xi.shape), cmap=cmap) if legend: - plt.colorbar() + axes.colorbar() - plt.title("Persistence density") - plt.xlabel("Birth") - plt.ylabel("Death") - return plt + axes.set_xlabel("Birth") + axes.set_ylabel("Death") + axes.set_title("Persistence density") + return axes except ImportError: print( -- cgit v1.2.3 From 7ea7538d02b8f0500dbc31f48dd31fb14d320135 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Wed, 20 Nov 2019 20:30:36 +0100 Subject: Minor tweaks to representations uniform -> even matmul -> indexing min+max -> clip --- src/python/gudhi/representations/kernel_methods.py | 2 +- src/python/gudhi/representations/preprocessing.py | 2 +- src/python/gudhi/representations/vector_methods.py | 34 +++++++++++----------- 3 files changed, 19 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/representations/kernel_methods.py b/src/python/gudhi/representations/kernel_methods.py index c855d2be..bfc83aff 100644 --- a/src/python/gudhi/representations/kernel_methods.py +++ b/src/python/gudhi/representations/kernel_methods.py @@ -161,7 +161,7 @@ class PersistenceScaleSpaceKernel(BaseEstimator, TransformerMixin): """ Xp = list(X) for i in range(len(Xp)): - op_X = np.matmul(Xp[i], np.array([[0.,1.], [1.,0.]])) + op_X = Xp[i][:,[1,0]] Xp[i] = np.concatenate([Xp[i], op_X], axis=0) return self.pwg_.transform(Xp) diff --git a/src/python/gudhi/representations/preprocessing.py b/src/python/gudhi/representations/preprocessing.py index 83227ca1..487b5376 100644 --- a/src/python/gudhi/representations/preprocessing.py +++ b/src/python/gudhi/representations/preprocessing.py @@ -30,7 +30,7 @@ class BirthPersistenceTransform(BaseEstimator, TransformerMixin): Fit the BirthPersistenceTransform class on a list of persistence diagrams (this function actually does nothing but is useful when BirthPersistenceTransform is included in a scikit-learn Pipeline). Parameters: - X (n x 2 numpy array): input persistence diagrams. + X (list of n x 2 numpy array): input persistence diagrams. y (n x 1 array): persistence diagram labels (unused). """ return self diff --git a/src/python/gudhi/representations/vector_methods.py b/src/python/gudhi/representations/vector_methods.py index bf32f18e..61c4fb84 100644 --- a/src/python/gudhi/representations/vector_methods.py +++ b/src/python/gudhi/representations/vector_methods.py @@ -83,7 +83,7 @@ class PersistenceImage(BaseEstimator, TransformerMixin): class Landscape(BaseEstimator, TransformerMixin): """ - This is a class for computing persistence landscapes from a list of persistence diagrams. A persistence landscape is a collection of 1D piecewise-linear functions computed from the rank function associated to the persistence diagram. These piecewise-linear functions are then sampled uniformly on a given range and the corresponding vectors of samples are concatenated and returned. See http://jmlr.org/papers/v16/bubenik15a.html for more details. + This is a class for computing persistence landscapes from a list of persistence diagrams. A persistence landscape is a collection of 1D piecewise-linear functions computed from the rank function associated to the persistence diagram. These piecewise-linear functions are then sampled evenly on a given range and the corresponding vectors of samples are concatenated and returned. See http://jmlr.org/papers/v16/bubenik15a.html for more details. """ def __init__(self, num_landscapes=5, resolution=100, sample_range=[np.nan, np.nan]): """ @@ -92,7 +92,7 @@ class Landscape(BaseEstimator, TransformerMixin): Parameters: num_landscapes (int): number of piecewise-linear functions to output (default 5). resolution (int): number of sample for all piecewise-linear functions (default 100). - sample_range ([double, double]): minimum and maximum of all piecewise-linear function domains, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + sample_range ([double, double]): minimum and maximum of all piecewise-linear function domains, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn evenly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. """ self.num_landscapes, self.resolution, self.sample_range = num_landscapes, resolution, sample_range @@ -136,9 +136,9 @@ class Landscape(BaseEstimator, TransformerMixin): for j in range(num_pts_in_diag): [px,py] = diagram[j,:2] - min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + min_idx = np.clip(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0, self.resolution) + mid_idx = np.clip(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0, self.resolution) + max_idx = np.clip(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0, self.resolution) if min_idx < self.resolution and max_idx > 0: @@ -165,7 +165,7 @@ class Landscape(BaseEstimator, TransformerMixin): class Silhouette(BaseEstimator, TransformerMixin): """ - This is a class for computing persistence silhouettes from a list of persistence diagrams. A persistence silhouette is computed by taking a weighted average of the collection of 1D piecewise-linear functions given by the persistence landscapes, and then by uniformly sampling this average on a given range. Finally, the corresponding vector of samples is returned. See https://arxiv.org/abs/1312.0308 for more details. + This is a class for computing persistence silhouettes from a list of persistence diagrams. A persistence silhouette is computed by taking a weighted average of the collection of 1D piecewise-linear functions given by the persistence landscapes, and then by evenly sampling this average on a given range. Finally, the corresponding vector of samples is returned. See https://arxiv.org/abs/1312.0308 for more details. """ def __init__(self, weight=lambda x: 1, resolution=100, sample_range=[np.nan, np.nan]): """ @@ -174,7 +174,7 @@ class Silhouette(BaseEstimator, TransformerMixin): Parameters: weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie on lists or numpy arrays of the form [p_x,p_y]. resolution (int): number of samples for the weighted average (default 100). - sample_range ([double, double]): minimum and maximum for the weighted average domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + sample_range ([double, double]): minimum and maximum for the weighted average domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn evenly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. """ self.weight, self.resolution, self.sample_range = weight, resolution, sample_range @@ -219,9 +219,9 @@ class Silhouette(BaseEstimator, TransformerMixin): [px,py] = diagram[j,:2] weight = weights[j] / total_weight - min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + min_idx = np.clip(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0, self.resolution) + mid_idx = np.clip(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0, self.resolution) + max_idx = np.clip(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0, self.resolution) if min_idx < self.resolution and max_idx > 0: @@ -243,7 +243,7 @@ class Silhouette(BaseEstimator, TransformerMixin): class BettiCurve(BaseEstimator, TransformerMixin): """ - This is a class for computing Betti curves from a list of persistence diagrams. A Betti curve is a 1D piecewise-constant function obtained from the rank function. It is sampled uniformly on a given range and the vector of samples is returned. See https://www.researchgate.net/publication/316604237_Time_Series_Classification_via_Topological_Data_Analysis for more details. + This is a class for computing Betti curves from a list of persistence diagrams. A Betti curve is a 1D piecewise-constant function obtained from the rank function. It is sampled evenly on a given range and the vector of samples is returned. See https://www.researchgate.net/publication/316604237_Time_Series_Classification_via_Topological_Data_Analysis for more details. """ def __init__(self, resolution=100, sample_range=[np.nan, np.nan]): """ @@ -251,7 +251,7 @@ class BettiCurve(BaseEstimator, TransformerMixin): Parameters: resolution (int): number of sample for the piecewise-constant function (default 100). - sample_range ([double, double]): minimum and maximum of the piecewise-constant function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + sample_range ([double, double]): minimum and maximum of the piecewise-constant function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn evenly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. """ self.resolution, self.sample_range = resolution, sample_range @@ -290,8 +290,8 @@ class BettiCurve(BaseEstimator, TransformerMixin): bc = np.zeros(self.resolution) for j in range(num_pts_in_diag): [px,py] = diagram[j,:2] - min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + min_idx = np.clip(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0, self.resolution) + max_idx = np.clip(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0, self.resolution) for k in range(min_idx, max_idx): bc[k] += 1 @@ -313,7 +313,7 @@ class Entropy(BaseEstimator, TransformerMixin): mode (string): what entropy to compute: either "scalar" for computing the entropy statistics, or "vector" for computing the entropy summary functions (default "scalar"). normalized (bool): whether to normalize the entropy summary function (default True). Used only if **mode** = "vector". resolution (int): number of sample for the entropy summary function (default 100). Used only if **mode** = "vector". - sample_range ([double, double]): minimum and maximum of the entropy summary function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. Used only if **mode** = "vector". + sample_range ([double, double]): minimum and maximum of the entropy summary function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn evenly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. Used only if **mode** = "vector". """ self.mode, self.normalized, self.resolution, self.sample_range = mode, normalized, resolution, sample_range @@ -359,8 +359,8 @@ class Entropy(BaseEstimator, TransformerMixin): ent = np.zeros(self.resolution) for j in range(num_pts_in_diag): [px,py] = orig_diagram[j,:2] - min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + min_idx = np.clip(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0, self.resolution) + max_idx = np.clip(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0, self.resolution) for k in range(min_idx, max_idx): ent[k] += (-1) * new_diagram[j,1] * np.log(new_diagram[j,1]) if self.normalized: -- cgit v1.2.3 From 96c90dd002e7d4f30ce3d0f3ddf45fbfd03fc01a Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Wed, 20 Nov 2019 22:41:54 +0100 Subject: More tweaks in representations wording for the case where CGAL is missing comparing pointers is sufficient to detect if we are doing fit_transform epsilon is an additive error for bottleneck, so it is unsafe to use 1e-3 or 1e-4 as default --- src/python/gudhi/representations/metrics.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/representations/metrics.py b/src/python/gudhi/representations/metrics.py index c512cb82..f9f59053 100644 --- a/src/python/gudhi/representations/metrics.py +++ b/src/python/gudhi/representations/metrics.py @@ -86,12 +86,12 @@ class BottleneckDistance(BaseEstimator, TransformerMixin): """ This is a class for computing the bottleneck distance matrix from a list of persistence diagrams. """ - def __init__(self, epsilon=1e-3): + def __init__(self, epsilon=None): """ Constructor for the BottleneckDistance class. Parameters: - epsilon (double): approximation quality (default 1e-4). + epsilon (double): absolute (additive) error tolerated on the distance (default is the smallest positive float), see :func:`bottleneck_distance`. """ self.epsilon = epsilon @@ -118,7 +118,8 @@ class BottleneckDistance(BaseEstimator, TransformerMixin): """ num_diag1 = len(X) - if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): + #if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): + if X is self.diagrams_: matrix = np.zeros((num_diag1, num_diag1)) if USE_GUDHI: @@ -127,7 +128,7 @@ class BottleneckDistance(BaseEstimator, TransformerMixin): matrix[i,j] = bottleneck_distance(X[i], X[j], self.epsilon) matrix[j,i] = matrix[i,j] else: - print("Gudhi required---returning null matrix") + print("Gudhi built without CGAL: returning a null matrix") else: num_diag2 = len(self.diagrams_) @@ -138,7 +139,7 @@ class BottleneckDistance(BaseEstimator, TransformerMixin): for j in range(num_diag2): matrix[i,j] = bottleneck_distance(X[i], self.diagrams_[j], self.epsilon) else: - print("Gudhi required---returning null matrix") + print("Gudhi built without CGAL: returning a null matrix") Xfit = matrix -- cgit v1.2.3 From 1e3b4f6c56409dde933341701575dc105ede12db Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 21 Nov 2019 09:34:06 +0100 Subject: Code review : review races for read_persistence_intervals_in_dimension function --- src/common/include/gudhi/reader_utils.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/common/include/gudhi/reader_utils.h b/src/common/include/gudhi/reader_utils.h index 5e96b5db..db31bf5c 100644 --- a/src/common/include/gudhi/reader_utils.h +++ b/src/common/include/gudhi/reader_utils.h @@ -293,6 +293,9 @@ Note: the function does not check that birth <= death. **/ template void read_persistence_intervals_and_dimension(std::string const& filename, OutputIterator out) { +#ifdef DEBUG_TRACES + std::cout << "read_persistence_intervals_and_dimension - " << filename << std::endl; +#endif // DEBUG_TRACES std::ifstream in(filename); if (!in.is_open()) { std::string error_str("read_persistence_intervals_and_dimension - Unable to open file "); @@ -308,8 +311,11 @@ void read_persistence_intervals_and_dimension(std::string const& filename, Outpu double numbers[4]; int n = sscanf(line.c_str(), "%lf %lf %lf %lf", &numbers[0], &numbers[1], &numbers[2], &numbers[3]); #ifdef DEBUG_TRACES - std::cout << "[" << n << "] = " << numbers[0] << "," << numbers[1] - << "," << numbers[2] << "," << numbers[3] << std::endl; + std::cout << "[" << n << "] = "; + for (int i = 0; i < n; i++) { + std::cout << numbers[i] << ","; + } + std::cout << std::endl; #endif // DEBUG_TRACES if (n >= 2) { int dim = (n >= 3 ? static_cast(numbers[n - 3]) : -1); -- cgit v1.2.3 From 84299342910d18aeaa1a605fe224bede13d647dc Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Thu, 21 Nov 2019 22:50:05 +0100 Subject: Generalize Clamping to min and max Fix #146. --- src/python/example/diagram_vectorizations_distances_kernels.py | 2 +- src/python/gudhi/representations/preprocessing.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/python/example/diagram_vectorizations_distances_kernels.py b/src/python/example/diagram_vectorizations_distances_kernels.py index a77bbfdd..119072eb 100755 --- a/src/python/example/diagram_vectorizations_distances_kernels.py +++ b/src/python/example/diagram_vectorizations_distances_kernels.py @@ -16,7 +16,7 @@ diags = [D] diags = DiagramSelector(use=True, point_type="finite").fit_transform(diags) diags = DiagramScaler(use=True, scalers=[([0,1], MinMaxScaler())]).fit_transform(diags) -diags = DiagramScaler(use=True, scalers=[([1], Clamping(limit=.9))]).fit_transform(diags) +diags = DiagramScaler(use=True, scalers=[([1], Clamping(maximum=.9))]).fit_transform(diags) D = diags[0] plt.scatter(D[:,0],D[:,1]) diff --git a/src/python/gudhi/representations/preprocessing.py b/src/python/gudhi/representations/preprocessing.py index 487b5376..a39b00e4 100644 --- a/src/python/gudhi/representations/preprocessing.py +++ b/src/python/gudhi/representations/preprocessing.py @@ -58,14 +58,15 @@ class Clamping(BaseEstimator, TransformerMixin): """ This is a class for clamping values. It can be used as a parameter for the DiagramScaler class, for instance if you want to clamp abscissae or ordinates of persistence diagrams. """ - def __init__(self, limit=np.inf): + def __init__(self, minimum=-np.inf, maximum=np.inf): """ Constructor for the Clamping class. Parameters: limit (double): clamping value (default np.inf). """ - self.limit = limit + self.minimum = minimum + self.maximum = maximum def fit(self, X, y=None): """ @@ -87,8 +88,7 @@ class Clamping(BaseEstimator, TransformerMixin): Returns: numpy array of size n: output list of values. """ - Xfit = np.minimum(X, self.limit) - #Xfit = np.where(X >= self.limit, self.limit * np.ones(X.shape), X) + Xfit = np.clip(X, self.minimum, self.maximum) return Xfit class DiagramScaler(BaseEstimator, TransformerMixin): -- cgit v1.2.3 From da22cbc891b2b7a9b326e3840533b4d3b49a26d3 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Fri, 22 Nov 2019 11:50:34 +0100 Subject: Fix link to bottleneck_distance. --- src/python/gudhi/representations/metrics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/python/gudhi/representations/metrics.py b/src/python/gudhi/representations/metrics.py index f9f59053..5f9ec6ab 100644 --- a/src/python/gudhi/representations/metrics.py +++ b/src/python/gudhi/representations/metrics.py @@ -91,7 +91,7 @@ class BottleneckDistance(BaseEstimator, TransformerMixin): Constructor for the BottleneckDistance class. Parameters: - epsilon (double): absolute (additive) error tolerated on the distance (default is the smallest positive float), see :func:`bottleneck_distance`. + epsilon (double): absolute (additive) error tolerated on the distance (default is the smallest positive float), see :func:`gudhi.bottleneck_distance`. """ self.epsilon = epsilon -- cgit v1.2.3 From d2d22e40e9ae720accbc027bb86d3f7ec7d721de Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 22 Nov 2019 15:29:37 +0100 Subject: Simplify and boldify main pages (modules, installation, examples, ...) in the left top menu of sphinx documentation --- src/python/doc/_templates/layout.html | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/python/doc/_templates/layout.html b/src/python/doc/_templates/layout.html index fe64fb3d..2f2d9c72 100644 --- a/src/python/doc/_templates/layout.html +++ b/src/python/doc/_templates/layout.html @@ -56,12 +56,16 @@

{%- endif %} {%- endblock %} -

GUDHI

-

File formats

-

GUDHI installation

-

Acknowledging the GUDHI library

-

Index

-

Examples

+ + + {%- if sidebars != None %} {#- new style sidebar: explicitly include/exclude templates #} {%- for sidebartemplate in sidebars %} -- cgit v1.2.3 From 0bceb28453a10d94571ff7adff8ddbe19cae0eba Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 22 Nov 2019 15:30:22 +0100 Subject: Fix typo - module*s* require plural form --- src/python/doc/index.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/python/doc/index.rst b/src/python/doc/index.rst index 1ef08096..c36a578f 100644 --- a/src/python/doc/index.rst +++ b/src/python/doc/index.rst @@ -1,5 +1,5 @@ -GUDHI Python module documentation -################################# +GUDHI Python modules documentation +################################## .. figure:: ../../doc/common/Gudhi_banner.png -- cgit v1.2.3 From eefdb67e436ba3b881c06b8248d169209dd7016c Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 22 Nov 2019 16:19:36 +0100 Subject: Add information about *make python* and *make clean* target. It does not fix, but workaround issues #88 #89 and #90 --- src/python/doc/installation.rst | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'src') diff --git a/src/python/doc/installation.rst b/src/python/doc/installation.rst index 7699a5bb..54504413 100644 --- a/src/python/doc/installation.rst +++ b/src/python/doc/installation.rst @@ -40,6 +40,20 @@ To build the GUDHI Python module, run the following commands in a terminal: cd python make +.. note:: + + :code:`make python` (or :code:`make` in python directory) is only a + `CMake custom targets `_ + to shortcut :code:`python setup.py build_ext --inplace` command. + No specific other options (:code:`-j8` for parallel, or even :code:`make clean`, ...) are + available. + But one can use :code:`python setup.py ...` specific options in the python directory: + +.. code-block:: bash + + python setup.py clean --all # Clean former compilation + python setup.py build_ext -j 8 --inplace # Build in parallel + GUDHI Python module installation ================================ @@ -59,6 +73,17 @@ Or install it definitely in your Python packages folder: # May require sudo or administrator privileges make install +.. note:: + + :code:`make install` is only a + `CMake custom targets `_ + to shortcut :code:`python setup.py install` command. + It does not take into account :code:`CMAKE_INSTALL_PREFIX`. + But one can use :code:`python setup.py install ...` specific options in the python directory: + +.. code-block:: bash + + python setup.py install --prefix /home/gudhi # Install in /home/gudhi directory Test suites =========== -- cgit v1.2.3 From b952302afa204e2b9e23ad503043655b2a0971b0 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 22 Nov 2019 17:28:46 +0100 Subject: Fix #142: Bottleneck.h already have such a check and CGAL dependency is only due to Bottleneck --- src/Nerve_GIC/include/gudhi/GIC.h | 5 ----- 1 file changed, 5 deletions(-) (limited to 'src') diff --git a/src/Nerve_GIC/include/gudhi/GIC.h b/src/Nerve_GIC/include/gudhi/GIC.h index fc6a2a91..b8169c59 100644 --- a/src/Nerve_GIC/include/gudhi/GIC.h +++ b/src/Nerve_GIC/include/gudhi/GIC.h @@ -48,11 +48,6 @@ #include #include -// Make compilation fail - required for external projects - https://github.com/GUDHI/gudhi-devel/issues/10 -#if CGAL_VERSION_NR < 1041101000 -# error Alpha_complex_3d is only available for CGAL >= 4.11 -#endif - namespace Gudhi { namespace cover_complex { -- cgit v1.2.3 From 85cab5aeff0407e1a8095328527e00b94f2796c6 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 22 Nov 2019 17:29:55 +0100 Subject: Error message in #10 fix was not correct, bad copy/paste --- src/Bottleneck_distance/include/gudhi/Bottleneck.h | 2 +- src/Contraction/include/gudhi/Skeleton_blocker_contractor.h | 2 +- src/Spatial_searching/include/gudhi/Kd_tree_search.h | 4 ++-- src/Tangential_complex/include/gudhi/Tangential_complex.h | 4 ++-- src/Witness_complex/include/gudhi/Euclidean_strong_witness_complex.h | 4 ++-- src/Witness_complex/include/gudhi/Euclidean_witness_complex.h | 4 ++-- src/common/include/gudhi/random_point_generators.h | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/Bottleneck_distance/include/gudhi/Bottleneck.h b/src/Bottleneck_distance/include/gudhi/Bottleneck.h index 82ba9f68..e466828a 100644 --- a/src/Bottleneck_distance/include/gudhi/Bottleneck.h +++ b/src/Bottleneck_distance/include/gudhi/Bottleneck.h @@ -26,7 +26,7 @@ // Make compilation fail - required for external projects - https://github.com/GUDHI/gudhi-devel/issues/10 #if CGAL_VERSION_NR < 1041101000 -# error Alpha_complex_3d is only available for CGAL >= 4.11 +# error bottleneck_distance is only available for CGAL >= 4.11 #endif namespace Gudhi { diff --git a/src/Contraction/include/gudhi/Skeleton_blocker_contractor.h b/src/Contraction/include/gudhi/Skeleton_blocker_contractor.h index c2b3157c..a0d9f2b2 100644 --- a/src/Contraction/include/gudhi/Skeleton_blocker_contractor.h +++ b/src/Contraction/include/gudhi/Skeleton_blocker_contractor.h @@ -40,7 +40,7 @@ // Make compilation fail - required for external projects - https://github.com/GUDHI/gudhi-devel/issues/10 #if CGAL_VERSION_NR < 1041101000 -# error Alpha_complex_3d is only available for CGAL >= 4.11 +# error Skeleton_blocker_contractor is only available for CGAL >= 4.11 #endif namespace Gudhi { diff --git a/src/Spatial_searching/include/gudhi/Kd_tree_search.h b/src/Spatial_searching/include/gudhi/Kd_tree_search.h index fedbb32e..87969dd9 100644 --- a/src/Spatial_searching/include/gudhi/Kd_tree_search.h +++ b/src/Spatial_searching/include/gudhi/Kd_tree_search.h @@ -30,11 +30,11 @@ // Make compilation fail - required for external projects - https://github.com/GUDHI/gudhi-devel/issues/10 #if CGAL_VERSION_NR < 1041101000 -# error Alpha_complex_3d is only available for CGAL >= 4.11 +# error Kd_tree_search is only available for CGAL >= 4.11 #endif #if !EIGEN_VERSION_AT_LEAST(3,1,0) -# error Alpha_complex_3d is only available for Eigen3 >= 3.1.0 installed with CGAL +# error Kd_tree_search is only available for Eigen3 >= 3.1.0 installed with CGAL #endif namespace Gudhi { diff --git a/src/Tangential_complex/include/gudhi/Tangential_complex.h b/src/Tangential_complex/include/gudhi/Tangential_complex.h index f59476b1..f058fa9f 100644 --- a/src/Tangential_complex/include/gudhi/Tangential_complex.h +++ b/src/Tangential_complex/include/gudhi/Tangential_complex.h @@ -67,11 +67,11 @@ // Make compilation fail - required for external projects - https://github.com/GUDHI/gudhi-devel/issues/10 #if CGAL_VERSION_NR < 1041101000 -# error Alpha_complex_3d is only available for CGAL >= 4.11 +# error Tangential_complex is only available for CGAL >= 4.11 #endif #if !EIGEN_VERSION_AT_LEAST(3,1,0) -# error Alpha_complex_3d is only available for Eigen3 >= 3.1.0 installed with CGAL +# error Tangential_complex is only available for Eigen3 >= 3.1.0 installed with CGAL #endif namespace sps = Gudhi::spatial_searching; diff --git a/src/Witness_complex/include/gudhi/Euclidean_strong_witness_complex.h b/src/Witness_complex/include/gudhi/Euclidean_strong_witness_complex.h index 7d3c2d6d..4d5e73f2 100644 --- a/src/Witness_complex/include/gudhi/Euclidean_strong_witness_complex.h +++ b/src/Witness_complex/include/gudhi/Euclidean_strong_witness_complex.h @@ -25,11 +25,11 @@ // Make compilation fail - required for external projects - https://github.com/GUDHI/gudhi-devel/issues/10 #if CGAL_VERSION_NR < 1041101000 -# error Alpha_complex_3d is only available for CGAL >= 4.11 +# error Euclidean_strong_witness_complex is only available for CGAL >= 4.11 #endif #if !EIGEN_VERSION_AT_LEAST(3,1,0) -# error Alpha_complex_3d is only available for Eigen3 >= 3.1.0 installed with CGAL +# error Euclidean_strong_witness_complex is only available for Eigen3 >= 3.1.0 installed with CGAL #endif namespace Gudhi { diff --git a/src/Witness_complex/include/gudhi/Euclidean_witness_complex.h b/src/Witness_complex/include/gudhi/Euclidean_witness_complex.h index 21682ec4..09cb7ec2 100644 --- a/src/Witness_complex/include/gudhi/Euclidean_witness_complex.h +++ b/src/Witness_complex/include/gudhi/Euclidean_witness_complex.h @@ -27,11 +27,11 @@ // Make compilation fail - required for external projects - https://github.com/GUDHI/gudhi-devel/issues/10 #if CGAL_VERSION_NR < 1041101000 -# error Alpha_complex_3d is only available for CGAL >= 4.11 +# error Euclidean_witness_complex is only available for CGAL >= 4.11 #endif #if !EIGEN_VERSION_AT_LEAST(3,1,0) -# error Alpha_complex_3d is only available for Eigen3 >= 3.1.0 installed with CGAL +# error Euclidean_witness_complex is only available for Eigen3 >= 3.1.0 installed with CGAL #endif namespace Gudhi { diff --git a/src/common/include/gudhi/random_point_generators.h b/src/common/include/gudhi/random_point_generators.h index fb69f832..9dd88ac4 100644 --- a/src/common/include/gudhi/random_point_generators.h +++ b/src/common/include/gudhi/random_point_generators.h @@ -21,7 +21,7 @@ // Make compilation fail - required for external projects - https://github.com/GUDHI/gudhi-devel/issues/10 #if CGAL_VERSION_NR < 1041101000 -# error Alpha_complex_3d is only available for CGAL >= 4.11 +# error random_point_generators is only available for CGAL >= 4.11 #endif namespace Gudhi { -- cgit v1.2.3 From 1caf1fb2ea7b12ddb12004758ebf4d28871ec7cd Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 26 Nov 2019 11:30:58 +0100 Subject: Fix doxygen warnings about new cgal bibtex entries --- src/Alpha_complex/doc/Intro_alpha_complex.h | 6 +++--- src/Alpha_complex/include/gudhi/Alpha_complex.h | 2 +- src/common/doc/installation.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/Alpha_complex/doc/Intro_alpha_complex.h b/src/Alpha_complex/doc/Intro_alpha_complex.h index adc1378f..3c32a1e6 100644 --- a/src/Alpha_complex/doc/Intro_alpha_complex.h +++ b/src/Alpha_complex/doc/Intro_alpha_complex.h @@ -38,12 +38,12 @@ namespace alpha_complex { * * Alpha_complex is constructing a Delaunay Triangulation - * \cite cgal:hdj-t-15b from CGAL (the Computational Geometry - * Algorithms Library \cite cgal:eb-15b) and is able to create a `SimplicialComplexForAlpha`. + * \cite cgal:hdj-t-19b from CGAL (the Computational Geometry + * Algorithms Library \cite cgal:eb-19b) and is able to create a `SimplicialComplexForAlpha`. * * The complex is a template class requiring an Epick_d dD Geometry Kernel - * \cite cgal:s-gkd-15b from CGAL as template parameter. + * \cite cgal:s-gkd-19b from CGAL as template parameter. * * \remark * - When the simplicial complex is constructed with an infinite value of alpha, the complex is a Delaunay diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex.h b/src/Alpha_complex/include/gudhi/Alpha_complex.h index 0c2569c8..6b4d8463 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex.h @@ -72,7 +72,7 @@ template struct Is_Epeck_D> { static const bool val * or an CGAL::Epick_d dD Geometry Kernel - * \cite cgal:s-gkd-15b from CGAL as template, default value is CGAL::Epeck_d * < * CGAL::Dynamic_dimension_tag > diff --git a/src/common/doc/installation.h b/src/common/doc/installation.h index 2e64bef8..c320e7e0 100644 --- a/src/common/doc/installation.h +++ b/src/common/doc/installation.h @@ -71,7 +71,7 @@ make doxygen * your operating system is detailed here http://doc.cgal.org/latest/Manual/installation.html * * The following examples/utilities require the Computational Geometry Algorithms - * Library (CGAL \cite cgal:eb-15b) and will not be built if CGAL version 4.11.0 or higher is not installed: + * Library (CGAL \cite cgal:eb-19b) and will not be built if CGAL version 4.11.0 or higher is not installed: * \li * Simplex_tree/example_alpha_shapes_3_simplex_tree_from_off_file.cpp * \li -- cgit v1.2.3 From 177e80b653d60119acb4455feaba02615083532b Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Tue, 26 Nov 2019 17:51:50 +0100 Subject: Fix link. --- src/python/doc/representations.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/python/doc/representations.rst b/src/python/doc/representations.rst index a137a035..c870f834 100644 --- a/src/python/doc/representations.rst +++ b/src/python/doc/representations.rst @@ -10,7 +10,7 @@ Representations manual This module, originally named sklearn_tda, aims at bridging the gap between persistence diagrams and machine learning tools, in particular scikit-learn. It provides tools, using the scikit-learn standard interface, to compute distances and kernels on diagrams, and to convert diagrams into vectors. -A diagram is represented as a numpy array of shape (n,2), as can be obtained from `SimplexTree.persistence_intervals_in_dimension` for instance. Points at infinity are represented as a numpy array of shape (n,1), storing only the birth time. +A diagram is represented as a numpy array of shape (n,2), as can be obtained from :func:`gudhi.SimplexTree.persistence_intervals_in_dimension` for instance. Points at infinity are represented as a numpy array of shape (n,1), storing only the birth time. A small example is provided -- cgit v1.2.3 From 1e1cb09adf07f0351557d3bf8011c9fc3e1b4628 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Wed, 27 Nov 2019 10:31:23 +0100 Subject: Fix #150 axes has no attribute colorbar --- src/python/gudhi/persistence_graphical_tools.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/persistence_graphical_tools.py b/src/python/gudhi/persistence_graphical_tools.py index c9dab323..7d232c85 100644 --- a/src/python/gudhi/persistence_graphical_tools.py +++ b/src/python/gudhi/persistence_graphical_tools.py @@ -369,7 +369,6 @@ def plot_persistence_density( persistence_dim = read_persistence_intervals_in_dimension( persistence_file=persistence_file, only_this_dim=dimension ) - print(persistence_dim) else: print("file " + persistence_file + " not found.") return None @@ -417,10 +416,10 @@ def plot_persistence_density( zi = k(np.vstack([xi.flatten(), yi.flatten()])) # Make the plot - axes.pcolormesh(xi, yi, zi.reshape(xi.shape), cmap=cmap) + img = axes.pcolormesh(xi, yi, zi.reshape(xi.shape), cmap=cmap) if legend: - axes.colorbar() + plt.colorbar(img, ax=axes) axes.set_xlabel("Birth") axes.set_ylabel("Death") -- cgit v1.2.3 From ae13d5bbcd83baa9b814b061c04b50c5c31bfc78 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Wed, 27 Nov 2019 14:01:56 +0100 Subject: Fix #112 density side by side with its diagram --- src/python/doc/persistence_graphical_tools_user.rst | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/python/doc/persistence_graphical_tools_user.rst b/src/python/doc/persistence_graphical_tools_user.rst index 2de99252..e4419bc1 100644 --- a/src/python/doc/persistence_graphical_tools_user.rst +++ b/src/python/doc/persistence_graphical_tools_user.rst @@ -67,10 +67,17 @@ If you want more information on a specific dimension, for instance: import matplotlib.pyplot as plot import gudhi - # rips_on_tore3D_1307.pers obtained from write_persistence_diagram method persistence_file=gudhi.__root_source_dir__ + \ '/data/persistence_diagram/rips_on_tore3D_1307.pers' - gudhi.plot_persistence_density(persistence_file=persistence_file, - max_intervals=0, dimension=1, legend=True) + birth_death = gudhi.read_persistence_intervals_in_dimension( + persistence_file=persistence_file, + only_this_dim=1) + pers_diag = [(1, (elt[0], elt[1])) for elt in birth_death] + # Use subplots to display diagram and density side by side + fig, axes = plot.subplots(nrows=1, ncols=2, figsize=(12, 5)) + gudhi.plot_persistence_diagram(persistence=pers_diag, + axes=axes[0]) + gudhi.plot_persistence_density(persistence=pers_diag, + dimension=1, legend=True, axes=axes[1]) plot.show() -- cgit v1.2.3 From dbb5183230e4b16bd36c5d582ef82e880741f149 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Wed, 27 Nov 2019 21:53:31 +0100 Subject: Code review : for loop simplification --- src/python/doc/persistence_graphical_tools_user.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/python/doc/persistence_graphical_tools_user.rst b/src/python/doc/persistence_graphical_tools_user.rst index e4419bc1..f41a926b 100644 --- a/src/python/doc/persistence_graphical_tools_user.rst +++ b/src/python/doc/persistence_graphical_tools_user.rst @@ -73,7 +73,7 @@ If you want more information on a specific dimension, for instance: birth_death = gudhi.read_persistence_intervals_in_dimension( persistence_file=persistence_file, only_this_dim=1) - pers_diag = [(1, (elt[0], elt[1])) for elt in birth_death] + pers_diag = [(1, elt) for elt in birth_death] # Use subplots to display diagram and density side by side fig, axes = plot.subplots(nrows=1, ncols=2, figsize=(12, 5)) gudhi.plot_persistence_diagram(persistence=pers_diag, -- cgit v1.2.3 From fe3bbb9b3de5001ba943d3be7109712847ec44ef Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Tue, 26 Nov 2019 23:08:53 +0100 Subject: Fix various links for sphinx and some minor doc changes along the way. (why were we documenting a hasse diagram that doesn't exist?) --- src/python/doc/alpha_complex_user.rst | 15 ++++++++------- src/python/doc/conf.py | 1 + src/python/doc/representations.rst | 2 +- src/python/doc/rips_complex_user.rst | 23 ++++++++++++----------- src/python/doc/simplex_tree_user.rst | 10 +++------- src/python/doc/tangential_complex_user.rst | 6 +++--- src/python/doc/witness_complex_user.rst | 4 ++-- src/python/gudhi/tangential_complex.pyx | 4 ++-- 8 files changed, 32 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/python/doc/alpha_complex_user.rst b/src/python/doc/alpha_complex_user.rst index f9662a6d..950db3cd 100644 --- a/src/python/doc/alpha_complex_user.rst +++ b/src/python/doc/alpha_complex_user.rst @@ -9,19 +9,19 @@ Definition .. include:: alpha_complex_sum.inc -Alpha_complex is constructing a :doc:`Simplex_tree ` using +`AlphaComplex` is constructing a :doc:`SimplexTree ` using `Delaunay Triangulation `_ :cite:`cgal:hdj-t-15b` from `CGAL `_ (the Computational Geometry Algorithms Library :cite:`cgal:eb-15b`). Remarks ^^^^^^^ -When Alpha_complex is constructed with an infinite value of :math:`\alpha`, the complex is a Delaunay complex. +When an :math:`\alpha`-complex is constructed with an infinite value of :math:`\alpha`, the complex is a Delaunay complex (with special filtration values). Example from points ------------------- -This example builds the Delaunay triangulation from the given points, and initializes the alpha complex with it: +This example builds the alpha-complex from the given points: .. testcode:: @@ -139,15 +139,16 @@ Non decreasing filtration values As the squared radii computed by CGAL are an approximation, it might happen that these alpha squared values do not quite define a proper filtration (i.e. non-decreasing with respect to inclusion). -We fix that up by calling `Simplex_tree::make_filtration_non_decreasing()` (cf. +We fix that up by calling :func:`~gudhi.SimplexTree.make_filtration_non_decreasing` (cf. `C++ version `_). Prune above given filtration value ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The simplex tree is pruned from the given maximum alpha squared value (cf. `Simplex_tree::prune_above_filtration()` -in the `C++ version `_). Note that this does not provide any kind -of speed-up, since we always first build the full filtered complex, so it is recommended not to use `max_alpha_square`. +The simplex tree is pruned from the given maximum alpha squared value (cf. +:func:`~gudhi.SimplexTree.prune_above_filtration`). Note that this does not provide any kind +of speed-up, since we always first build the full filtered complex, so it is recommended not to use +:paramref:`~gudhi.AlphaComplex.create_simplex_tree.max_alpha_square`. In the following example, a threshold of 59 is used. diff --git a/src/python/doc/conf.py b/src/python/doc/conf.py index 64d9cba1..3cc5d1d6 100755 --- a/src/python/doc/conf.py +++ b/src/python/doc/conf.py @@ -41,6 +41,7 @@ extensions = [ 'sphinx.ext.viewcode', 'sphinx.ext.napoleon', 'sphinxcontrib.bibtex', + 'sphinx_paramlinks', ] todo_include_todos = True diff --git a/src/python/doc/representations.rst b/src/python/doc/representations.rst index c870f834..b3131a25 100644 --- a/src/python/doc/representations.rst +++ b/src/python/doc/representations.rst @@ -10,7 +10,7 @@ Representations manual This module, originally named sklearn_tda, aims at bridging the gap between persistence diagrams and machine learning tools, in particular scikit-learn. It provides tools, using the scikit-learn standard interface, to compute distances and kernels on diagrams, and to convert diagrams into vectors. -A diagram is represented as a numpy array of shape (n,2), as can be obtained from :func:`gudhi.SimplexTree.persistence_intervals_in_dimension` for instance. Points at infinity are represented as a numpy array of shape (n,1), storing only the birth time. +A diagram is represented as a numpy array of shape (n,2), as can be obtained from :func:`~gudhi.SimplexTree.persistence_intervals_in_dimension` for instance. Points at infinity are represented as a numpy array of shape (n,1), storing only the birth time. A small example is provided diff --git a/src/python/doc/rips_complex_user.rst b/src/python/doc/rips_complex_user.rst index 3f6b960d..a8659542 100644 --- a/src/python/doc/rips_complex_user.rst +++ b/src/python/doc/rips_complex_user.rst @@ -40,12 +40,12 @@ A vertex name corresponds to the index of the point in the given range (aka. the On this example, as edges (4,5), (4,6) and (5,6) are in the complex, simplex (4,5,6) is added with the filtration value set with :math:`max(filtration(4,5), filtration(4,6), filtration(5,6))`. And so on for simplex (0,1,2,3). -If the `RipsComplex` interfaces are not detailed enough for your need, please refer to rips_persistence_step_by_step.cpp -C++ example, where the graph construction over the Simplex_tree is more detailed. +If the :doc:`RipsComplex ` interfaces are not detailed enough for your need, please refer to +rips_persistence_step_by_step.cpp C++ example, where the graph construction over the Simplex_tree is more detailed. A Rips complex can easily become huge, even if we limit the length of the edges and the dimension of the simplices. One easy trick, before building a Rips -complex on a point cloud, is to call `sparsify_point_set` which removes points +complex on a point cloud, is to call :func:`~gudhi.sparsify_point_set` which removes points that are too close to each other. This does not change its persistence diagram by more than the length used to define "too close". @@ -57,7 +57,7 @@ a :math:`\frac{1}{1-\varepsilon}`-interleaving, although in practice the error is usually smaller. A more intuitive presentation of the idea is available in :cite:`cavanna15geometric`, and in a video :cite:`cavanna15visualizing`. Passing an extra argument `sparse=0.3` at the -construction of a `RipsComplex` object asks it to build a sparse Rips with +construction of a :class:`~gudhi.RipsComplex` object asks it to build a sparse Rips with parameter :math:`\varepsilon=0.3`, while the default `sparse=None` builds the regular Rips complex. @@ -69,7 +69,7 @@ Example from a point cloud ^^^^^^^^^^^^^^^^^^^^^^^^^^ This example builds the neighborhood graph from the given points, up to max_edge_length. -Then it creates a :doc:`Simplex_tree ` with it. +Then it creates a :doc:`SimplexTree ` with it. Finally, it is asked to display information about the simplicial complex. @@ -128,7 +128,7 @@ Example from OFF file This example builds the :doc:`RipsComplex ` from the given points in an OFF file, and max_edge_length value. -Then it creates a :doc:`Simplex_tree ` with it. +Then it creates a :doc:`SimplexTree ` with it. Finally, it is asked to display information about the Rips complex. @@ -178,7 +178,7 @@ Example from a distance matrix ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This example builds the one skeleton graph from the given distance matrix, and max_edge_length value. -Then it creates a :doc:`Simplex_tree ` with it. +Then it creates a :doc:`SimplexTree ` with it. Finally, it is asked to display information about the simplicial complex. @@ -233,7 +233,7 @@ Example from csv file This example builds the :doc:`RipsComplex ` from the given distance matrix in a csv file, and max_edge_length value. -Then it creates a :doc:`Simplex_tree ` with it. +Then it creates a :doc:`SimplexTree ` with it. Finally, it is asked to display information about the Rips complex. @@ -286,7 +286,7 @@ Example from a correlation matrix Analogously to the case of distance matrix, Rips complexes can be also constructed based on correlation matrix. Given a correlation matrix M, comportment-wise 1-M is a distance matrix. This example builds the one skeleton graph from the given corelation matrix and threshold value. -Then it creates a :doc:`Simplex_tree ` with it. +Then it creates a :doc:`SimplexTree ` with it. Finally, it is asked to display information about the simplicial complex. @@ -307,7 +307,7 @@ Finally, it is asked to display information about the simplicial complex. [0.01, 0.01, 0.72, 1., 0.7], [0.89, 0.61, 0.03, 0.7, 1.]], float) - distance_matrix = np.ones((correlation_matrix.shape),float) - correlation_matrix + distance_matrix = 1 - correlation_matrix rips_complex = gudhi.RipsComplex(distance_matrix=distance_matrix, max_edge_length=1.0) simplex_tree = rips_complex.create_simplex_tree(max_dimension=1) @@ -342,6 +342,7 @@ until dimension 1 - one skeleton graph in other words), the output is: [1, 3] -> 0.99 .. note:: - As persistence diagrams points will be under the diagonal, + If you compute the persistence diagram and convert distances back to correlation values, + points in the persistence diagram will be under the diagonal, and bottleneck distance and persistence graphical tool will not work properly, this is a known issue. diff --git a/src/python/doc/simplex_tree_user.rst b/src/python/doc/simplex_tree_user.rst index aebeb29f..3df7617f 100644 --- a/src/python/doc/simplex_tree_user.rst +++ b/src/python/doc/simplex_tree_user.rst @@ -23,13 +23,9 @@ scheme. Implementation -------------- -There are two implementation of complexes. The first on is the Simplex_tree data structure. -The simplex tree is an efficient and flexible data structure for representing general (filtered) simplicial complexes. -The data structure is described in :cite`boissonnatmariasimplextreealgorithmica`. - -The second one is the Hasse_complex. The Hasse complex is a data structure representing explicitly all co-dimension 1 -incidence relations in a complex. It is consequently faster when accessing the boundary of a simplex, but is less -compact and harder to construct from scratch. +The :class:`simplex tree` is an efficient and flexible data structure for representing general +(filtered) simplicial complexes. +The data structure is described in :cite:`boissonnatmariasimplextreealgorithmica`. Example ------- diff --git a/src/python/doc/tangential_complex_user.rst b/src/python/doc/tangential_complex_user.rst index ebfe1e29..852cf5b6 100644 --- a/src/python/doc/tangential_complex_user.rst +++ b/src/python/doc/tangential_complex_user.rst @@ -107,12 +107,12 @@ inconsistencies, but is not guaranteed to succeed. Output ^^^^^^ -The result of the computation is exported as a Simplex_tree. It is the union of +The result of the computation is exported as a :class:`~gudhi.SimplexTree`. It is the union of the stars of all the input points. A vertex in the Simplex Tree is the index of the point in the range provided by the user. The point corresponding to a -vertex can also be obtained through the Tangential_complex::get_point function. +vertex can also be obtained through the :func:`gudhi.TangentialComplex.get_point` function. Note that even if the positions of the points are perturbed, their original -positions are kept (e.g. Tangential_complex::get_point returns the original +positions are kept (e.g. :func:`~gudhi.TangentialComplex.get_point` returns the original position of the point). The result can be obtained after the computation of the Tangential complex diff --git a/src/python/doc/witness_complex_user.rst b/src/python/doc/witness_complex_user.rst index 40e94134..45ba5b3b 100644 --- a/src/python/doc/witness_complex_user.rst +++ b/src/python/doc/witness_complex_user.rst @@ -47,7 +47,7 @@ which leads to definitions of **weak relaxed witness complex** (or just relaxed In particular case of 0-relaxation, weak complex corresponds to **witness complex** introduced in :cite:`de2004topological`, whereas 0-relaxed strong witness complex consists of just vertices and is not very interesting. Hence for small relaxation weak version is preferable. -However, to capture the homotopy type (for example using Gudhi::persistent_cohomology::Persistent_cohomology) it is +However, to capture the homotopy type (for example using :func:`gudhi.SimplexTree.persistence`) it is often necessary to work with higher filtration values. In this case strong relaxed witness complex is faster to compute and offers similar results. @@ -69,7 +69,7 @@ The construction of the Euclidean versions of complexes follow the same scheme: In the non-Euclidean classes, the lists of nearest landmarks are supposed to be given as input. -The constructors take on the steps 1 and 2, while the function 'create_complex' executes the step 3. +The constructors take on the steps 1 and 2, while the function :func:`!create_complex` executes the step 3. Constructing weak relaxed witness complex from an off file ---------------------------------------------------------- diff --git a/src/python/gudhi/tangential_complex.pyx b/src/python/gudhi/tangential_complex.pyx index b7678f4d..f4c8b079 100644 --- a/src/python/gudhi/tangential_complex.pyx +++ b/src/python/gudhi/tangential_complex.pyx @@ -91,7 +91,7 @@ cdef class TangentialComplex: Raises: ValueError: In debug mode, if the computed star dimension is too low. Try to set a bigger maximal edge length value with - :func:`~gudhi.Tangential_complex.set_max_squared_edge_length` + :meth:`set_max_squared_edge_length` if this happens. """ self.thisptr.compute_tangential_complex() @@ -166,7 +166,7 @@ cdef class TangentialComplex: :type max_squared_edge_length: double If the maximal edge length value is too low - :func:`~gudhi.Tangential_complex.compute_tangential_complex` + :meth:`compute_tangential_complex` will throw an exception in debug mode. """ self.thisptr.set_max_squared_edge_length(max_squared_edge_length) -- cgit v1.2.3 From e1404be9da9cda8e6f41e7cf207934f1914a3505 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 2 Dec 2019 17:35:09 +0100 Subject: CMake generates how_to_cite_gudhi.bib to auto update version --- biblio/how_to_cite_gudhi.bib | 158 ---------------------- biblio/how_to_cite_gudhi.bib.in | 158 ++++++++++++++++++++++ src/cmake/modules/GUDHI_user_version_target.cmake | 13 +- 3 files changed, 167 insertions(+), 162 deletions(-) delete mode 100644 biblio/how_to_cite_gudhi.bib create mode 100644 biblio/how_to_cite_gudhi.bib.in (limited to 'src') diff --git a/biblio/how_to_cite_gudhi.bib b/biblio/how_to_cite_gudhi.bib deleted file mode 100644 index cba94d4d..00000000 --- a/biblio/how_to_cite_gudhi.bib +++ /dev/null @@ -1,158 +0,0 @@ -@book{gudhi:urm -, title = "{GUDHI} User and Reference Manual" -, author = "{The GUDHI Project}" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, year = 2019 -, url = "https://gudhi.inria.fr/doc/3.0.0/" -} - -@incollection{gudhi:FilteredComplexes -, author = "Cl\'ement Maria" -, title = "Filtered Complexes" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group__simplex__tree.html" -, year = 2019 -} - -@incollection{gudhi:PersistentCohomology -, author = "Cl\'ement Maria" -, title = "Persistent Cohomology" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group__persistent__cohomology.html" -, year = 2019 -} - -@incollection{gudhi:Contraction -, author = "David Salinas" -, title = "Contraction" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group__contr.html" -, year = 2019 -} - -@incollection{gudhi:SkeletonBlocker -, author = "David Salinas" -, title = "Skeleton-Blocker" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group__skbl.html" -, year = 2019 -} - -@incollection{gudhi:AlphaComplex -, author = "Vincent Rouvreau" -, title = "Alpha complex" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group__alpha__complex.html" -, year = 2019 -} - -@incollection{gudhi:CubicalComplex -, author = "Pawel Dlotko" -, title = "Cubical complex" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group__cubical__complex.html" -, year = 2019 -} - -@incollection{gudhi:WitnessComplex -, author = "Siargey Kachanovich" -, title = "Witness complex" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group__witness__complex.html" -, year = 2019 -} - -@incollection{gudhi:SubSampling -, author = "Cl\'ement Jamin, Siargey Kachanovich" -, title = "Subsampling" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group__subsampling.html" -, year = 2019 -} - -@incollection{gudhi:SpatialSearching -, author = "Cl\'ement Jamin" -, title = "Spatial searching" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group__spatial__searching.html" -, year = 2019 -} - -@incollection{gudhi:TangentialComplex -, author = "Cl\'ement Jamin" -, title = "Tangential complex" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group__tangential__complex.html" -, year = 2019 -} - -@incollection{gudhi:RipsComplex -, author = "Cl\'ement Maria, Pawel Dlotko, Vincent Rouvreau, Marc Glisse" -, title = "Rips complex" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group__rips__complex.html" -, year = 2019 -} - -@incollection{gudhi:BottleneckDistance -, author = "Fran{{\c{c}}ois Godi" -, title = "Bottleneck distance" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group__bottleneck__distance.html" -, year = 2019 -} - -@incollection{gudhi:cython -, author = "Vincent Rouvreau" -, title = "Cython interface" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/python/3.0.0/" -, year = 2019 -} - -@incollection{gudhi:CoverComplex -, author = "Mathieu Carri\`ere" -, title = "Cover complex" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group__cover__complex.html" -, year = 2019 -} - -@incollection{gudhi:PersistenceRepresentations -, author = "Pawel Dlotko" -, title = "Persistence representations" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group___persistence__representations.html" -, year = 2019 -} diff --git a/biblio/how_to_cite_gudhi.bib.in b/biblio/how_to_cite_gudhi.bib.in new file mode 100644 index 00000000..23a37956 --- /dev/null +++ b/biblio/how_to_cite_gudhi.bib.in @@ -0,0 +1,158 @@ +@book{gudhi:urm +, title = "{GUDHI} User and Reference Manual" +, author = "{The GUDHI Project}" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, year = 2019 +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/" +} + +@incollection{gudhi:FilteredComplexes +, author = "Cl\'ement Maria" +, title = "Filtered Complexes" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__simplex__tree.html" +, year = 2019 +} + +@incollection{gudhi:PersistentCohomology +, author = "Cl\'ement Maria" +, title = "Persistent Cohomology" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__persistent__cohomology.html" +, year = 2019 +} + +@incollection{gudhi:Contraction +, author = "David Salinas" +, title = "Contraction" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__contr.html" +, year = 2019 +} + +@incollection{gudhi:SkeletonBlocker +, author = "David Salinas" +, title = "Skeleton-Blocker" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__skbl.html" +, year = 2019 +} + +@incollection{gudhi:AlphaComplex +, author = "Vincent Rouvreau" +, title = "Alpha complex" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__alpha__complex.html" +, year = 2019 +} + +@incollection{gudhi:CubicalComplex +, author = "Pawel Dlotko" +, title = "Cubical complex" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__cubical__complex.html" +, year = 2019 +} + +@incollection{gudhi:WitnessComplex +, author = "Siargey Kachanovich" +, title = "Witness complex" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__witness__complex.html" +, year = 2019 +} + +@incollection{gudhi:SubSampling +, author = "Cl\'ement Jamin, Siargey Kachanovich" +, title = "Subsampling" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__subsampling.html" +, year = 2019 +} + +@incollection{gudhi:SpatialSearching +, author = "Cl\'ement Jamin" +, title = "Spatial searching" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__spatial__searching.html" +, year = 2019 +} + +@incollection{gudhi:TangentialComplex +, author = "Cl\'ement Jamin" +, title = "Tangential complex" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__tangential__complex.html" +, year = 2019 +} + +@incollection{gudhi:RipsComplex +, author = "Cl\'ement Maria, Pawel Dlotko, Vincent Rouvreau, Marc Glisse" +, title = "Rips complex" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__rips__complex.html" +, year = 2019 +} + +@incollection{gudhi:BottleneckDistance +, author = "Fran{{\c{c}}ois Godi" +, title = "Bottleneck distance" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__bottleneck__distance.html" +, year = 2019 +} + +@incollection{gudhi:cython +, author = "Vincent Rouvreau" +, title = "Cython interface" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/python/@GUDHI_VERSION@/" +, year = 2019 +} + +@incollection{gudhi:CoverComplex +, author = "Mathieu Carri\`ere" +, title = "Cover complex" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__cover__complex.html" +, year = 2019 +} + +@incollection{gudhi:PersistenceRepresentations +, author = "Pawel Dlotko" +, title = "Persistence representations" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group___persistence__representations.html" +, year = 2019 +} diff --git a/src/cmake/modules/GUDHI_user_version_target.cmake b/src/cmake/modules/GUDHI_user_version_target.cmake index f75fb19e..c179220c 100644 --- a/src/cmake/modules/GUDHI_user_version_target.cmake +++ b/src/cmake/modules/GUDHI_user_version_target.cmake @@ -18,12 +18,19 @@ foreach(GUDHI_MODULE ${GUDHI_MODULES_FULL_LIST}) set(GUDHI_DOXYGEN_IMAGE_PATH "${GUDHI_DOXYGEN_IMAGE_PATH} doc/${GUDHI_MODULE}/ \\ \n") endforeach(GUDHI_MODULE ${GUDHI_MODULES_FULL_LIST}) -# Generate setup.py file to cythonize Gudhi - This file must be named setup.py by convention +# Generate Doxyfile for Doxygen - cf. root CMakeLists.txt for explanation configure_file(${CMAKE_SOURCE_DIR}/src/Doxyfile.in "${CMAKE_CURRENT_BINARY_DIR}/src/Doxyfile" @ONLY) - add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/src/Doxyfile ${GUDHI_USER_VERSION_DIR}/Doxyfile) +# Generate bib files for Doxygen - cf. root CMakeLists.txt for explanation +configure_file(${CMAKE_SOURCE_DIR}/biblio/how_to_cite_gudhi.bib.in "${CMAKE_CURRENT_BINARY_DIR}/biblio/how_to_cite_gudhi.bib" @ONLY) +file(COPY "${CMAKE_SOURCE_DIR}/biblio/how_to_cite_cgal.bib" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/biblio/") +file(COPY "${CMAKE_SOURCE_DIR}/biblio/bibliography.bib" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/biblio/") +# Copy biblio directory for user version +add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E + copy_directory ${CMAKE_CURRENT_BINARY_DIR}/biblio ${GUDHI_USER_VERSION_DIR}/biblio) + add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/Conventions.txt ${GUDHI_USER_VERSION_DIR}/Conventions.txt) add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E @@ -39,8 +46,6 @@ add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/CMakeGUDHIVersion.txt ${GUDHI_USER_VERSION_DIR}/CMakeGUDHIVersion.txt) -add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E - copy_directory ${CMAKE_SOURCE_DIR}/biblio ${GUDHI_USER_VERSION_DIR}/biblio) add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/${GUDHI_PYTHON_PATH} ${GUDHI_USER_VERSION_DIR}/python) add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E -- cgit v1.2.3 From 5b01dbc9d59e1e3e5e00d247c6cdeec406a398b5 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 2 Dec 2019 17:53:28 +0100 Subject: Some bad cgal referenced bibliography as it was updated --- src/python/doc/alpha_complex_user.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/python/doc/alpha_complex_user.rst b/src/python/doc/alpha_complex_user.rst index f9662a6d..dbe19286 100644 --- a/src/python/doc/alpha_complex_user.rst +++ b/src/python/doc/alpha_complex_user.rst @@ -11,8 +11,8 @@ Definition Alpha_complex is constructing a :doc:`Simplex_tree ` using `Delaunay Triangulation `_ -:cite:`cgal:hdj-t-15b` from `CGAL `_ (the Computational Geometry Algorithms Library -:cite:`cgal:eb-15b`). +:cite:`cgal:hdj-t-19b` from `CGAL `_ (the Computational Geometry Algorithms Library +:cite:`cgal:eb-19b`). Remarks ^^^^^^^ -- cgit v1.2.3 From 518de123bcf19a85accca5660844355da1106511 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 2 Dec 2019 23:32:33 +0100 Subject: Code review : year is also generated --- biblio/how_to_cite_gudhi.bib.in | 32 +++++++++++------------ src/cmake/modules/GUDHI_user_version_target.cmake | 1 + 2 files changed, 17 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/biblio/how_to_cite_gudhi.bib.in b/biblio/how_to_cite_gudhi.bib.in index 23a37956..05d3cc98 100644 --- a/biblio/how_to_cite_gudhi.bib.in +++ b/biblio/how_to_cite_gudhi.bib.in @@ -3,7 +3,7 @@ , author = "{The GUDHI Project}" , publisher = "{GUDHI Editorial Board}" , edition = "{@GUDHI_VERSION@}" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/" } @@ -14,7 +14,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__simplex__tree.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:PersistentCohomology @@ -24,7 +24,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__persistent__cohomology.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:Contraction @@ -34,7 +34,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__contr.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:SkeletonBlocker @@ -44,7 +44,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__skbl.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:AlphaComplex @@ -54,7 +54,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__alpha__complex.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:CubicalComplex @@ -64,7 +64,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__cubical__complex.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:WitnessComplex @@ -74,7 +74,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__witness__complex.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:SubSampling @@ -84,7 +84,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__subsampling.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:SpatialSearching @@ -94,7 +94,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__spatial__searching.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:TangentialComplex @@ -104,7 +104,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__tangential__complex.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:RipsComplex @@ -114,7 +114,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__rips__complex.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:BottleneckDistance @@ -124,7 +124,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__bottleneck__distance.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:cython @@ -134,7 +134,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/python/@GUDHI_VERSION@/" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:CoverComplex @@ -144,7 +144,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__cover__complex.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:PersistenceRepresentations @@ -154,5 +154,5 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group___persistence__representations.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } diff --git a/src/cmake/modules/GUDHI_user_version_target.cmake b/src/cmake/modules/GUDHI_user_version_target.cmake index c179220c..4fa74330 100644 --- a/src/cmake/modules/GUDHI_user_version_target.cmake +++ b/src/cmake/modules/GUDHI_user_version_target.cmake @@ -24,6 +24,7 @@ add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/src/Doxyfile ${GUDHI_USER_VERSION_DIR}/Doxyfile) # Generate bib files for Doxygen - cf. root CMakeLists.txt for explanation +string(TIMESTAMP GUDHI_VERSION_YEAR "%Y") configure_file(${CMAKE_SOURCE_DIR}/biblio/how_to_cite_gudhi.bib.in "${CMAKE_CURRENT_BINARY_DIR}/biblio/how_to_cite_gudhi.bib" @ONLY) file(COPY "${CMAKE_SOURCE_DIR}/biblio/how_to_cite_cgal.bib" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/biblio/") file(COPY "${CMAKE_SOURCE_DIR}/biblio/bibliography.bib" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/biblio/") -- cgit v1.2.3 From 17cf8da254d88fa42cbe9e7bb486147def47c26b Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 6 Dec 2019 15:27:15 +0100 Subject: Modify boost tests to be more verbose and errors in colour. I kept coverage tests (was only used by Jenkins) and they still can be activated through an option --- src/Alpha_complex/test/CMakeLists.txt | 22 +++++++-------- src/Bitmap_cubical_complex/test/CMakeLists.txt | 5 ++-- src/Bottleneck_distance/test/CMakeLists.txt | 5 ++-- src/Cech_complex/test/CMakeLists.txt | 5 ++-- src/Nerve_GIC/test/CMakeLists.txt | 5 ++-- .../test/CMakeLists.txt | 33 ++++++---------------- src/Persistent_cohomology/test/CMakeLists.txt | 13 ++++----- src/Rips_complex/test/CMakeLists.txt | 5 ++-- src/Simplex_tree/test/CMakeLists.txt | 18 ++++-------- src/Skeleton_blocker/test/CMakeLists.txt | 11 +++----- src/Spatial_searching/test/CMakeLists.txt | 9 ++---- src/Subsampling/test/CMakeLists.txt | 14 ++++----- src/Tangential_complex/test/CMakeLists.txt | 6 ++-- src/Toplex_map/test/CMakeLists.txt | 8 ++---- src/Witness_complex/test/CMakeLists.txt | 8 ++---- src/cmake/modules/GUDHI_boost_test.cmake | 26 +++++++++++++++++ src/cmake/modules/GUDHI_compilation_flags.cmake | 2 ++ src/cmake/modules/GUDHI_test_coverage.cmake | 26 ----------------- src/common/test/CMakeLists.txt | 13 +++------ 19 files changed, 95 insertions(+), 139 deletions(-) create mode 100644 src/cmake/modules/GUDHI_boost_test.cmake delete mode 100644 src/cmake/modules/GUDHI_test_coverage.cmake (limited to 'src') diff --git a/src/Alpha_complex/test/CMakeLists.txt b/src/Alpha_complex/test/CMakeLists.txt index ad5b6314..0476c6d4 100644 --- a/src/Alpha_complex/test/CMakeLists.txt +++ b/src/Alpha_complex/test/CMakeLists.txt @@ -1,27 +1,27 @@ project(Alpha_complex_tests) -include(GUDHI_test_coverage) +include(GUDHI_boost_test) if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) # Do not forget to copy test files in current binary dir file(COPY "${CMAKE_SOURCE_DIR}/data/points/alphacomplexdoc.off" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) add_executable ( Alpha_complex_test_unit Alpha_complex_unit_test.cpp ) - target_link_libraries(Alpha_complex_test_unit ${CGAL_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) + target_link_libraries(Alpha_complex_test_unit ${CGAL_LIBRARY}) if (TBB_FOUND) target_link_libraries(Alpha_complex_test_unit ${TBB_LIBRARIES}) endif() - gudhi_add_coverage_test(Alpha_complex_test_unit) + gudhi_add_boost_test(Alpha_complex_test_unit) add_executable ( Alpha_complex_3d_test_unit Alpha_complex_3d_unit_test.cpp ) - target_link_libraries(Alpha_complex_3d_test_unit ${CGAL_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) + target_link_libraries(Alpha_complex_3d_test_unit ${CGAL_LIBRARY}) add_executable ( Weighted_alpha_complex_3d_test_unit Weighted_alpha_complex_3d_unit_test.cpp ) - target_link_libraries(Weighted_alpha_complex_3d_test_unit ${CGAL_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) + target_link_libraries(Weighted_alpha_complex_3d_test_unit ${CGAL_LIBRARY}) add_executable ( Periodic_alpha_complex_3d_test_unit Periodic_alpha_complex_3d_unit_test.cpp ) - target_link_libraries(Periodic_alpha_complex_3d_test_unit ${CGAL_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) + target_link_libraries(Periodic_alpha_complex_3d_test_unit ${CGAL_LIBRARY}) add_executable ( Weighted_periodic_alpha_complex_3d_test_unit Weighted_periodic_alpha_complex_3d_unit_test.cpp ) - target_link_libraries(Weighted_periodic_alpha_complex_3d_test_unit ${CGAL_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) + target_link_libraries(Weighted_periodic_alpha_complex_3d_test_unit ${CGAL_LIBRARY}) if (TBB_FOUND) target_link_libraries(Alpha_complex_3d_test_unit ${TBB_LIBRARIES}) target_link_libraries(Weighted_alpha_complex_3d_test_unit ${TBB_LIBRARIES}) @@ -29,9 +29,9 @@ if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) target_link_libraries(Weighted_periodic_alpha_complex_3d_test_unit ${TBB_LIBRARIES}) endif() - gudhi_add_coverage_test(Alpha_complex_3d_test_unit) - gudhi_add_coverage_test(Weighted_alpha_complex_3d_test_unit) - gudhi_add_coverage_test(Periodic_alpha_complex_3d_test_unit) - gudhi_add_coverage_test(Weighted_periodic_alpha_complex_3d_test_unit) + gudhi_add_boost_test(Alpha_complex_3d_test_unit) + gudhi_add_boost_test(Weighted_alpha_complex_3d_test_unit) + gudhi_add_boost_test(Periodic_alpha_complex_3d_test_unit) + gudhi_add_boost_test(Weighted_periodic_alpha_complex_3d_test_unit) endif (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) diff --git a/src/Bitmap_cubical_complex/test/CMakeLists.txt b/src/Bitmap_cubical_complex/test/CMakeLists.txt index d2f002a6..eb7eb6b5 100644 --- a/src/Bitmap_cubical_complex/test/CMakeLists.txt +++ b/src/Bitmap_cubical_complex/test/CMakeLists.txt @@ -1,14 +1,13 @@ project(Bitmap_cubical_complex_tests) -include(GUDHI_test_coverage) +include(GUDHI_boost_test) # Do not forget to copy test files in current binary dir file(COPY "${CMAKE_SOURCE_DIR}/data/bitmap/sinusoid.txt" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) add_executable ( Bitmap_cubical_complex_test_unit Bitmap_test.cpp ) -target_link_libraries(Bitmap_cubical_complex_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) if (TBB_FOUND) target_link_libraries(Bitmap_cubical_complex_test_unit ${TBB_LIBRARIES}) endif() -gudhi_add_coverage_test(Bitmap_cubical_complex_test_unit) +gudhi_add_boost_test(Bitmap_cubical_complex_test_unit) diff --git a/src/Bottleneck_distance/test/CMakeLists.txt b/src/Bottleneck_distance/test/CMakeLists.txt index ec2d045f..3acd3d86 100644 --- a/src/Bottleneck_distance/test/CMakeLists.txt +++ b/src/Bottleneck_distance/test/CMakeLists.txt @@ -1,14 +1,13 @@ project(Bottleneck_distance_tests) if (NOT CGAL_VERSION VERSION_LESS 4.11.0) - include(GUDHI_test_coverage) + include(GUDHI_boost_test) add_executable ( Bottleneck_distance_test_unit bottleneck_unit_test.cpp ) - target_link_libraries(Bottleneck_distance_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) if (TBB_FOUND) target_link_libraries(Bottleneck_distance_test_unit ${TBB_LIBRARIES}) endif(TBB_FOUND) - gudhi_add_coverage_test(Bottleneck_distance_test_unit) + gudhi_add_boost_test(Bottleneck_distance_test_unit) endif (NOT CGAL_VERSION VERSION_LESS 4.11.0) diff --git a/src/Cech_complex/test/CMakeLists.txt b/src/Cech_complex/test/CMakeLists.txt index 8db51173..db510af3 100644 --- a/src/Cech_complex/test/CMakeLists.txt +++ b/src/Cech_complex/test/CMakeLists.txt @@ -1,10 +1,9 @@ cmake_minimum_required(VERSION 2.6) project(Cech_complex_tests) -include(GUDHI_test_coverage) +include(GUDHI_boost_test) add_executable ( Cech_complex_test_unit test_cech_complex.cpp ) -target_link_libraries(Cech_complex_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) if (TBB_FOUND) target_link_libraries(Cech_complex_test_unit ${TBB_LIBRARIES}) endif() @@ -12,4 +11,4 @@ endif() # Do not forget to copy test files in current binary dir file(COPY "${CMAKE_SOURCE_DIR}/data/points/alphacomplexdoc.off" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) -gudhi_add_coverage_test(Cech_complex_test_unit) +gudhi_add_boost_test(Cech_complex_test_unit) diff --git a/src/Nerve_GIC/test/CMakeLists.txt b/src/Nerve_GIC/test/CMakeLists.txt index b89c18a2..567bf43f 100644 --- a/src/Nerve_GIC/test/CMakeLists.txt +++ b/src/Nerve_GIC/test/CMakeLists.txt @@ -1,16 +1,15 @@ project(Graph_induced_complex_tests) if (NOT CGAL_VERSION VERSION_LESS 4.11.0) - include(GUDHI_test_coverage) + include(GUDHI_boost_test) add_executable ( Nerve_GIC_test_unit test_GIC.cpp ) - target_link_libraries(Nerve_GIC_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) if (TBB_FOUND) target_link_libraries(Nerve_GIC_test_unit ${TBB_LIBRARIES}) endif() file(COPY data DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) - gudhi_add_coverage_test(Nerve_GIC_test_unit) + gudhi_add_boost_test(Nerve_GIC_test_unit) endif (NOT CGAL_VERSION VERSION_LESS 4.11.0) diff --git a/src/Persistence_representations/test/CMakeLists.txt b/src/Persistence_representations/test/CMakeLists.txt index a95880c9..92d68a63 100644 --- a/src/Persistence_representations/test/CMakeLists.txt +++ b/src/Persistence_representations/test/CMakeLists.txt @@ -1,51 +1,36 @@ project(Persistence_representations_test) -include(GUDHI_test_coverage) +include(GUDHI_boost_test) # copy data directory for tests purpose. file(COPY data DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) add_executable ( Persistence_intervals_test_unit persistence_intervals_test.cpp ) -target_link_libraries(Persistence_intervals_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) - -gudhi_add_coverage_test(Persistence_intervals_test_unit) +gudhi_add_boost_test(Persistence_intervals_test_unit) add_executable (Vector_representation_test_unit vector_representation_test.cpp ) -target_link_libraries(Vector_representation_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) - -gudhi_add_coverage_test(Vector_representation_test_unit) +gudhi_add_boost_test(Vector_representation_test_unit) add_executable (Persistence_lanscapes_test_unit persistence_lanscapes_test.cpp ) -target_link_libraries(Persistence_lanscapes_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) - -gudhi_add_coverage_test(Persistence_lanscapes_test_unit) +gudhi_add_boost_test(Persistence_lanscapes_test_unit) add_executable ( Persistence_lanscapes_on_grid_test_unit persistence_lanscapes_on_grid_test.cpp ) -target_link_libraries(Persistence_lanscapes_on_grid_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) - -gudhi_add_coverage_test(Persistence_lanscapes_on_grid_test_unit) +gudhi_add_boost_test(Persistence_lanscapes_on_grid_test_unit) add_executable (Persistence_heat_maps_test_unit persistence_heat_maps_test.cpp ) -target_link_libraries(Persistence_heat_maps_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) - -gudhi_add_coverage_test(Persistence_heat_maps_test_unit) +gudhi_add_boost_test(Persistence_heat_maps_test_unit) add_executable ( Read_persistence_from_file_test_unit read_persistence_from_file_test.cpp ) -target_link_libraries(Read_persistence_from_file_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) - -gudhi_add_coverage_test(Read_persistence_from_file_test_unit) +gudhi_add_boost_test(Read_persistence_from_file_test_unit) add_executable ( kernels_unit kernels.cpp ) -target_link_libraries(kernels_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) - -gudhi_add_coverage_test(kernels_unit) +gudhi_add_boost_test(kernels_unit) if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) add_executable (Persistence_intervals_with_distances_test_unit persistence_intervals_with_distances_test.cpp ) - target_link_libraries(Persistence_intervals_with_distances_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) if (TBB_FOUND) target_link_libraries(Persistence_intervals_with_distances_test_unit ${TBB_LIBRARIES}) endif(TBB_FOUND) - gudhi_add_coverage_test(Persistence_intervals_with_distances_test_unit) + gudhi_add_boost_test(Persistence_intervals_with_distances_test_unit) endif (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) diff --git a/src/Persistent_cohomology/test/CMakeLists.txt b/src/Persistent_cohomology/test/CMakeLists.txt index f8baf861..64669c4e 100644 --- a/src/Persistent_cohomology/test/CMakeLists.txt +++ b/src/Persistent_cohomology/test/CMakeLists.txt @@ -1,11 +1,9 @@ project(Persistent_cohomology_tests) -include(GUDHI_test_coverage) +include(GUDHI_boost_test) add_executable ( Persistent_cohomology_test_unit persistent_cohomology_unit_test.cpp ) -target_link_libraries(Persistent_cohomology_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) add_executable ( Persistent_cohomology_test_betti_numbers betti_numbers_unit_test.cpp ) -target_link_libraries(Persistent_cohomology_test_betti_numbers ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) if (TBB_FOUND) target_link_libraries(Persistent_cohomology_test_unit ${TBB_LIBRARIES}) target_link_libraries(Persistent_cohomology_test_betti_numbers ${TBB_LIBRARIES}) @@ -16,13 +14,12 @@ file(COPY "${CMAKE_SOURCE_DIR}/src/Persistent_cohomology/test/simplex_tree_file_ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) # Unitary tests -gudhi_add_coverage_test(Persistent_cohomology_test_unit) -gudhi_add_coverage_test(Persistent_cohomology_test_betti_numbers) +gudhi_add_boost_test(Persistent_cohomology_test_unit) +gudhi_add_boost_test(Persistent_cohomology_test_betti_numbers) if(GMPXX_FOUND AND GMP_FOUND) add_executable ( Persistent_cohomology_test_unit_multi_field persistent_cohomology_unit_test_multi_field.cpp ) - target_link_libraries(Persistent_cohomology_test_unit_multi_field - ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} ${GMPXX_LIBRARIES} ${GMP_LIBRARIES}) + target_link_libraries(Persistent_cohomology_test_unit_multi_field ${GMPXX_LIBRARIES} ${GMP_LIBRARIES}) if (TBB_FOUND) target_link_libraries(Persistent_cohomology_test_unit_multi_field ${TBB_LIBRARIES}) endif(TBB_FOUND) @@ -31,7 +28,7 @@ if(GMPXX_FOUND AND GMP_FOUND) file(COPY "${CMAKE_SOURCE_DIR}/src/Persistent_cohomology/test/simplex_tree_file_for_multi_field_unit_test.txt" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) # Unitary tests - gudhi_add_coverage_test(Persistent_cohomology_test_unit_multi_field) + gudhi_add_boost_test(Persistent_cohomology_test_unit_multi_field) endif(GMPXX_FOUND AND GMP_FOUND) diff --git a/src/Rips_complex/test/CMakeLists.txt b/src/Rips_complex/test/CMakeLists.txt index 745d953c..b359584e 100644 --- a/src/Rips_complex/test/CMakeLists.txt +++ b/src/Rips_complex/test/CMakeLists.txt @@ -1,9 +1,8 @@ project(Rips_complex_tests) -include(GUDHI_test_coverage) +include(GUDHI_boost_test) add_executable ( Rips_complex_test_unit test_rips_complex.cpp ) -target_link_libraries(Rips_complex_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) if (TBB_FOUND) target_link_libraries(Rips_complex_test_unit ${TBB_LIBRARIES}) endif() @@ -12,4 +11,4 @@ endif() file(COPY "${CMAKE_SOURCE_DIR}/data/points/alphacomplexdoc.off" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) file(COPY "${CMAKE_SOURCE_DIR}/data/distance_matrix/full_square_distance_matrix.csv" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) -gudhi_add_coverage_test(Rips_complex_test_unit) +gudhi_add_boost_test(Rips_complex_test_unit) diff --git a/src/Simplex_tree/test/CMakeLists.txt b/src/Simplex_tree/test/CMakeLists.txt index 5bea3938..8b9163f5 100644 --- a/src/Simplex_tree/test/CMakeLists.txt +++ b/src/Simplex_tree/test/CMakeLists.txt @@ -1,38 +1,30 @@ project(Simplex_tree_tests) -include(GUDHI_test_coverage) +include(GUDHI_boost_test) # Do not forget to copy test files in current binary dir file(COPY "simplex_tree_for_unit_test.txt" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) add_executable ( Simplex_tree_test_unit simplex_tree_unit_test.cpp ) -target_link_libraries(Simplex_tree_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) if (TBB_FOUND) target_link_libraries(Simplex_tree_test_unit ${TBB_LIBRARIES}) endif() - -gudhi_add_coverage_test(Simplex_tree_test_unit) +gudhi_add_boost_test(Simplex_tree_test_unit) add_executable ( Simplex_tree_remove_test_unit simplex_tree_remove_unit_test.cpp ) -target_link_libraries(Simplex_tree_remove_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) if (TBB_FOUND) target_link_libraries(Simplex_tree_remove_test_unit ${TBB_LIBRARIES}) endif() - -gudhi_add_coverage_test(Simplex_tree_remove_test_unit) +gudhi_add_boost_test(Simplex_tree_remove_test_unit) add_executable ( Simplex_tree_iostream_operator_test_unit simplex_tree_iostream_operator_unit_test.cpp ) -target_link_libraries(Simplex_tree_iostream_operator_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) if (TBB_FOUND) target_link_libraries(Simplex_tree_iostream_operator_test_unit ${TBB_LIBRARIES}) endif() - -gudhi_add_coverage_test(Simplex_tree_iostream_operator_test_unit) +gudhi_add_boost_test(Simplex_tree_iostream_operator_test_unit) add_executable ( Simplex_tree_ctor_and_move_test_unit simplex_tree_ctor_and_move_unit_test.cpp ) -target_link_libraries(Simplex_tree_ctor_and_move_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) if (TBB_FOUND) target_link_libraries(Simplex_tree_ctor_and_move_test_unit ${TBB_LIBRARIES}) endif() - -gudhi_add_coverage_test(Simplex_tree_ctor_and_move_test_unit) +gudhi_add_boost_test(Simplex_tree_ctor_and_move_test_unit) diff --git a/src/Skeleton_blocker/test/CMakeLists.txt b/src/Skeleton_blocker/test/CMakeLists.txt index 19c65871..24b6c11e 100644 --- a/src/Skeleton_blocker/test/CMakeLists.txt +++ b/src/Skeleton_blocker/test/CMakeLists.txt @@ -1,17 +1,14 @@ project(Skeleton_blocker_tests) -include(GUDHI_test_coverage) +include(GUDHI_boost_test) add_executable ( Skeleton_blocker_test_unit test_skeleton_blocker_complex.cpp ) -target_link_libraries(Skeleton_blocker_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) add_executable ( Skeleton_blocker_test_geometric_complex test_skeleton_blocker_geometric_complex.cpp ) -target_link_libraries(Skeleton_blocker_test_geometric_complex ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) add_executable ( Skeleton_blocker_test_simplifiable test_skeleton_blocker_simplifiable.cpp ) -target_link_libraries(Skeleton_blocker_test_simplifiable ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) # Do not forget to copy test files in current binary dir file(COPY "test2.off" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) -gudhi_add_coverage_test(Skeleton_blocker_test_unit) -gudhi_add_coverage_test(Skeleton_blocker_test_geometric_complex) -gudhi_add_coverage_test(Skeleton_blocker_test_simplifiable) +gudhi_add_boost_test(Skeleton_blocker_test_unit) +gudhi_add_boost_test(Skeleton_blocker_test_geometric_complex) +gudhi_add_boost_test(Skeleton_blocker_test_simplifiable) diff --git a/src/Spatial_searching/test/CMakeLists.txt b/src/Spatial_searching/test/CMakeLists.txt index 18f7c6b8..a6c23951 100644 --- a/src/Spatial_searching/test/CMakeLists.txt +++ b/src/Spatial_searching/test/CMakeLists.txt @@ -1,11 +1,8 @@ project(Spatial_searching_tests) if(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) - include(GUDHI_test_coverage) - + include(GUDHI_boost_test) add_executable( Spatial_searching_test_Kd_tree_search test_Kd_tree_search.cpp ) - target_link_libraries(Spatial_searching_test_Kd_tree_search - ${CGAL_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) - - gudhi_add_coverage_test(Spatial_searching_test_Kd_tree_search) + target_link_libraries(Spatial_searching_test_Kd_tree_search ${CGAL_LIBRARY}) + gudhi_add_boost_test(Spatial_searching_test_Kd_tree_search) endif () diff --git a/src/Subsampling/test/CMakeLists.txt b/src/Subsampling/test/CMakeLists.txt index cf54788e..354021c1 100644 --- a/src/Subsampling/test/CMakeLists.txt +++ b/src/Subsampling/test/CMakeLists.txt @@ -1,18 +1,18 @@ project(Subsampling_tests) if(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) - include(GUDHI_test_coverage) + include(GUDHI_boost_test) add_executable( Subsampling_test_pick_n_random_points test_pick_n_random_points.cpp ) - target_link_libraries(Subsampling_test_pick_n_random_points ${CGAL_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) + target_link_libraries(Subsampling_test_pick_n_random_points ${CGAL_LIBRARY}) add_executable( Subsampling_test_choose_n_farthest_points test_choose_n_farthest_points.cpp ) - target_link_libraries(Subsampling_test_choose_n_farthest_points ${CGAL_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) + target_link_libraries(Subsampling_test_choose_n_farthest_points ${CGAL_LIBRARY}) add_executable(Subsampling_test_sparsify_point_set test_sparsify_point_set.cpp) - target_link_libraries(Subsampling_test_sparsify_point_set ${CGAL_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) + target_link_libraries(Subsampling_test_sparsify_point_set ${CGAL_LIBRARY}) - gudhi_add_coverage_test(Subsampling_test_pick_n_random_points) - gudhi_add_coverage_test(Subsampling_test_choose_n_farthest_points) - gudhi_add_coverage_test(Subsampling_test_sparsify_point_set) + gudhi_add_boost_test(Subsampling_test_pick_n_random_points) + gudhi_add_boost_test(Subsampling_test_choose_n_farthest_points) + gudhi_add_boost_test(Subsampling_test_sparsify_point_set) endif(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) diff --git a/src/Tangential_complex/test/CMakeLists.txt b/src/Tangential_complex/test/CMakeLists.txt index ae17a286..2207d67c 100644 --- a/src/Tangential_complex/test/CMakeLists.txt +++ b/src/Tangential_complex/test/CMakeLists.txt @@ -1,13 +1,13 @@ project(Tangential_complex_tests) if(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) - include(GUDHI_test_coverage) + include(GUDHI_boost_test) add_executable( Tangential_complex_test_TC test_tangential_complex.cpp ) - target_link_libraries(Tangential_complex_test_TC ${CGAL_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) + target_link_libraries(Tangential_complex_test_TC ${CGAL_LIBRARY}) if (TBB_FOUND) target_link_libraries(Tangential_complex_test_TC ${TBB_LIBRARIES}) endif() - gudhi_add_coverage_test(Tangential_complex_test_TC) + gudhi_add_boost_test(Tangential_complex_test_TC) endif(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) diff --git a/src/Toplex_map/test/CMakeLists.txt b/src/Toplex_map/test/CMakeLists.txt index 59517db5..2997584d 100644 --- a/src/Toplex_map/test/CMakeLists.txt +++ b/src/Toplex_map/test/CMakeLists.txt @@ -1,11 +1,9 @@ project(Toplex_map_tests) -include(GUDHI_test_coverage) +include(GUDHI_boost_test) add_executable( Toplex_map_unit_test toplex_map_unit_test.cpp ) -target_link_libraries(Toplex_map_unit_test ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) -gudhi_add_coverage_test(Toplex_map_unit_test) +gudhi_add_boost_test(Toplex_map_unit_test) add_executable( Lazy_toplex_map_unit_test lazy_toplex_map_unit_test.cpp ) -target_link_libraries(Lazy_toplex_map_unit_test ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) -gudhi_add_coverage_test(Lazy_toplex_map_unit_test) +gudhi_add_boost_test(Lazy_toplex_map_unit_test) diff --git a/src/Witness_complex/test/CMakeLists.txt b/src/Witness_complex/test/CMakeLists.txt index 96188e46..690933aa 100644 --- a/src/Witness_complex/test/CMakeLists.txt +++ b/src/Witness_complex/test/CMakeLists.txt @@ -1,22 +1,20 @@ project(Witness_complex_tests) -include(GUDHI_test_coverage) +include(GUDHI_boost_test) add_executable ( Witness_complex_test_simple_witness_complex test_simple_witness_complex.cpp ) -target_link_libraries(Witness_complex_test_simple_witness_complex ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) if (TBB_FOUND) target_link_libraries(Witness_complex_test_simple_witness_complex ${TBB_LIBRARIES}) endif(TBB_FOUND) -gudhi_add_coverage_test(Witness_complex_test_simple_witness_complex) +gudhi_add_boost_test(Witness_complex_test_simple_witness_complex) # CGAL and Eigen3 are required for Euclidean version of Witness if(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) add_executable ( Witness_complex_test_euclidean_simple_witness_complex test_euclidean_simple_witness_complex.cpp ) - target_link_libraries(Witness_complex_test_euclidean_simple_witness_complex ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) if (TBB_FOUND) target_link_libraries(Witness_complex_test_euclidean_simple_witness_complex ${TBB_LIBRARIES}) endif(TBB_FOUND) - gudhi_add_coverage_test(Witness_complex_test_euclidean_simple_witness_complex) + gudhi_add_boost_test(Witness_complex_test_euclidean_simple_witness_complex) endif(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) diff --git a/src/cmake/modules/GUDHI_boost_test.cmake b/src/cmake/modules/GUDHI_boost_test.cmake new file mode 100644 index 00000000..c3b29883 --- /dev/null +++ b/src/cmake/modules/GUDHI_boost_test.cmake @@ -0,0 +1,26 @@ +if (WITH_GUDHI_BOOST_TEST_COVERAGE) + # Make CTest output XML coverage report - WITH_GUDHI_BOOST_TEST_COVERAGE must be set - default is OFF + if (GCOVR_PATH) + # for gcovr to make coverage reports - Corbera Jenkins plugin + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") + endif() + if (GPROF_PATH) + # for gprof to make coverage reports - Jenkins + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg") + endif() + set(GUDHI_UT_LOG_FORMAT "--log_format=XML") + set(GUDHI_UT_LOG_SINK "--log_sink=${CMAKE_BINARY_DIR}/${unitary_test}_UT.xml") + set(GUDHI_UT_LOG_LEVEL "--log_level=test_suite") + set(GUDHI_UT_REPORT_LEVEL "--report_level=no") +else (WITH_GUDHI_BOOST_TEST_COVERAGE) + # Make CTest more verbose and color output + set(GUDHI_UT_LOG_LEVEL "--color_output") + set(GUDHI_UT_REPORT_LEVEL "--report_level=detailed") +endif(WITH_GUDHI_BOOST_TEST_COVERAGE) + +function(gudhi_add_boost_test unitary_test) + target_link_libraries(${unitary_test} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) + add_test(NAME ${unitary_test} COMMAND $ + ${GUDHI_UT_LOG_FORMAT} ${GUDHI_UT_LOG_SINK} + ${GUDHI_UT_LOG_LEVEL} ${GUDHI_UT_REPORT_LEVEL}) +endfunction() diff --git a/src/cmake/modules/GUDHI_compilation_flags.cmake b/src/cmake/modules/GUDHI_compilation_flags.cmake index 6cd2614d..34c2e065 100644 --- a/src/cmake/modules/GUDHI_compilation_flags.cmake +++ b/src/cmake/modules/GUDHI_compilation_flags.cmake @@ -73,3 +73,5 @@ if(CMAKE_BUILD_TYPE MATCHES Debug) else() message("++ Release compilation flags are: ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE}") endif() + +option(WITH_GUDHI_BOOST_TEST_COVERAGE "Report xml coverage files on boost tests" OFF) diff --git a/src/cmake/modules/GUDHI_test_coverage.cmake b/src/cmake/modules/GUDHI_test_coverage.cmake deleted file mode 100644 index bea5b2d6..00000000 --- a/src/cmake/modules/GUDHI_test_coverage.cmake +++ /dev/null @@ -1,26 +0,0 @@ - -if (GCOVR_PATH) - # for gcovr to make coverage reports - Corbera Jenkins plugin - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") -endif() -if (GPROF_PATH) - # for gprof to make coverage reports - Jenkins - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg") -endif() - -if (DEBUG_TRACES) - # Make CTest more verbose with DEBUG_TRACES - no XML output - set(GUDHI_UT_LOG_LEVEL "--log_level=all") - set(GUDHI_UT_REPORT_LEVEL "--report_level=detailed") -else() - set(GUDHI_UT_LOG_FORMAT "--log_format=XML") - set(GUDHI_UT_LOG_SINK "--log_sink=${CMAKE_BINARY_DIR}/${unitary_test}_UT.xml") - set(GUDHI_UT_LOG_LEVEL "--log_level=test_suite") - set(GUDHI_UT_REPORT_LEVEL "--report_level=no") -endif() - -function(gudhi_add_coverage_test unitary_test) - add_test(NAME ${unitary_test} COMMAND $ - ${GUDHI_UT_LOG_FORMAT} ${GUDHI_UT_LOG_SINK} - ${GUDHI_UT_LOG_LEVEL} ${GUDHI_UT_REPORT_LEVEL}) -endfunction() diff --git a/src/common/test/CMakeLists.txt b/src/common/test/CMakeLists.txt index 0b49fa1e..34de7398 100644 --- a/src/common/test/CMakeLists.txt +++ b/src/common/test/CMakeLists.txt @@ -1,15 +1,10 @@ project(Common_tests) -include(GUDHI_test_coverage) +include(GUDHI_boost_test) add_executable ( Common_test_points_off_reader test_points_off_reader.cpp ) -target_link_libraries(Common_test_points_off_reader ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) - add_executable ( Common_test_distance_matrix_reader test_distance_matrix_reader.cpp ) -target_link_libraries(Common_test_distance_matrix_reader ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) - add_executable ( Common_test_persistence_intervals_reader test_persistence_intervals_reader.cpp ) -target_link_libraries(Common_test_persistence_intervals_reader ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) # Do not forget to copy test files in current binary dir file(COPY "${CMAKE_SOURCE_DIR}/data/points/alphacomplexdoc.off" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) @@ -19,6 +14,6 @@ file(COPY "${CMAKE_SOURCE_DIR}/src/common/test/persistence_intervals_with_dimens file(COPY "${CMAKE_SOURCE_DIR}/src/common/test/persistence_intervals_with_field.pers" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) file(COPY "${CMAKE_SOURCE_DIR}/src/common/test/persistence_intervals_without_dimension.pers" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) -gudhi_add_coverage_test(Common_test_points_off_reader) -gudhi_add_coverage_test(Common_test_distance_matrix_reader) -gudhi_add_coverage_test(Common_test_persistence_intervals_reader) +gudhi_add_boost_test(Common_test_points_off_reader) +gudhi_add_boost_test(Common_test_distance_matrix_reader) +gudhi_add_boost_test(Common_test_persistence_intervals_reader) -- cgit v1.2.3 From ce58cc97866605fe64df479e96d455e90f56f8e2 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Sun, 8 Dec 2019 21:22:09 -0500 Subject: fixed useless coordinates in Landscape if min and max are computed from data --- src/python/doc/representations.rst | 25 ++++++++++++++++++++-- .../diagram_vectorizations_distances_kernels.py | 6 +++--- src/python/gudhi/representations/vector_methods.py | 12 ++++++++--- 3 files changed, 35 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/python/doc/representations.rst b/src/python/doc/representations.rst index b3131a25..b338f7f0 100644 --- a/src/python/doc/representations.rst +++ b/src/python/doc/representations.rst @@ -8,9 +8,9 @@ Representations manual .. include:: representations_sum.inc -This module, originally named sklearn_tda, aims at bridging the gap between persistence diagrams and machine learning tools, in particular scikit-learn. It provides tools, using the scikit-learn standard interface, to compute distances and kernels on diagrams, and to convert diagrams into vectors. +This module, originally available at https://github.com/MathieuCarriere/sklearn-tda and named sklearn_tda, aims at bridging the gap between persistence diagrams and machine learning, by providing implementations of most of the vector representations for persistence diagrams in the literature, in a scikit-learn format. More specifically, it provides tools, using the scikit-learn standard interface, to compute distances and kernels on persistence diagrams, and to convert these diagrams into vectors in Euclidean space. -A diagram is represented as a numpy array of shape (n,2), as can be obtained from :func:`~gudhi.SimplexTree.persistence_intervals_in_dimension` for instance. Points at infinity are represented as a numpy array of shape (n,1), storing only the birth time. +A diagram is represented as a numpy array of shape (n,2), as can be obtained from `SimplexTree.persistence_intervals_in_dimension` for instance. Points at infinity are represented as a numpy array of shape (n,1), storing only the birth time. A small example is provided @@ -46,3 +46,24 @@ Metrics :members: :special-members: :show-inheritance: + +Basic example +------------- + +This example computes the first two Landscapes associated to a persistence diagram with four points. The landscapes are evaluated on ten samples, leading to two vectors with ten coordinates each, that are eventually concatenated in order to produce a single vector representation. + +.. testcode:: + + import numpy as np + from gudhi.representations import Landscape + # A single diagram with 4 points + D = np.array([[0.,4.],[1.,2.],[3.,8.],[6.,8.]]) + diags = [D] + l=Landscape(num_landscapes=2,resolution=10).fit_transform(diags) + print(l) + +The output is: + +.. testoutput:: + + [[0. 1.25707872 2.51415744 1.88561808 0.7856742 2.04275292 3.29983165 2.51415744 1.25707872 0. 0. 0. 0.31426968 0. 0.62853936 0. 0. 0.31426968 1.25707872 0. ]] diff --git a/src/python/example/diagram_vectorizations_distances_kernels.py b/src/python/example/diagram_vectorizations_distances_kernels.py index 119072eb..f777984c 100755 --- a/src/python/example/diagram_vectorizations_distances_kernels.py +++ b/src/python/example/diagram_vectorizations_distances_kernels.py @@ -26,9 +26,9 @@ plt.show() LS = Landscape(resolution=1000) L = LS.fit_transform(diags) -plt.plot(L[0][:1000]) -plt.plot(L[0][1000:2000]) -plt.plot(L[0][2000:3000]) +plt.plot(L[0][:999]) +plt.plot(L[0][999:2*999]) +plt.plot(L[0][2*999:3*999]) plt.title("Landscape") plt.show() diff --git a/src/python/gudhi/representations/vector_methods.py b/src/python/gudhi/representations/vector_methods.py index 61c4fb84..083551a4 100644 --- a/src/python/gudhi/representations/vector_methods.py +++ b/src/python/gudhi/representations/vector_methods.py @@ -104,10 +104,11 @@ class Landscape(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. y (n x 1 array): persistence diagram labels (unused). """ + self.nan_in_range = np.isnan(np.array(self.sample_range)) if np.isnan(np.array(self.sample_range)).any(): pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] - self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) + self.sample_range = np.where(self.nan_in_range, np.array([mx, My]), np.array(self.sample_range)) return self def transform(self, X): @@ -121,7 +122,7 @@ class Landscape(BaseEstimator, TransformerMixin): numpy array with shape (number of diagrams) x (number of samples = **num_landscapes** x **resolution**): output persistence landscapes. """ num_diag, Xfit = len(X), [] - x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution + self.nan_in_range.sum()) step_x = x_values[1] - x_values[0] for i in range(num_diag): @@ -157,7 +158,12 @@ class Landscape(BaseEstimator, TransformerMixin): for k in range( min(self.num_landscapes, len(events[j])) ): ls[k,j] = events[j][k] - Xfit.append(np.sqrt(2)*np.reshape(ls,[1,-1])) + if self.nan_in_range[0]: + ls = ls[:,1:] + if self.nan_in_range[1]: + ls = ls[:,:-1] + ls = np.sqrt(2)*np.reshape(ls,[1,-1]) + Xfit.append(ls) Xfit = np.concatenate(Xfit,0) -- cgit v1.2.3 From 5385b57782d63cf86048762e9a1c9b0c1070930c Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 9 Dec 2019 10:29:02 +0100 Subject: Document 'ctest --output-on-failure' in installation tests section --- src/common/doc/installation.h | 5 ++++- src/python/doc/installation.rst | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/common/doc/installation.h b/src/common/doc/installation.h index c320e7e0..c4ca10d1 100644 --- a/src/common/doc/installation.h +++ b/src/common/doc/installation.h @@ -32,7 +32,10 @@ make \endverbatim * * \subsection testsuites Test suites * To test your build, run the following command in a terminal: - * \verbatim make test \endverbatim + * \verbatim make test \endverbatim + * `make test` is using Ctest<\a> (CMake test driver + * program). If some of the tests are failing, plend send us the result of the following command: + * \verbatim ctest --output-on-failure \endverbatim * * \subsection documentationgeneration Documentation * To generate the documentation, Doxygen is required. diff --git a/src/python/doc/installation.rst b/src/python/doc/installation.rst index 54504413..3553ae51 100644 --- a/src/python/doc/installation.rst +++ b/src/python/doc/installation.rst @@ -98,6 +98,18 @@ following command in a terminal: export PYTHONPATH='$PYTHONPATH:/path-to-gudhi/build/python' make test +`make test` is using +`Ctest `_ (CMake test +driver program). If some of the tests are failing, plend send us the result of +the following command: + +.. code-block:: bash + + cd /path-to-gudhi/build/python + # For windows, you have to set PYTHONPATH environment variable + export PYTHONPATH='$PYTHONPATH:/path-to-gudhi/build/python' + ctest --output-on-failure + Debugging issues ================ -- cgit v1.2.3 From 406d7349a4732a185374822bf465f914e95d07c9 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 9 Dec 2019 11:11:00 +0100 Subject: Add some debug traces to find why sphinx fails --- .travis.yml | 3 ++- src/python/doc/python3-sphinx-build.py | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/.travis.yml b/.travis.yml index 51bc26c3..1c490b69 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ matrix: - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='OFF' MAKE_TARGET='doxygen' CTEST_COMMAND='echo No tests for doxygen target' - env: # 5. Only Python, associated tests and sphinx documentation - - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='ON' MAKE_TARGET='all sphinx' CTEST_COMMAND='ctest --output-on-failure' + - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='ON' MAKE_TARGET='all' CTEST_COMMAND='ctest --output-on-failure' cache: directories: @@ -56,6 +56,7 @@ install: - python3 -m pip install --upgrade pip setuptools wheel - python3 -m pip install --user pytest Cython sphinx sphinxcontrib-bibtex sphinx-paramlinks matplotlib numpy scipy scikit-learn - python3 -m pip install --user POT + - which python3 script: - rm -rf build diff --git a/src/python/doc/python3-sphinx-build.py b/src/python/doc/python3-sphinx-build.py index 84d158cf..a8eede8a 100755 --- a/src/python/doc/python3-sphinx-build.py +++ b/src/python/doc/python3-sphinx-build.py @@ -5,6 +5,8 @@ Emulate sphinx-build for python3 """ from sys import exit, argv +print(sys.executable) +import sphinx from sphinx import main if __name__ == '__main__': -- cgit v1.2.3 From 3f99285cfbafd7c9fcabad6469bf16b3ba52396f Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 9 Dec 2019 13:31:18 +0100 Subject: need to import sys --- src/python/doc/python3-sphinx-build.py | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/python/doc/python3-sphinx-build.py b/src/python/doc/python3-sphinx-build.py index a8eede8a..5dc26fd2 100755 --- a/src/python/doc/python3-sphinx-build.py +++ b/src/python/doc/python3-sphinx-build.py @@ -5,6 +5,7 @@ Emulate sphinx-build for python3 """ from sys import exit, argv +import sys print(sys.executable) import sphinx from sphinx import main -- cgit v1.2.3 From 83867c78935460f727831b54e390c5be30bb4eee Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 9 Dec 2019 13:59:07 +0100 Subject: need to import sys --- src/python/doc/python3-sphinx-build.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/python/doc/python3-sphinx-build.py b/src/python/doc/python3-sphinx-build.py index 5dc26fd2..3628e89e 100755 --- a/src/python/doc/python3-sphinx-build.py +++ b/src/python/doc/python3-sphinx-build.py @@ -4,9 +4,8 @@ Emulate sphinx-build for python3 """ -from sys import exit, argv -import sys -print(sys.executable) +from sys import exit, argv, executable +print(executable) import sphinx from sphinx import main -- cgit v1.2.3 From 4391bf38f14f483b9032e3eaf99f315f2f053026 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 10 Dec 2019 00:04:02 +0100 Subject: Remove debug traces --- .travis.yml | 1 - src/python/doc/python3-sphinx-build.py | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'src') diff --git a/.travis.yml b/.travis.yml index b7cd854a..d6c82e70 100644 --- a/.travis.yml +++ b/.travis.yml @@ -60,7 +60,6 @@ install: - python3 -m pip install --upgrade pip setuptools wheel - python3 -m pip install --user pytest Cython sphinx sphinxcontrib-bibtex sphinx-paramlinks matplotlib numpy scipy scikit-learn - python3 -m pip install --user POT - - which python3 script: - rm -rf build diff --git a/src/python/doc/python3-sphinx-build.py b/src/python/doc/python3-sphinx-build.py index 3628e89e..d1f0f08e 100755 --- a/src/python/doc/python3-sphinx-build.py +++ b/src/python/doc/python3-sphinx-build.py @@ -4,8 +4,7 @@ Emulate sphinx-build for python3 """ -from sys import exit, argv, executable -print(executable) +from sys import exit, argv import sphinx from sphinx import main -- cgit v1.2.3 From 94118b7c5c723bf62dcdafd404d492e8d78d0019 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 10 Dec 2019 10:06:00 +0100 Subject: Remove useless import --- src/python/doc/python3-sphinx-build.py | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/python/doc/python3-sphinx-build.py b/src/python/doc/python3-sphinx-build.py index d1f0f08e..84d158cf 100755 --- a/src/python/doc/python3-sphinx-build.py +++ b/src/python/doc/python3-sphinx-build.py @@ -5,7 +5,6 @@ Emulate sphinx-build for python3 """ from sys import exit, argv -import sphinx from sphinx import main if __name__ == '__main__': -- cgit v1.2.3 From 5ecc15ba30e7a20604d50c1fdec9e7da2de64898 Mon Sep 17 00:00:00 2001 From: mathieu Date: Tue, 10 Dec 2019 14:24:52 -0500 Subject: fixed doc and examples --- src/python/doc/representations.rst | 4 ++-- src/python/example/diagram_vectorizations_distances_kernels.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/python/doc/representations.rst b/src/python/doc/representations.rst index b338f7f0..409e97da 100644 --- a/src/python/doc/representations.rst +++ b/src/python/doc/representations.rst @@ -10,7 +10,7 @@ Representations manual This module, originally available at https://github.com/MathieuCarriere/sklearn-tda and named sklearn_tda, aims at bridging the gap between persistence diagrams and machine learning, by providing implementations of most of the vector representations for persistence diagrams in the literature, in a scikit-learn format. More specifically, it provides tools, using the scikit-learn standard interface, to compute distances and kernels on persistence diagrams, and to convert these diagrams into vectors in Euclidean space. -A diagram is represented as a numpy array of shape (n,2), as can be obtained from `SimplexTree.persistence_intervals_in_dimension` for instance. Points at infinity are represented as a numpy array of shape (n,1), storing only the birth time. +A diagram is represented as a numpy array of shape (n,2), as can be obtained from :func:`~gudhi.SimplexTree.persistence_intervals_in_dimension` for instance. Points at infinity are represented as a numpy array of shape (n,1), storing only the birth time. A small example is provided @@ -66,4 +66,4 @@ The output is: .. testoutput:: - [[0. 1.25707872 2.51415744 1.88561808 0.7856742 2.04275292 3.29983165 2.51415744 1.25707872 0. 0. 0. 0.31426968 0. 0.62853936 0. 0. 0.31426968 1.25707872 0. ]] + [[1.02851895 2.05703791 2.57129739 1.54277843 0.89995409 1.92847304 2.95699199 3.08555686 0. 0.64282435 0. 0. 0.51425948 0. 0. 0. ]] diff --git a/src/python/example/diagram_vectorizations_distances_kernels.py b/src/python/example/diagram_vectorizations_distances_kernels.py index f777984c..0ea4ba79 100755 --- a/src/python/example/diagram_vectorizations_distances_kernels.py +++ b/src/python/example/diagram_vectorizations_distances_kernels.py @@ -26,9 +26,9 @@ plt.show() LS = Landscape(resolution=1000) L = LS.fit_transform(diags) -plt.plot(L[0][:999]) -plt.plot(L[0][999:2*999]) -plt.plot(L[0][2*999:3*999]) +plt.plot(L[0][:998]) +plt.plot(L[0][998:2*998]) +plt.plot(L[0][2*998:3*998]) plt.title("Landscape") plt.show() -- cgit v1.2.3 From 682f8c8cb18ba898a3d23a82fff454e862541aed Mon Sep 17 00:00:00 2001 From: Mathieu Carrière Date: Wed, 11 Dec 2019 13:48:26 -0500 Subject: Update src/python/doc/representations.rst Co-Authored-By: Vincent Rouvreau <10407034+VincentRouvreau@users.noreply.github.com> --- src/python/doc/representations.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/python/doc/representations.rst b/src/python/doc/representations.rst index 409e97da..470b57bf 100644 --- a/src/python/doc/representations.rst +++ b/src/python/doc/representations.rst @@ -66,4 +66,6 @@ The output is: .. testoutput:: - [[1.02851895 2.05703791 2.57129739 1.54277843 0.89995409 1.92847304 2.95699199 3.08555686 0. 0.64282435 0. 0. 0.51425948 0. 0. 0. ]] + [[1.02851895 2.05703791 2.57129739 1.54277843 0.89995409 1.92847304 + 2.95699199 3.08555686 0. 0.64282435 0. 0. + 0.51425948 0. 0. 0. ]] -- cgit v1.2.3 From 363ae171ee7f45cf11d01653e4d4e9580117cfd0 Mon Sep 17 00:00:00 2001 From: mathieu Date: Wed, 11 Dec 2019 13:50:21 -0500 Subject: fixed landscape --- .../example/diagram_vectorizations_distances_kernels.py | 6 +++--- src/python/gudhi/representations/vector_methods.py | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/python/example/diagram_vectorizations_distances_kernels.py b/src/python/example/diagram_vectorizations_distances_kernels.py index 0ea4ba79..119072eb 100755 --- a/src/python/example/diagram_vectorizations_distances_kernels.py +++ b/src/python/example/diagram_vectorizations_distances_kernels.py @@ -26,9 +26,9 @@ plt.show() LS = Landscape(resolution=1000) L = LS.fit_transform(diags) -plt.plot(L[0][:998]) -plt.plot(L[0][998:2*998]) -plt.plot(L[0][2*998:3*998]) +plt.plot(L[0][:1000]) +plt.plot(L[0][1000:2000]) +plt.plot(L[0][2000:3000]) plt.title("Landscape") plt.show() diff --git a/src/python/gudhi/representations/vector_methods.py b/src/python/gudhi/representations/vector_methods.py index 083551a4..cd532275 100644 --- a/src/python/gudhi/representations/vector_methods.py +++ b/src/python/gudhi/representations/vector_methods.py @@ -129,19 +129,19 @@ class Landscape(BaseEstimator, TransformerMixin): diagram, num_pts_in_diag = X[i], X[i].shape[0] - ls = np.zeros([self.num_landscapes, self.resolution]) + ls = np.zeros([self.num_landscapes, self.resolution + self.nan_in_range.sum()]) events = [] - for j in range(self.resolution): + for j in range(self.resolution + self.nan_in_range.sum()): events.append([]) for j in range(num_pts_in_diag): [px,py] = diagram[j,:2] - min_idx = np.clip(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0, self.resolution) - mid_idx = np.clip(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0, self.resolution) - max_idx = np.clip(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0, self.resolution) + min_idx = np.clip(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0, self.resolution + self.nan_in_range.sum()) + mid_idx = np.clip(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0, self.resolution + self.nan_in_range.sum()) + max_idx = np.clip(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0, self.resolution + self.nan_in_range.sum()) - if min_idx < self.resolution and max_idx > 0: + if min_idx < self.resolution + self.nan_in_range.sum() and max_idx > 0: landscape_value = self.sample_range[0] + min_idx * step_x - px for k in range(min_idx, mid_idx): @@ -153,7 +153,7 @@ class Landscape(BaseEstimator, TransformerMixin): events[k].append(landscape_value) landscape_value -= step_x - for j in range(self.resolution): + for j in range(self.resolution + self.nan_in_range.sum()): events[j].sort(reverse=True) for k in range( min(self.num_landscapes, len(events[j])) ): ls[k,j] = events[j][k] -- cgit v1.2.3 From 9e75cc1832403f8ffec38fc3a4f6b1081fe4770e Mon Sep 17 00:00:00 2001 From: mathieu Date: Wed, 11 Dec 2019 13:57:15 -0500 Subject: update example --- src/python/doc/representations.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/python/doc/representations.rst b/src/python/doc/representations.rst index 470b57bf..11dcbcf9 100644 --- a/src/python/doc/representations.rst +++ b/src/python/doc/representations.rst @@ -67,5 +67,6 @@ The output is: .. testoutput:: [[1.02851895 2.05703791 2.57129739 1.54277843 0.89995409 1.92847304 - 2.95699199 3.08555686 0. 0.64282435 0. 0. - 0.51425948 0. 0. 0. ]] + 2.95699199 3.08555686 2.05703791 1.02851895 0. 0.64282435 + 0. 0. 0.51425948 0. 0. 0. + 0.77138922 1.02851895]] -- cgit v1.2.3 From 2886885ff4cf1f134863de0fa97b64f824d67622 Mon Sep 17 00:00:00 2001 From: mathieu Date: Wed, 11 Dec 2019 15:30:45 -0500 Subject: cleanup --- src/python/gudhi/representations/vector_methods.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/representations/vector_methods.py b/src/python/gudhi/representations/vector_methods.py index cd532275..9b280f68 100644 --- a/src/python/gudhi/representations/vector_methods.py +++ b/src/python/gudhi/representations/vector_methods.py @@ -95,6 +95,7 @@ class Landscape(BaseEstimator, TransformerMixin): sample_range ([double, double]): minimum and maximum of all piecewise-linear function domains, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn evenly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. """ self.num_landscapes, self.resolution, self.sample_range = num_landscapes, resolution, sample_range + self.nan_in_range = np.isnan(np.array(self.sample_range)) def fit(self, X, y=None): """ @@ -104,8 +105,7 @@ class Landscape(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. y (n x 1 array): persistence diagram labels (unused). """ - self.nan_in_range = np.isnan(np.array(self.sample_range)) - if np.isnan(np.array(self.sample_range)).any(): + if self.nan_in_range.any(): pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] self.sample_range = np.where(self.nan_in_range, np.array([mx, My]), np.array(self.sample_range)) -- cgit v1.2.3 From 7bd6907e577e22803fec179f652ecf0ec64dcb4a Mon Sep 17 00:00:00 2001 From: mathieu Date: Wed, 11 Dec 2019 15:38:00 -0500 Subject: cleanup for landscape resolution --- src/python/gudhi/representations/vector_methods.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/representations/vector_methods.py b/src/python/gudhi/representations/vector_methods.py index 9b280f68..fe26dbe2 100644 --- a/src/python/gudhi/representations/vector_methods.py +++ b/src/python/gudhi/representations/vector_methods.py @@ -96,6 +96,7 @@ class Landscape(BaseEstimator, TransformerMixin): """ self.num_landscapes, self.resolution, self.sample_range = num_landscapes, resolution, sample_range self.nan_in_range = np.isnan(np.array(self.sample_range)) + self.new_resolution = self.resolution + self.nan_in_range.sum() def fit(self, X, y=None): """ @@ -122,26 +123,26 @@ class Landscape(BaseEstimator, TransformerMixin): numpy array with shape (number of diagrams) x (number of samples = **num_landscapes** x **resolution**): output persistence landscapes. """ num_diag, Xfit = len(X), [] - x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution + self.nan_in_range.sum()) + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.new_resolution) step_x = x_values[1] - x_values[0] for i in range(num_diag): diagram, num_pts_in_diag = X[i], X[i].shape[0] - ls = np.zeros([self.num_landscapes, self.resolution + self.nan_in_range.sum()]) + ls = np.zeros([self.num_landscapes, self.new_resolution]) events = [] - for j in range(self.resolution + self.nan_in_range.sum()): + for j in range(self.new_resolution): events.append([]) for j in range(num_pts_in_diag): [px,py] = diagram[j,:2] - min_idx = np.clip(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0, self.resolution + self.nan_in_range.sum()) - mid_idx = np.clip(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0, self.resolution + self.nan_in_range.sum()) - max_idx = np.clip(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0, self.resolution + self.nan_in_range.sum()) + min_idx = np.clip(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0, self.new_resolution) + mid_idx = np.clip(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0, self.new_resolution) + max_idx = np.clip(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0, self.new_resolution) - if min_idx < self.resolution + self.nan_in_range.sum() and max_idx > 0: + if min_idx < self.new_resolution and max_idx > 0: landscape_value = self.sample_range[0] + min_idx * step_x - px for k in range(min_idx, mid_idx): @@ -153,7 +154,7 @@ class Landscape(BaseEstimator, TransformerMixin): events[k].append(landscape_value) landscape_value -= step_x - for j in range(self.resolution + self.nan_in_range.sum()): + for j in range(self.new_resolution): events[j].sort(reverse=True) for k in range( min(self.num_landscapes, len(events[j])) ): ls[k,j] = events[j][k] -- cgit v1.2.3 From 7f426f5a85051676ffb6f8952689bd400ddcc10b Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau <10407034+VincentRouvreau@users.noreply.github.com> Date: Thu, 12 Dec 2019 09:40:35 +0100 Subject: Code review: Typo in src/common/doc/installation.h Co-Authored-By: Marc Glisse --- src/common/doc/installation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/common/doc/installation.h b/src/common/doc/installation.h index c4ca10d1..d70a2efa 100644 --- a/src/common/doc/installation.h +++ b/src/common/doc/installation.h @@ -34,7 +34,7 @@ make \endverbatim * To test your build, run the following command in a terminal: * \verbatim make test \endverbatim * `make test` is using Ctest<\a> (CMake test driver - * program). If some of the tests are failing, plend send us the result of the following command: + * program). If some of the tests are failing, please send us the result of the following command: * \verbatim ctest --output-on-failure \endverbatim * * \subsection documentationgeneration Documentation -- cgit v1.2.3 From a1b6e5a034bc698b02d3c93e1707cab5622eaea6 Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau <10407034+VincentRouvreau@users.noreply.github.com> Date: Thu, 12 Dec 2019 09:41:04 +0100 Subject: Code review: typo in src/python/doc/installation.rst Co-Authored-By: Marc Glisse --- src/python/doc/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/python/doc/installation.rst b/src/python/doc/installation.rst index 3553ae51..c3ddc017 100644 --- a/src/python/doc/installation.rst +++ b/src/python/doc/installation.rst @@ -100,7 +100,7 @@ following command in a terminal: `make test` is using `Ctest `_ (CMake test -driver program). If some of the tests are failing, plend send us the result of +driver program). If some of the tests are failing, please send us the result of the following command: .. code-block:: bash -- cgit v1.2.3 From 38f0ff98cf6da1e882b6b44fc3ed7b3d310fb91f Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 12 Dec 2019 10:00:10 +0100 Subject: Doc review: use ctest directly and provide options suggestions --- src/python/doc/installation.rst | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/python/doc/installation.rst b/src/python/doc/installation.rst index 3553ae51..50a697c7 100644 --- a/src/python/doc/installation.rst +++ b/src/python/doc/installation.rst @@ -83,32 +83,30 @@ Or install it definitely in your Python packages folder: .. code-block:: bash - python setup.py install --prefix /home/gudhi # Install in /home/gudhi directory + python setup.py install --prefix /home/gudhi # Install in /home/gudhi directory Test suites =========== -To test your build, `py.test `_ is optional. Run the -following command in a terminal: +To test your build, `py.test `_ is required. Run the +following `Ctest `_ +(CMake test driver program) command in a terminal: .. code-block:: bash cd /path-to-gudhi/build/python # For windows, you have to set PYTHONPATH environment variable export PYTHONPATH='$PYTHONPATH:/path-to-gudhi/build/python' - make test + ctest -`make test` is using -`Ctest `_ (CMake test -driver program). If some of the tests are failing, plend send us the result of -the following command: +.. note:: + + One can use :code:`ctest` specific options in the python directory: .. code-block:: bash - cd /path-to-gudhi/build/python - # For windows, you have to set PYTHONPATH environment variable - export PYTHONPATH='$PYTHONPATH:/path-to-gudhi/build/python' - ctest --output-on-failure + # Launch tests in parallel on 8 cores and set failing tests in verbose mode + ctest -j 8 --output-on-failure Debugging issues ================ -- cgit v1.2.3 From b884a620824a9d707cec82c9dddf7ab236263b95 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 12 Dec 2019 10:04:59 +0100 Subject: conflict resolution --- src/python/doc/installation.rst | 7 ------- 1 file changed, 7 deletions(-) (limited to 'src') diff --git a/src/python/doc/installation.rst b/src/python/doc/installation.rst index 14000ecf..50a697c7 100644 --- a/src/python/doc/installation.rst +++ b/src/python/doc/installation.rst @@ -99,16 +99,9 @@ following `Ctest `_ export PYTHONPATH='$PYTHONPATH:/path-to-gudhi/build/python' ctest -<<<<<<< HEAD .. note:: One can use :code:`ctest` specific options in the python directory: -======= -`make test` is using -`Ctest `_ (CMake test -driver program). If some of the tests are failing, please send us the result of -the following command: ->>>>>>> a1b6e5a034bc698b02d3c93e1707cab5622eaea6 .. code-block:: bash -- cgit v1.2.3 From 3f2eb763d4cdbf895a95c89ee938940962a9d938 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 13 Dec 2019 11:32:38 +0100 Subject: Add a more representative image for python persistence representations --- src/Persistence_representations/doc/sklearn-tda.png | Bin 0 -> 388075 bytes src/python/doc/representations_sum.inc | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 src/Persistence_representations/doc/sklearn-tda.png (limited to 'src') diff --git a/src/Persistence_representations/doc/sklearn-tda.png b/src/Persistence_representations/doc/sklearn-tda.png new file mode 100644 index 00000000..f0ff07f4 Binary files /dev/null and b/src/Persistence_representations/doc/sklearn-tda.png differ diff --git a/src/python/doc/representations_sum.inc b/src/python/doc/representations_sum.inc index 7b167a17..d4fe37f6 100644 --- a/src/python/doc/representations_sum.inc +++ b/src/python/doc/representations_sum.inc @@ -3,7 +3,7 @@ +------------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------------------+ | .. figure:: | Vectorizations, distances and kernels that work on persistence | :Author: Mathieu Carrière | - | ../../doc/Persistence_representations/average_landscape.png | diagrams, compatible with scikit-learn. | | + | ../../doc/Persistence_representations/sklearn-tda.png | diagrams, compatible with scikit-learn. | | | | | :Introduced in: GUDHI 3.1.0 | | | | | | | | :Copyright: MIT | -- cgit v1.2.3 From 03c577e3b2b4cea844d64c7aeb68b9f73db4602a Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 13 Dec 2019 12:05:06 +0100 Subject: Move image in python img instead of C++ one --- src/Persistence_representations/doc/sklearn-tda.png | Bin 388075 -> 0 bytes src/python/doc/img/sklearn-tda.png | Bin 0 -> 388075 bytes src/python/doc/representations_sum.inc | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 src/Persistence_representations/doc/sklearn-tda.png create mode 100644 src/python/doc/img/sklearn-tda.png (limited to 'src') diff --git a/src/Persistence_representations/doc/sklearn-tda.png b/src/Persistence_representations/doc/sklearn-tda.png deleted file mode 100644 index f0ff07f4..00000000 Binary files a/src/Persistence_representations/doc/sklearn-tda.png and /dev/null differ diff --git a/src/python/doc/img/sklearn-tda.png b/src/python/doc/img/sklearn-tda.png new file mode 100644 index 00000000..f0ff07f4 Binary files /dev/null and b/src/python/doc/img/sklearn-tda.png differ diff --git a/src/python/doc/representations_sum.inc b/src/python/doc/representations_sum.inc index d4fe37f6..700828f1 100644 --- a/src/python/doc/representations_sum.inc +++ b/src/python/doc/representations_sum.inc @@ -3,7 +3,7 @@ +------------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------------------+ | .. figure:: | Vectorizations, distances and kernels that work on persistence | :Author: Mathieu Carrière | - | ../../doc/Persistence_representations/sklearn-tda.png | diagrams, compatible with scikit-learn. | | + | img/sklearn-tda.png | diagrams, compatible with scikit-learn. | | | | | :Introduced in: GUDHI 3.1.0 | | | | | | | | :Copyright: MIT | -- cgit v1.2.3