From aedd6928abdcb3057ce7d48f254f8e698d7c7a79 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 26 Apr 2019 16:41:25 +0200 Subject: numpy is now a hard dependency. persistence_intervals_in_dimension and read_persistence_intervals_in_dimension now returns a numpy array of dimension 2 (list of [birth, death]). --- src/cython/cython/cubical_complex.pyx | 10 ++++--- src/cython/cython/periodic_cubical_complex.pyx | 10 ++++--- src/cython/cython/persistence_graphical_tools.py | 29 +++++++++----------- src/cython/cython/reader_utils.pyx | 34 +++++++++++++----------- src/cython/cython/simplex_tree.pyx | 8 +++--- src/cython/setup.py.in | 8 +++--- src/cython/test/test_reader_utils.py | 19 ++++++------- 7 files changed, 64 insertions(+), 54 deletions(-) diff --git a/src/cython/cython/cubical_complex.pyx b/src/cython/cython/cubical_complex.pyx index e94cd539..53d79b92 100644 --- a/src/cython/cython/cubical_complex.pyx +++ b/src/cython/cython/cubical_complex.pyx @@ -5,13 +5,15 @@ from libcpp.string cimport string from libcpp cimport bool import os +from numpy import array as np_array + """This file is part of the Gudhi Library. The Gudhi library (Geometric Understanding in Higher Dimensions) is a generic C++ library for computational topology. Author(s): Vincent Rouvreau - Copyright (C) 2016 Inria + Copyright (C) 2019 Inria This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -182,9 +184,9 @@ cdef class CubicalComplex: specific dimension. :param dimension: The specific dimension. - :type from_value: int. + :type dimension: int. :returns: The persistence intervals. - :rtype: list of pair of float + :rtype: numpy array of dimension 2 :note: intervals_in_dim function requires persistence function to be launched first. @@ -195,4 +197,4 @@ cdef class CubicalComplex: else: print("intervals_in_dim function requires persistence function" " to be launched first.") - return intervals_result + return np_array(intervals_result) diff --git a/src/cython/cython/periodic_cubical_complex.pyx b/src/cython/cython/periodic_cubical_complex.pyx index e626950b..3866f53b 100644 --- a/src/cython/cython/periodic_cubical_complex.pyx +++ b/src/cython/cython/periodic_cubical_complex.pyx @@ -5,13 +5,15 @@ from libcpp.string cimport string from libcpp cimport bool import os +from numpy import array as np_array + """This file is part of the Gudhi Library. The Gudhi library (Geometric Understanding in Higher Dimensions) is a generic C++ library for computational topology. Author(s): Vincent Rouvreau - Copyright (C) 2016 Inria + Copyright (C) 2019 Inria This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -184,9 +186,9 @@ cdef class PeriodicCubicalComplex: specific dimension. :param dimension: The specific dimension. - :type from_value: int. + :type dimension: int. :returns: The persistence intervals. - :rtype: list of pair of float + :rtype: numpy array of dimension 2 :note: intervals_in_dim function requires persistence function to be launched first. @@ -197,4 +199,4 @@ cdef class PeriodicCubicalComplex: else: print("intervals_in_dim function requires persistence function" " to be launched first.") - return intervals_result + return np_array(intervals_result) diff --git a/src/cython/cython/persistence_graphical_tools.py b/src/cython/cython/persistence_graphical_tools.py index d7be936f..98cd355c 100644 --- a/src/cython/cython/persistence_graphical_tools.py +++ b/src/cython/cython/persistence_graphical_tools.py @@ -1,10 +1,14 @@ +from os import path +from math import isfinite +import numpy as np + """This file is part of the Gudhi Library. The Gudhi library (Geometric Understanding in Higher Dimensions) is a generic C++ library for computational topology. Author(s): Vincent Rouvreau, Bertrand Michel - Copyright (C) 2016 Inria + Copyright (C) 2019 Inria This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -85,11 +89,9 @@ def plot_persistence_barcode(persistence=[], persistence_file='', alpha=0.6, try: import matplotlib.pyplot as plt import matplotlib.patches as mpatches - import numpy as np - import os if persistence_file is not '': - if os.path.isfile(persistence_file): + if path.isfile(persistence_file): # Reset persistence persistence = [] diag = read_persistence_intervals_grouped_by_dimension(persistence_file=persistence_file) @@ -144,7 +146,7 @@ def plot_persistence_barcode(persistence=[], persistence_file='', alpha=0.6, return plt except ImportError: - print("This function is not available, you may be missing numpy and/or matplotlib.") + print("This function is not available, you may be missing matplotlib.") def plot_persistence_diagram(persistence=[], persistence_file='', alpha=0.6, band=0., max_intervals=1000, max_plots=1000, inf_delta=0.1, legend=False): @@ -177,11 +179,9 @@ def plot_persistence_diagram(persistence=[], persistence_file='', alpha=0.6, try: import matplotlib.pyplot as plt import matplotlib.patches as mpatches - import numpy as np - import os if persistence_file is not '': - if os.path.isfile(persistence_file): + if path.isfile(persistence_file): # Reset persistence persistence = [] diag = read_persistence_intervals_grouped_by_dimension(persistence_file=persistence_file) @@ -240,7 +240,7 @@ def plot_persistence_diagram(persistence=[], persistence_file='', alpha=0.6, return plt except ImportError: - print("This function is not available, you may be missing numpy and/or matplotlib.") + print("This function is not available, you may be missing matplotlib.") def plot_persistence_density(persistence=[], persistence_file='', nbins=300, bw_method=None, @@ -288,13 +288,10 @@ def plot_persistence_density(persistence=[], persistence_file='', """ try: import matplotlib.pyplot as plt - import numpy as np from scipy.stats import kde - import os - import math if persistence_file is not '': - if os.path.isfile(persistence_file): + if path.isfile(persistence_file): # Reset persistence persistence = [] diag = read_persistence_intervals_grouped_by_dimension(persistence_file=persistence_file) @@ -318,8 +315,8 @@ def plot_persistence_density(persistence=[], persistence_file='', reverse=True)[:max_intervals] # Set as numpy array birth and death (remove undefined values - inf and NaN) - birth = np.asarray([(interval[1][0]) for interval in persistence_dim if (math.isfinite(interval[1][1]) and math.isfinite(interval[1][0]))]) - death = np.asarray([(interval[1][1]) for interval in persistence_dim if (math.isfinite(interval[1][1]) and math.isfinite(interval[1][0]))]) + birth = np.asarray([(interval[1][0]) for interval in persistence_dim if (isfinite(interval[1][1]) and isfinite(interval[1][0]))]) + death = np.asarray([(interval[1][1]) for interval in persistence_dim if (isfinite(interval[1][1]) and isfinite(interval[1][0]))]) # line display of equation : birth = death x = np.linspace(death.min(), birth.max(), 1000) @@ -345,4 +342,4 @@ def plot_persistence_density(persistence=[], persistence_file='', return plt except ImportError: - print("This function is not available, you may be missing numpy, matplotlib and/or scipy.") + print("This function is not available, you may be missing matplotlib and/or scipy.") diff --git a/src/cython/cython/reader_utils.pyx b/src/cython/cython/reader_utils.pyx index e4572db0..6dde5286 100644 --- a/src/cython/cython/reader_utils.pyx +++ b/src/cython/cython/reader_utils.pyx @@ -3,7 +3,9 @@ from libcpp.vector cimport vector from libcpp.string cimport string from libcpp.map cimport map from libcpp.pair cimport pair -import os + +from os import path +from numpy import array as np_array """This file is part of the Gudhi Library. The Gudhi library (Geometric Understanding in Higher Dimensions) is a generic C++ @@ -11,7 +13,7 @@ import os Author(s): Vincent Rouvreau - Copyright (C) 2017 Inria + Copyright (C) 2019 Inria This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -48,7 +50,7 @@ def read_lower_triangular_matrix_from_csv_file(csv_file='', separator=';'): :rtype: vector[vector[double]] """ if csv_file is not '': - if os.path.isfile(csv_file): + if path.isfile(csv_file): return read_matrix_from_csv_file(str.encode(csv_file), ord(separator[0])) print("file " + csv_file + " not set or not found.") return [] @@ -67,29 +69,31 @@ def read_persistence_intervals_grouped_by_dimension(persistence_file=''): :rtype: map[int, vector[pair[double, double]]] """ if persistence_file is not '': - if os.path.isfile(persistence_file): + if path.isfile(persistence_file): return read_pers_intervals_grouped_by_dimension(str.encode(persistence_file)) print("file " + persistence_file + " not set or not found.") return [] def read_persistence_intervals_in_dimension(persistence_file='', only_this_dim=-1): """Reads a file containing persistence intervals. - Each line might contain 2, 3 or 4 values: [[field] dimension] birth death - If `only_this_dim` = -1, dimension is ignored and all lines are returned. - If `only_this_dim` is >= 0, only the lines where dimension = `only_this_dim` - (or where dimension is not specified) are returned. - The return value is an `vector[pair[birth, death]]` - where `birth` a `double`, and `death` a `double`. + Each line of persistence_file might contain 2, 3 or 4 values: + [[field] dimension] birth death Note: the function does not check that birth <= death. :param persistence_file: A persistence file style name. :type persistence_file: string - - :returns: The persistence pairs grouped by dimension. - :rtype: map[int, vector[pair[double, double]]] + :param only_this_dim: The specific dimension. Default value is -1. + If `only_this_dim` = -1, dimension is ignored and all lines are returned. + If `only_this_dim` is >= 0, only the lines where dimension = + `only_this_dim` (or where dimension is not specified) are returned. + :type only_this_dim: int. + + :returns: The persistence intervals. + :rtype: numpy array of dimension 2 """ if persistence_file is not '': - if os.path.isfile(persistence_file): - return read_pers_intervals_in_dimension(str.encode(persistence_file), only_this_dim) + if path.isfile(persistence_file): + return np_array(read_pers_intervals_in_dimension(str.encode( + persistence_file), only_this_dim)) print("file " + persistence_file + " not set or not found.") return [] diff --git a/src/cython/cython/simplex_tree.pyx b/src/cython/cython/simplex_tree.pyx index 0ab97f80..ee04589e 100644 --- a/src/cython/cython/simplex_tree.pyx +++ b/src/cython/cython/simplex_tree.pyx @@ -4,13 +4,15 @@ from libcpp.utility cimport pair from libcpp cimport bool from libcpp.string cimport string +from numpy import array as np_array + """This file is part of the Gudhi Library. The Gudhi library (Geometric Understanding in Higher Dimensions) is a generic C++ library for computational topology. Author(s): Vincent Rouvreau - Copyright (C) 2016 Inria + Copyright (C) 2019 Inria This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -498,7 +500,7 @@ cdef class SimplexTree: :param dimension: The specific dimension. :type dimension: int. :returns: The persistence intervals. - :rtype: list of pair of float + :rtype: numpy array of dimension 2 :note: intervals_in_dim function requires :func:`persistence()` @@ -510,7 +512,7 @@ cdef class SimplexTree: else: print("intervals_in_dim function requires persistence function" " to be launched first.") - return intervals_result + return np_array(intervals_result) def persistence_pairs(self): """This function returns the persistence pairs of the simplicial diff --git a/src/cython/setup.py.in b/src/cython/setup.py.in index 4037aab6..c66905ac 100644 --- a/src/cython/setup.py.in +++ b/src/cython/setup.py.in @@ -1,5 +1,6 @@ from distutils.core import setup, Extension from Cython.Build import cythonize +from numpy import get_include as numpy_get_include """This file is part of the Gudhi Library. The Gudhi library (Geometric Understanding in Higher Dimensions) is a generic C++ @@ -7,7 +8,7 @@ from Cython.Build import cythonize Author(s): Vincent Rouvreau - Copyright (C) 2016 Inria + Copyright (C) 2019 Inria This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -35,7 +36,7 @@ gudhi = Extension( extra_link_args=[@GUDHI_CYTHON_EXTRA_LINK_ARGS@], libraries=[@GUDHI_CYTHON_LIBRARIES@], library_dirs=[@GUDHI_CYTHON_LIBRARY_DIRS@], - include_dirs = [@GUDHI_CYTHON_INCLUDE_DIRS@], + include_dirs = [numpy_get_include(), @GUDHI_CYTHON_INCLUDE_DIRS@], runtime_library_dirs=[@GUDHI_CYTHON_RUNTIME_LIBRARY_DIRS@], ) @@ -46,5 +47,6 @@ setup( version='@GUDHI_VERSION@', url='http://gudhi.gforge.inria.fr/', ext_modules = cythonize(gudhi), - install_requires = ["cython",], + install_requires = ['cython','numpy >= 1.9',], + setup_requires = ['numpy >= 1.9',], ) diff --git a/src/cython/test/test_reader_utils.py b/src/cython/test/test_reader_utils.py index b240c84f..36e927b0 100755 --- a/src/cython/test/test_reader_utils.py +++ b/src/cython/test/test_reader_utils.py @@ -1,4 +1,5 @@ import gudhi +import numpy as np """This file is part of the Gudhi Library. The Gudhi library (Geometric Understanding in Higher Dimensions) is a generic C++ @@ -53,7 +54,7 @@ def test_non_existing_persistence_file(): persistence = gudhi.read_persistence_intervals_grouped_by_dimension(persistence_file='pouetpouettralala.toubiloubabdou') assert persistence == [] persistence = gudhi.read_persistence_intervals_in_dimension(persistence_file='pouetpouettralala.toubiloubabdou', only_this_dim=1) - assert persistence == [] + np.testing.assert_array_equal(persistence, []) def test_read_persistence_intervals_without_dimension(): # Create test file @@ -61,11 +62,11 @@ def test_read_persistence_intervals_without_dimension(): test_file.write('# Simple persistence diagram without dimension\n2.7 3.7\n9.6 14.\n34.2 34.974\n3. inf') test_file.close() persistence = gudhi.read_persistence_intervals_in_dimension(persistence_file='persistence_intervals_without_dimension.pers') - assert persistence == [(2.7, 3.7), (9.6, 14.), (34.2, 34.974), (3., float('Inf'))] + np.testing.assert_array_equal(persistence, [(2.7, 3.7), (9.6, 14.), (34.2, 34.974), (3., float('Inf'))]) persistence = gudhi.read_persistence_intervals_in_dimension(persistence_file='persistence_intervals_without_dimension.pers', only_this_dim=0) - assert persistence == [] + np.testing.assert_array_equal(persistence, []) persistence = gudhi.read_persistence_intervals_in_dimension(persistence_file='persistence_intervals_without_dimension.pers', only_this_dim=1) - assert persistence == [] + np.testing.assert_array_equal(persistence, []) persistence = gudhi.read_persistence_intervals_grouped_by_dimension(persistence_file='persistence_intervals_without_dimension.pers') assert persistence == {-1: [(2.7, 3.7), (9.6, 14.0), (34.2, 34.974), (3.0, float('Inf'))]} @@ -75,14 +76,14 @@ def test_read_persistence_intervals_with_dimension(): test_file.write('# Simple persistence diagram with dimension\n0 2.7 3.7\n1 9.6 14.\n3 34.2 34.974\n1 3. inf') test_file.close() persistence = gudhi.read_persistence_intervals_in_dimension(persistence_file='persistence_intervals_with_dimension.pers') - assert persistence == [(2.7, 3.7), (9.6, 14.), (34.2, 34.974), (3., float('Inf'))] + np.testing.assert_array_equal(persistence, [(2.7, 3.7), (9.6, 14.), (34.2, 34.974), (3., float('Inf'))]) persistence = gudhi.read_persistence_intervals_in_dimension(persistence_file='persistence_intervals_with_dimension.pers', only_this_dim=0) - assert persistence == [(2.7, 3.7)] + np.testing.assert_array_equal(persistence, [(2.7, 3.7)]) persistence = gudhi.read_persistence_intervals_in_dimension(persistence_file='persistence_intervals_with_dimension.pers', only_this_dim=1) - assert persistence == [(9.6, 14.), (3., float('Inf'))] + np.testing.assert_array_equal(persistence, [(9.6, 14.), (3., float('Inf'))]) persistence = gudhi.read_persistence_intervals_in_dimension(persistence_file='persistence_intervals_with_dimension.pers', only_this_dim=2) - assert persistence == [] + np.testing.assert_array_equal(persistence, []) persistence = gudhi.read_persistence_intervals_in_dimension(persistence_file='persistence_intervals_with_dimension.pers', only_this_dim=3) - assert persistence == [(34.2, 34.974)] + np.testing.assert_array_equal(persistence, [(34.2, 34.974)]) persistence = gudhi.read_persistence_intervals_grouped_by_dimension(persistence_file='persistence_intervals_with_dimension.pers') assert persistence == {0: [(2.7, 3.7)], 1: [(9.6, 14.0), (3.0, float('Inf'))], 3: [(34.2, 34.974)]} -- cgit v1.2.3 From 7e8685458f684f347b2aa6a77a288c249c722f25 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 21 May 2019 18:08:38 +0200 Subject: Use numpy to speed computations for plot_persistence_density and fix code review --- src/cython/cython/persistence_graphical_tools.py | 30 +++++++++++------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/cython/cython/persistence_graphical_tools.py b/src/cython/cython/persistence_graphical_tools.py index 98cd355c..7bb69840 100644 --- a/src/cython/cython/persistence_graphical_tools.py +++ b/src/cython/cython/persistence_graphical_tools.py @@ -291,32 +291,30 @@ def plot_persistence_density(persistence=[], persistence_file='', from scipy.stats import kde if persistence_file is not '': + if dimension is None: + # All dimension case + dimension = -1 if path.isfile(persistence_file): - # Reset persistence - persistence = [] - diag = read_persistence_intervals_grouped_by_dimension(persistence_file=persistence_file) - for key in diag.keys(): - for persistence_interval in diag[key]: - persistence.append((key, persistence_interval)) + persistence_dim = read_persistence_intervals_in_dimension(persistence_file=persistence_file, + only_this_dim=dimension) + print(persistence_dim) else: print("file " + persistence_file + " not found.") return None - persistence_dim = [] - if dimension is not None: - persistence_dim = [(dim_interval) for dim_interval in persistence if (dim_interval[0] == dimension)] - else: - persistence_dim = persistence + if len(persistence) > 0: + persistence_dim = np.array([(dim_interval[1][0], dim_interval[1][1]) for dim_interval in persistence if (dim_interval[0] == dimension) or (dimension is None)]) + persistence_dim = persistence_dim[np.isfinite(persistence_dim[:,1])] if max_intervals > 0 and max_intervals < len(persistence_dim): # Sort by life time, then takes only the max_intervals elements - persistence_dim = sorted(persistence_dim, - key=lambda life_time: life_time[1][1]-life_time[1][0], - reverse=True)[:max_intervals] + persistence_dim = np.array(sorted(persistence_dim, + key=lambda life_time: life_time[1]-life_time[0], + reverse=True)[:max_intervals]) # Set as numpy array birth and death (remove undefined values - inf and NaN) - birth = np.asarray([(interval[1][0]) for interval in persistence_dim if (isfinite(interval[1][1]) and isfinite(interval[1][0]))]) - death = np.asarray([(interval[1][1]) for interval in persistence_dim if (isfinite(interval[1][1]) and isfinite(interval[1][0]))]) + birth = persistence_dim[:,0] + death = persistence_dim[:,1] # line display of equation : birth = death x = np.linspace(death.min(), birth.max(), 1000) -- cgit v1.2.3