From b5510c0c362cd2bef79d82fd9809e654aea73957 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 16 Dec 2019 23:10:24 +0100 Subject: Add numpy ndarray to init a cubical and a periodic one --- src/python/doc/cubical_complex_user.rst | 4 +-- src/python/gudhi/cubical_complex.pyx | 27 ++++++++++++++----- src/python/gudhi/periodic_cubical_complex.pyx | 39 ++++++++++++++++++++++----- src/python/test/test_cubical_complex.py | 33 ++++++++++++++++++++--- 4 files changed, 84 insertions(+), 19 deletions(-) diff --git a/src/python/doc/cubical_complex_user.rst b/src/python/doc/cubical_complex_user.rst index b13b500e..21806038 100644 --- a/src/python/doc/cubical_complex_user.rst +++ b/src/python/doc/cubical_complex_user.rst @@ -142,8 +142,8 @@ Or it can be defined as follows: .. testcode:: from gudhi import PeriodicCubicalComplex as pcc - periodic_cc = pcc(dimensions=[3,3], - top_dimensional_cells= [0, 0, 0, 0, 1, 0, 0, 0, 0], + from numpy import array as np_array + periodic_cc = pcc(numpy_array = np_array([[0, 0, 0], [0, 1, 0], [0, 0, 0]]), periodic_dimensions=[True, False]) result_str = 'Periodic cubical complex is of dimension ' + repr(periodic_cc.dimension()) + ' - ' + \ repr(periodic_cc.num_simplices()) + ' simplices.' diff --git a/src/python/gudhi/cubical_complex.pyx b/src/python/gudhi/cubical_complex.pyx index 011c407c..2ec0146a 100644 --- a/src/python/gudhi/cubical_complex.pyx +++ b/src/python/gudhi/cubical_complex.pyx @@ -5,7 +5,7 @@ from libcpp.string cimport string from libcpp cimport bool import os -from numpy import array as np_array +import numpy as np # This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. # See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. @@ -47,7 +47,7 @@ cdef class CubicalComplex: # Fake constructor that does nothing but documenting the constructor def __init__(self, dimensions=None, top_dimensional_cells=None, - perseus_file=''): + numpy_array=None, perseus_file=''): """CubicalComplex constructor from dimensions and top_dimensional_cells or from a Perseus-style file name. @@ -58,16 +58,31 @@ cdef class CubicalComplex: Or + :param numpy_array: Filtration values in a d-array. + :type numpy_array: numpy ndarray + + Or + :param perseus_file: A Perseus-style file name. :type perseus_file: string """ # The real cython constructor def __cinit__(self, dimensions=None, top_dimensional_cells=None, - perseus_file=''): - if (dimensions is not None) and (top_dimensional_cells is not None) and (perseus_file == ''): + numpy_array=None, perseus_file=''): + if ((dimensions is not None) and (top_dimensional_cells is not None) + and (numpy_array is None) and (perseus_file == '')): self.thisptr = new Bitmap_cubical_complex_base_interface(dimensions, top_dimensional_cells) - elif (dimensions is None) and (top_dimensional_cells is None) and (perseus_file != ''): + elif ((dimensions is None) and (top_dimensional_cells is None) + and (numpy_array is not None) and (perseus_file == '')): + if isinstance(numpy_array, np.ndarray): + dimensions = list(numpy_array.shape) + top_dimensional_cells = numpy_array.flatten().tolist() + self.thisptr = new Bitmap_cubical_complex_base_interface(dimensions, top_dimensional_cells) + else: + print("numpy_array is not an instance of ndarray. It is a " + type(numpy_array)) + elif ((dimensions is None) and (top_dimensional_cells is None) + and (numpy_array is None) and (perseus_file != '')): if os.path.isfile(perseus_file): self.thisptr = new Bitmap_cubical_complex_base_interface(str.encode(perseus_file)) else: @@ -184,4 +199,4 @@ cdef class CubicalComplex: else: print("intervals_in_dim function requires persistence function" " to be launched first.") - return np_array(intervals_result) + return np.array(intervals_result) diff --git a/src/python/gudhi/periodic_cubical_complex.pyx b/src/python/gudhi/periodic_cubical_complex.pyx index c89055db..8318b7d3 100644 --- a/src/python/gudhi/periodic_cubical_complex.pyx +++ b/src/python/gudhi/periodic_cubical_complex.pyx @@ -5,7 +5,7 @@ from libcpp.string cimport string from libcpp cimport bool import os -from numpy import array as np_array +import numpy as np # This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. # See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. @@ -47,7 +47,7 @@ cdef class PeriodicCubicalComplex: # Fake constructor that does nothing but documenting the constructor def __init__(self, dimensions=None, top_dimensional_cells=None, - periodic_dimensions=None, perseus_file=''): + numpy_array=None, periodic_dimensions=None, perseus_file=''): """PeriodicCubicalComplex constructor from dimensions and top_dimensional_cells or from a Perseus-style file name. @@ -60,16 +60,41 @@ cdef class PeriodicCubicalComplex: Or + :param numpy_array: Filtration values in a d-array. + :type numpy_array: numpy ndarray + :param periodic_dimensions: A list of top dimensional cells periodicity value. + :type periodic_dimensions: list of boolean + + Or + :param perseus_file: A Perseus-style file name. :type perseus_file: string """ # The real cython constructor def __cinit__(self, dimensions=None, top_dimensional_cells=None, - periodic_dimensions=None, perseus_file=''): - if (dimensions is not None) and (top_dimensional_cells is not None) and (periodic_dimensions is not None) and (perseus_file == ''): - self.thisptr = new Periodic_cubical_complex_base_interface(dimensions, top_dimensional_cells, periodic_dimensions) - elif (dimensions is None) and (top_dimensional_cells is None) and (periodic_dimensions is None) and (perseus_file != ''): + numpy_array=None, periodic_dimensions=None, + perseus_file=''): + if ((dimensions is not None) and (top_dimensional_cells is not None) + and (numpy_array is None) and (periodic_dimensions is not None) + and (perseus_file == '')): + self.thisptr = new Periodic_cubical_complex_base_interface(dimensions, + top_dimensional_cells, + periodic_dimensions) + elif ((dimensions is None) and (top_dimensional_cells is None) + and (numpy_array is not None) and (periodic_dimensions is not None) + and (perseus_file == '')): + if isinstance(numpy_array, np.ndarray): + dimensions = list(numpy_array.shape) + top_dimensional_cells = numpy_array.flatten().tolist() + self.thisptr = new Periodic_cubical_complex_base_interface(dimensions, + top_dimensional_cells, + periodic_dimensions) + else: + print("numpy_array is not an instance of ndarray. It is a " + type(numpy_array)) + elif ((dimensions is None) and (top_dimensional_cells is None) + and (numpy_array is None) and (periodic_dimensions is None) + and (perseus_file != '')): if os.path.isfile(perseus_file): self.thisptr = new Periodic_cubical_complex_base_interface(str.encode(perseus_file)) else: @@ -186,4 +211,4 @@ cdef class PeriodicCubicalComplex: else: print("intervals_in_dim function requires persistence function" " to be launched first.") - return np_array(intervals_result) + return np.array(intervals_result) diff --git a/src/python/test/test_cubical_complex.py b/src/python/test/test_cubical_complex.py index 68f54fbe..a37d7821 100755 --- a/src/python/test/test_cubical_complex.py +++ b/src/python/test/test_cubical_complex.py @@ -1,4 +1,5 @@ from gudhi import CubicalComplex +import numpy as np """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. @@ -56,7 +57,7 @@ def test_dimension_or_perseus_file_constructor(): assert cub.__is_persistence_defined() == False -def test_dimension_simple_constructor(): +def simple_constructor(cub): cub = CubicalComplex( dimensions=[3, 3], top_dimensional_cells=[1, 2, 3, 4, 5, 6, 7, 8, 9] ) @@ -67,12 +68,22 @@ def test_dimension_simple_constructor(): assert cub.betti_numbers() == [1, 0, 0] assert cub.persistent_betti_numbers(0, 1000) == [0, 0, 0] - -def test_user_case_simple_constructor(): +def test_simple_constructor_from_top_cells(): cub = CubicalComplex( dimensions=[3, 3], - top_dimensional_cells=[float("inf"), 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0], + top_dimensional_cells=[1, 2, 3, 4, 5, 6, 7, 8, 9], ) + simple_constructor(cub) + +def test_simple_constructor_from_numpy_array(): + cub = CubicalComplex( + numpy_array=np.array([[1, 2, 3], + [4, 5, 6], + [7, 8, 9]]) + ) + simple_constructor(cub) + +def user_case_simple_constructor(cub): assert cub.__is_defined() == True assert cub.__is_persistence_defined() == False assert cub.persistence() == [(1, (0.0, 1.0)), (0, (0.0, float("inf")))] @@ -83,6 +94,20 @@ def test_user_case_simple_constructor(): ) assert other_cub.persistence() == [(1, (0.0, 1.0)), (0, (0.0, float("inf")))] +def test_user_case_simple_constructor_from_top_cells(): + cub = CubicalComplex( + dimensions=[3, 3], + top_dimensional_cells=[float("inf"), 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0], + ) + user_case_simple_constructor(cub) + +def test_user_case_simple_constructor_from_numpy_array(): + cub = CubicalComplex( + numpy_array=np.array([[float("inf"), 0.0, 0.0], + [0.0, 1.0, 0.0], + [0.0, 0.0, 0.0]]) + ) + user_case_simple_constructor(cub) def test_dimension_file_constructor(): # Create test file -- cgit v1.2.3