summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Alpha_complex/doc/Intro_alpha_complex.h14
-rw-r--r--src/Alpha_complex/doc/alpha_complex_representation.ipe6
-rw-r--r--src/Alpha_complex/doc/alpha_complex_representation.pngbin14606 -> 19568 bytes
-rw-r--r--src/common/doc/installation.h2
-rw-r--r--src/common/doc/main_page.md4
-rw-r--r--src/python/doc/alpha_complex_sum.inc6
-rw-r--r--src/python/doc/alpha_complex_user.rst19
-rw-r--r--src/python/doc/cubical_complex_user.rst3
-rw-r--r--src/python/gudhi/cubical_complex.pyx26
-rw-r--r--src/python/gudhi/periodic_cubical_complex.pyx40
-rwxr-xr-xsrc/python/test/test_cubical_complex.py61
11 files changed, 135 insertions, 46 deletions
diff --git a/src/Alpha_complex/doc/Intro_alpha_complex.h b/src/Alpha_complex/doc/Intro_alpha_complex.h
index 3c32a1e6..a8b1a106 100644
--- a/src/Alpha_complex/doc/Intro_alpha_complex.h
+++ b/src/Alpha_complex/doc/Intro_alpha_complex.h
@@ -31,8 +31,8 @@ namespace alpha_complex {
* circumsphere is empty (the simplex is then said to be Gabriel), and as the minimum of the filtration
* values of the codimension 1 cofaces that make it not Gabriel otherwise.
*
- * All simplices that have a filtration value strictly greater than a given alpha squared value are not inserted into
- * the complex.
+ * All simplices that have a filtration value \f$ > \alpha^2 \f$ are removed from the Delaunay complex
+ * when creating the simplicial complex if it is specified.
*
* \image html "alpha_complex_representation.png" "Alpha-complex representation"
*
@@ -46,8 +46,8 @@ namespace alpha_complex {
* \cite cgal:s-gkd-19b from CGAL as template parameter.
*
* \remark
- * - When the simplicial complex is constructed with an infinite value of alpha, the complex is a Delaunay
- * complex.
+ * - When an \f$\alpha\f$-complex is constructed with an infinite value of \f$ \alpha^2 \f$, the complex is a Delaunay
+ * complex (with special filtration values).
* - For people only interested in the topology of the \ref alpha_complex (for instance persistence),
* \ref alpha_complex is equivalent to the \ref cech_complex and much smaller if you do not bound the radii.
* \ref cech_complex can still make sense in higher dimension precisely because you can bound the radii.
@@ -135,13 +135,13 @@ namespace alpha_complex {
*
* \subsubsection nondecreasing Non decreasing filtration values
*
- * As the squared radii computed by CGAL are an approximation, it might happen that these alpha squared values do not
- * quite define a proper filtration (i.e. non-decreasing with respect to inclusion).
+ * As the squared radii computed by CGAL are an approximation, it might happen that these \f$ \alpha^2 \f$ values do
+ * not quite define a proper filtration (i.e. non-decreasing with respect to inclusion).
* We fix that up by calling `SimplicialComplexForAlpha::make_filtration_non_decreasing()`.
*
* \subsubsection pruneabove Prune above given filtration value
*
- * The simplex tree is pruned from the given maximum alpha squared value (cf.
+ * The simplex tree is pruned from the given maximum \f$ \alpha^2 \f$ value (cf.
* `SimplicialComplexForAlpha::prune_above_filtration()`).
* In the following example, the value is given by the user as argument of the program.
*
diff --git a/src/Alpha_complex/doc/alpha_complex_representation.ipe b/src/Alpha_complex/doc/alpha_complex_representation.ipe
index e8096b93..40ff1d0f 100644
--- a/src/Alpha_complex/doc/alpha_complex_representation.ipe
+++ b/src/Alpha_complex/doc/alpha_complex_representation.ipe
@@ -1,7 +1,7 @@
<?xml version="1.0"?>
<!DOCTYPE ipe SYSTEM "ipe.dtd">
-<ipe version="70107" creator="Ipe 7.1.10">
-<info created="D:20150603143945" modified="D:20160404172133"/>
+<ipe version="70206" creator="Ipe 7.2.7">
+<info created="D:20150603143945" modified="D:20200110100102"/>
<ipestyle name="basic">
<symbol name="arrow/arc(spx)">
<path stroke="sym-stroke" fill="sym-stroke" pen="sym-pen">
@@ -305,7 +305,7 @@ h
108.275 743.531 m
166.45 743.531 l
</path>
-<text matrix="1 0 0 1 142.618 -109.867" transformations="translations" pos="127.397 746.763" stroke="darkgray" type="label" width="6.41" height="4.289" depth="0" valign="baseline">$\alpha$</text>
+<text matrix="1 0 0 1 126.618 -109.867" transformations="translations" pos="127.397 746.763" stroke="darkgray" type="label" width="45.707" height="9.041" depth="1.32" valign="baseline" style="math">\alpha = \sqrt{32.0}</text>
<use matrix="1 0 0 1 -209.478 12.0238" name="mark/fdisk(sfx)" pos="300 720" size="normal" stroke="black" fill="white"/>
<use matrix="1 0 0 1 -210.178 22.1775" name="mark/fdisk(sfx)" pos="280 660" size="normal" stroke="black" fill="white"/>
<use matrix="1 0 0 1 -210.178 22.1775" name="mark/fdisk(sfx)" pos="370 690" size="normal" stroke="black" fill="white"/>
diff --git a/src/Alpha_complex/doc/alpha_complex_representation.png b/src/Alpha_complex/doc/alpha_complex_representation.png
index 7b81cd69..5ebb1e75 100644
--- a/src/Alpha_complex/doc/alpha_complex_representation.png
+++ b/src/Alpha_complex/doc/alpha_complex_representation.png
Binary files differ
diff --git a/src/common/doc/installation.h b/src/common/doc/installation.h
index d70a2efa..ce2c5448 100644
--- a/src/common/doc/installation.h
+++ b/src/common/doc/installation.h
@@ -33,7 +33,7 @@ make \endverbatim
* \subsection testsuites Test suites
* To test your build, run the following command in a terminal:
* \verbatim make test \endverbatim
- * `make test` is using <a href="https://cmake.org/cmake/help/latest/manual/ctest.1.html">Ctest<\a> (CMake test driver
+ * `make test` is using <a href="https://cmake.org/cmake/help/latest/manual/ctest.1.html">Ctest</a> (CMake test driver
* program). If some of the tests are failing, please send us the result of the following command:
* \verbatim ctest --output-on-failure \endverbatim
*
diff --git a/src/common/doc/main_page.md b/src/common/doc/main_page.md
index e8d11fdf..0b4bfb7a 100644
--- a/src/common/doc/main_page.md
+++ b/src/common/doc/main_page.md
@@ -43,8 +43,8 @@
The filtration value of each simplex is computed as the square of the circumradius of the simplex if the
circumsphere is empty (the simplex is then said to be Gabriel), and as the minimum of the filtration
values of the codimension 1 cofaces that make it not Gabriel otherwise.
- All simplices that have a filtration value strictly greater than a given alpha squared value are not inserted into
- the complex.<br>
+ All simplices that have a filtration value \f$ > \alpha^2 \f$ are removed from the Delaunay complex
+ when creating the simplicial complex if it is specified.<br>
For performances reasons, it is advised to use \ref cgal &ge; 5.0.0.
</td>
<td width="15%">
diff --git a/src/python/doc/alpha_complex_sum.inc b/src/python/doc/alpha_complex_sum.inc
index c5ba9dc7..a1184663 100644
--- a/src/python/doc/alpha_complex_sum.inc
+++ b/src/python/doc/alpha_complex_sum.inc
@@ -9,9 +9,9 @@
| | circumradius of the simplex if the circumsphere is empty (the simplex | :Copyright: MIT (`GPL v3 </licensing/>`_) |
| | is then said to be Gabriel), and as the minimum of the filtration | |
| | values of the codimension 1 cofaces that make it not Gabriel | :Requires: `Eigen <installation.html#eigen>`__ :math:`\geq` 3.1.0 and `CGAL <installation.html#cgal>`__ :math:`\geq` 4.11.0 |
- | | otherwise. All simplices that have a filtration value strictly | |
- | | greater than a given alpha squared value are not inserted into the | |
- | | complex. | |
+ | | otherwise. All simplices that have a filtration value | |
+ | | :math:`> \alpha^2` are removed from the Delaunay complex | |
+ | | when creating the simplicial complex if it is specified. | |
| | | |
| | This package requires having CGAL version 4.7 or higher (4.8.1 is | |
| | advised for better performance). | |
diff --git a/src/python/doc/alpha_complex_user.rst b/src/python/doc/alpha_complex_user.rst
index b7e69e12..60319e84 100644
--- a/src/python/doc/alpha_complex_user.rst
+++ b/src/python/doc/alpha_complex_user.rst
@@ -16,7 +16,8 @@ Definition
Remarks
^^^^^^^
-When an :math:`\alpha`-complex is constructed with an infinite value of :math:`\alpha`, the complex is a Delaunay complex (with special filtration values).
+When an :math:`\alpha`-complex is constructed with an infinite value of :math:`\alpha^2`,
+the complex is a Delaunay complex (with special filtration values).
Example from points
-------------------
@@ -137,19 +138,20 @@ sets the filtration value (0 in case of a vertex - propagation will have no effe
Non decreasing filtration values
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-As the squared radii computed by CGAL are an approximation, it might happen that these alpha squared values do not
-quite define a proper filtration (i.e. non-decreasing with respect to inclusion).
+As the squared radii computed by CGAL are an approximation, it might happen that these
+:math:`\alpha^2` values do not quite define a proper filtration (i.e. non-decreasing with
+respect to inclusion).
We fix that up by calling :func:`~gudhi.SimplexTree.make_filtration_non_decreasing` (cf.
`C++ version <http://gudhi.gforge.inria.fr/doc/latest/index.html>`_).
Prune above given filtration value
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-The simplex tree is pruned from the given maximum alpha squared value (cf.
+The simplex tree is pruned from the given maximum :math:`\alpha^2` value (cf.
:func:`~gudhi.SimplexTree.prune_above_filtration`). Note that this does not provide any kind
of speed-up, since we always first build the full filtered complex, so it is recommended not to use
:paramref:`~gudhi.AlphaComplex.create_simplex_tree.max_alpha_square`.
-In the following example, a threshold of 59 is used.
+In the following example, a threshold of :math:`\alpha^2 = 32.0` is used.
Example from OFF file
@@ -166,7 +168,7 @@ Then, it is asked to display information about the alpha complex:
import gudhi
alpha_complex = gudhi.AlphaComplex(off_file=gudhi.__root_source_dir__ + \
'/data/points/alphacomplexdoc.off')
- simplex_tree = alpha_complex.create_simplex_tree(max_alpha_square=59.0)
+ simplex_tree = alpha_complex.create_simplex_tree(max_alpha_square=32.0)
result_str = 'Alpha complex is of dimension ' + repr(simplex_tree.dimension()) + ' - ' + \
repr(simplex_tree.num_simplices()) + ' simplices - ' + \
repr(simplex_tree.num_vertices()) + ' vertices.'
@@ -179,7 +181,7 @@ the program output is:
.. testoutput::
- Alpha complex is of dimension 2 - 23 simplices - 7 vertices.
+ Alpha complex is of dimension 2 - 20 simplices - 7 vertices.
[0] -> 0.00
[1] -> 0.00
[2] -> 0.00
@@ -200,9 +202,6 @@ the program output is:
[4, 6] -> 22.74
[4, 5, 6] -> 22.74
[3, 6] -> 30.25
- [2, 6] -> 36.50
- [2, 3, 6] -> 36.50
- [2, 4, 6] -> 37.24
CGAL citations
==============
diff --git a/src/python/doc/cubical_complex_user.rst b/src/python/doc/cubical_complex_user.rst
index b13b500e..56cf0170 100644
--- a/src/python/doc/cubical_complex_user.rst
+++ b/src/python/doc/cubical_complex_user.rst
@@ -142,8 +142,7 @@ 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],
+ periodic_cc = pcc(top_dimensional_cells = [[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..b7047d4f 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=''):
+ perseus_file=''):
"""CubicalComplex constructor from dimensions and
top_dimensional_cells or from a Perseus-style file name.
@@ -58,6 +58,12 @@ cdef class CubicalComplex:
Or
+ :param top_dimensional_cells: A multidimensional array of cells
+ filtration values.
+ :type top_dimensional_cells: anything convertible to a numpy ndarray
+
+ Or
+
:param perseus_file: A Perseus-style file name.
:type perseus_file: string
"""
@@ -65,9 +71,19 @@ cdef class CubicalComplex:
# The real cython constructor
def __cinit__(self, dimensions=None, top_dimensional_cells=None,
perseus_file=''):
- if (dimensions is not None) and (top_dimensional_cells is not None) and (perseus_file == ''):
+ if ((dimensions is not None) and (top_dimensional_cells is not None)
+ and (perseus_file == '')):
+ self.thisptr = new Bitmap_cubical_complex_base_interface(dimensions, top_dimensional_cells)
+ elif ((dimensions is None) and (top_dimensional_cells is not None)
+ and (perseus_file == '')):
+ top_dimensional_cells = np.array(top_dimensional_cells,
+ copy = False,
+ order = 'F')
+ dimensions = top_dimensional_cells.shape
+ top_dimensional_cells = top_dimensional_cells.ravel(order='F')
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 (perseus_file != '')):
if os.path.isfile(perseus_file):
self.thisptr = new Bitmap_cubical_complex_base_interface(str.encode(perseus_file))
else:
@@ -184,4 +200,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..eb96ab5f 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=''):
+ periodic_dimensions=None, perseus_file=''):
"""PeriodicCubicalComplex constructor from dimensions and
top_dimensional_cells or from a Perseus-style file name.
@@ -60,6 +60,14 @@ cdef class PeriodicCubicalComplex:
Or
+ :param top_dimensional_cells: A multidimensional array of cells
+ filtration values.
+ :type top_dimensional_cells: anything convertible to a 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
"""
@@ -67,16 +75,32 @@ cdef class PeriodicCubicalComplex:
# The real cython constructor
def __cinit__(self, dimensions=None, top_dimensional_cells=None,
periodic_dimensions=None, perseus_file=''):
- if (dimensions is not None) and (top_dimensional_cells is not None) and (periodic_dimensions is not None) and (perseus_file == ''):
- 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 != ''):
+ 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 not None)
+ and (periodic_dimensions is not None) and (perseus_file == '')):
+ top_dimensional_cells = np.array(top_dimensional_cells,
+ copy = False,
+ order = 'F')
+ dimensions = top_dimensional_cells.shape
+ top_dimensional_cells = top_dimensional_cells.ravel(order='F')
+ 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 != '')):
if os.path.isfile(perseus_file):
self.thisptr = new Periodic_cubical_complex_base_interface(str.encode(perseus_file))
else:
print("file " + perseus_file + " not found.")
else:
- print("CubicalComplex can be constructed from dimensions and "
- "top_dimensional_cells or from a Perseus-style file name.")
+ print("CubicalComplex can be constructed from dimensions, "
+ "top_dimensional_cells and periodic_dimensions, or from "
+ "top_dimensional_cells and periodic_dimensions or from "
+ "a Perseus-style file name.")
def __dealloc__(self):
if self.thisptr != NULL:
@@ -186,4 +210,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..121da12a 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
+from gudhi import CubicalComplex, PeriodicCubicalComplex
+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(
+ top_dimensional_cells=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(
+ top_dimensional_cells=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
@@ -96,3 +121,29 @@ def test_dimension_file_constructor():
assert cub.__is_persistence_defined() == True
assert cub.betti_numbers() == [1, 0, 0]
assert cub.persistent_betti_numbers(0, 1000) == [1, 0, 0]
+
+def test_connected_sublevel_sets():
+ array_cells = np.array([[3, 3], [2, 2], [4, 4]])
+ linear_cells = [3, 3, 2, 2, 4, 4]
+ dimensions = [2, 3]
+ periodic_dimensions = [False, False]
+ # with a numpy array version
+ cub = CubicalComplex(top_dimensional_cells = array_cells)
+ assert cub.persistence() == [(0, (2.0, float("inf")))]
+ assert cub.betti_numbers() == [1, 0, 0]
+ # with vector of dimensions
+ cub = CubicalComplex(dimensions = dimensions,
+ top_dimensional_cells = linear_cells)
+ assert cub.persistence() == [(0, (2.0, float("inf")))]
+ assert cub.betti_numbers() == [1, 0, 0]
+ # periodic with a numpy array version
+ cub = PeriodicCubicalComplex(top_dimensional_cells = array_cells,
+ periodic_dimensions = periodic_dimensions)
+ assert cub.persistence() == [(0, (2.0, float("inf")))]
+ assert cub.betti_numbers() == [1, 0, 0]
+ # periodic with vector of dimensions
+ cub = PeriodicCubicalComplex(dimensions = dimensions,
+ top_dimensional_cells = linear_cells,
+ periodic_dimensions = periodic_dimensions)
+ assert cub.persistence() == [(0, (2.0, float("inf")))]
+ assert cub.betti_numbers() == [1, 0, 0]