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 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 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 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 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 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 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 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 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(-) 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(-) 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 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(-) 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 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 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(+) 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 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(+) 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 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(+) 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 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(-) 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 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(-) 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(-) 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 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(-) 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 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(+) 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(-) 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 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(-) 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 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(-) 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(-) 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(-) 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(-) 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 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 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(-) 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(-) 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