From 11aebf8f6ac0fe32c7909cedda6b383329f1fd6d Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Sun, 1 Nov 2020 22:02:59 +0100 Subject: added code, doc and test --- src/python/CMakeLists.txt | 3 +- src/python/gudhi/differentiation/__init__.py | 3 + src/python/gudhi/differentiation/tensorflow.py | 251 +++++++++++++++++++++++++ src/python/test/simplextree.txt | 21 +++ src/python/test/test_diff.py | 41 ++++ 5 files changed, 318 insertions(+), 1 deletion(-) create mode 100644 src/python/gudhi/differentiation/__init__.py create mode 100644 src/python/gudhi/differentiation/tensorflow.py create mode 100644 src/python/test/simplextree.txt create mode 100644 src/python/test/test_diff.py (limited to 'src') diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index c09996fe..44b6a93c 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -252,7 +252,8 @@ if(PYTHONINTERP_FOUND) # Other .py files file(COPY "gudhi/persistence_graphical_tools.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") file(COPY "gudhi/representations" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi/") - file(COPY "gudhi/wasserstein" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") + file(COPY "gudhi/wasserstein" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") + file(COPY "gudhi/differentiation" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") file(COPY "gudhi/point_cloud" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") file(COPY "gudhi/clustering" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi" FILES_MATCHING PATTERN "*.py") file(COPY "gudhi/weighted_rips_complex.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") diff --git a/src/python/gudhi/differentiation/__init__.py b/src/python/gudhi/differentiation/__init__.py new file mode 100644 index 00000000..0260ed97 --- /dev/null +++ b/src/python/gudhi/differentiation/__init__.py @@ -0,0 +1,3 @@ +from .tensorflow import * + +__all__ = ["SimplexTreeModel", "RipsModel", "CubicalModel"] diff --git a/src/python/gudhi/differentiation/tensorflow.py b/src/python/gudhi/differentiation/tensorflow.py new file mode 100644 index 00000000..d3a35e62 --- /dev/null +++ b/src/python/gudhi/differentiation/tensorflow.py @@ -0,0 +1,251 @@ +import numpy as np +import tensorflow as tf +import tensorflow_addons as tfa +from ..simplex_tree import SimplexTree +from ..rips_complex import RipsComplex +from ..cubical_complex import CubicalComplex + +# In this file, we write functions based on the Gudhi library that compute persistence diagrams associated to +# different filtrations (lower star, Rips, cubical), as well as the corresponding positive and negative +# simplices. We also wrap these functions into Tensorflow models. + + + +######################################### +# Lower star filtration on simplex tree # +######################################### + +# The parameters of the model are the vertex function values of the simplex tree. + +def _SimplexTree(stbase, fct, dim, card): + # Parameters: stbase (array containing the name of the file where the simplex tree is located) + # fct (function values on the vertices of stbase), + # dim (homological dimension), + # card (number of persistence diagram points, sorted by distance-to-diagonal) + + # Copy stbase in another simplex tree st + st = SimplexTree() + f = open(stbase[0], "r") + for line in f: + ints = line.split(" ") + s = [int(v) for v in ints[:-1]] + st.insert(s, -1e10) + f.close() + + # Assign new filtration values + for i in range(st.num_vertices()): + st.assign_filtration([i], fct[i]) + st.make_filtration_non_decreasing() + + # Compute persistence diagram + dgm = st.persistence() + + # Get vertex pairs for optimization. First, get all simplex pairs + pairs = st.persistence_pairs() + + # Then, loop over all simplex pairs + indices, pers = [], [] + for s1, s2 in pairs: + # Select pairs with good homological dimension and finite lifetime + if len(s1) == dim+1 and len(s2) > 0: + # Get IDs of the vertices corresponding to the filtration values of the simplices + l1, l2 = np.array(s1), np.array(s2) + i1 = l1[np.argmax(fct[l1])] + i2 = l2[np.argmax(fct[l2])] + indices.append(i1) + indices.append(i2) + # Compute lifetime + pers.append(st.filtration(s2) - st.filtration(s1)) + + # Sort vertex pairs wrt lifetime + perm = np.argsort(pers) + indices = list(np.reshape(indices, [-1,2])[perm][::-1,:].flatten()) + + # Pad vertex pairs + indices = indices[:2*card] + [0 for _ in range(0,max(0,2*card-len(indices)))] + return list(np.array(indices, dtype=np.int32)) + +class SimplexTreeModel(tf.keras.Model): + """ + TensorFlow model for computing lower-star persistence out of a simplex tree. + + Attributes: + F (TensorFlow variable): filter function values over the vertices of the simplex tree + stbase (string): path to the file containing the simplex tree + card (int): maximum number of points in the persistence diagram + dim (int): homology dimension + """ + def __init__(self, F, stbase="simplextree.txt", dim=0, card=50): + super(SimplexTreeModel, self).__init__() + self.F = F + self.dim = dim + self.card = card + self.st = stbase + + def call(self): + d, c = self.dim, self.card + st, fct = self.st, self.F + + # Turn STPers into a numpy function + SimplexTreeTF = lambda fct: tf.numpy_function(_SimplexTree, [np.array([st], dtype=str), fct, d, c], [tf.int32 for _ in range(2*c)]) + + # Don't try to compute gradients for the vertex pairs + fcts = tf.reshape(fct, [1, self.F.shape[0]]) + inds = tf.nest.map_structure(tf.stop_gradient, tf.map_fn(SimplexTreeTF, + fcts, dtype=[tf.int32 for _ in range(2*c)])) + + # Get persistence diagram + self.dgm = tf.reshape(tf.gather_nd(self.F, inds), [c,2]) + return self.dgm + + + + + + + + + + +############################ +# Vietoris-Rips filtration # +############################ + +# The parameters of the model are the point coordinates. + +def _Rips(DX, mel, dim, card): + # Parameters: DX (distance matrix), + # mel (maximum edge length for Rips filtration), + # dim (homological dimension), + # card (number of persistence diagram points, sorted by distance-to-diagonal) + + # Compute the persistence pairs with Gudhi + rc = RipsComplex(distance_matrix=DX, max_edge_length=mel) + st = rc.create_simplex_tree(max_dimension=dim+1) + dgm = st.persistence() + pairs = st.persistence_pairs() + + # Retrieve vertices v_a and v_b by picking the ones achieving the maximal + # distance among all pairwise distances between the simplex vertices + indices, pers = [], [] + for s1, s2 in pairs: + if len(s1) == dim+1 and len(s2) > 0: + l1, l2 = np.array(s1), np.array(s2) + i1 = [s1[v] for v in np.unravel_index(np.argmax(DX[l1,:][:,l1]),[len(s1), len(s1)])] + i2 = [s2[v] for v in np.unravel_index(np.argmax(DX[l2,:][:,l2]),[len(s2), len(s2)])] + indices += i1 + indices += i2 + pers.append(st.filtration(s2) - st.filtration(s1)) + + # Sort points with distance-to-diagonal + perm = np.argsort(pers) + indices = list(np.reshape(indices, [-1,4])[perm][::-1,:].flatten()) + + # Output indices + indices = indices[:4*card] + [0 for _ in range(0,max(0,4*card-len(indices)))] + return list(np.array(indices, dtype=np.int32)) + +class RipsModel(tf.keras.Model): + """ + TensorFlow model for computing Rips persistence out of a point cloud. + + Attributes: + X (TensorFlow variable): point cloud of shape [number of points, number of dimensions] + mel (float): maximum edge length for the Rips complex + card (int): maximum number of points in the persistence diagram + dim (int): homology dimension + """ + def __init__(self, X, mel=12, dim=1, card=50): + super(RipsModel, self).__init__() + self.X = X + self.mel = mel + self.dim = dim + self.card = card + + def call(self): + m, d, c = self.mel, self.dim, self.card + + # Compute distance matrix + DX = tfa.losses.metric_learning.pairwise_distance(self.X) + DXX = tf.reshape(DX, [1, DX.shape[0], DX.shape[1]]) + + # Turn numpy function into tensorflow function + RipsTF = lambda DX: tf.numpy_function(_Rips, [DX, m, d, c], [tf.int32 for _ in range(4*c)]) + + # Compute vertices associated to positive and negative simplices + # Don't compute gradient for this operation + ids = tf.nest.map_structure(tf.stop_gradient, tf.map_fn(RipsTF,DXX,dtype=[tf.int32 for _ in range(4*c)])) + + # Get persistence diagram by simply picking the corresponding entries in the distance matrix + dgm = tf.reshape(tf.gather_nd(DX, tf.reshape(ids, [2*c,2])), [c,2]) + return dgm + + + + + + + + + +###################### +# Cubical filtration # +###################### + +# The parameters of the model are the pixel values. + +def _Cubical(X, dim, card): + # Parameters: X (image), + # dim (homological dimension), + # card (number of persistence diagram points, sorted by distance-to-diagonal) + + # Compute the persistence pairs with Gudhi + cc = CubicalComplex(dimensions=X.shape, top_dimensional_cells=X.flatten()) + cc.persistence() + cof = cc.cofaces_of_persistence_pairs()[0][dim] + + # Sort points with distance-to-diagonal + Xs = X.shape + pers = [X[np.unravel_index(cof[idx,1], Xs)] - X[np.unravel_index(cof[idx,0], Xs)] for idx in range(len(cof))] + perm = np.argsort(pers) + cof = cof[perm[::-1]] + + # Retrieve and ouput image indices/pixels corresponding to positive and negative simplices + D = len(Xs) + ocof = np.array([0 for _ in range(D*card*2)]) + count = 0 + for idx in range(0,min(2*card, 2*cof.shape[0]),2): + ocof[D*idx:D*(idx+1)] = np.unravel_index(cof[count,0], Xs) + ocof[D*(idx+1):D*(idx+2)] = np.unravel_index(cof[count,1], Xs) + count += 1 + return list(np.array(ocof, dtype=np.int32)) + +class CubicalModel(tf.keras.Model): + """ + TensorFlow model for computing cubical persistence out of a cubical complex. + + Attributes: + X (TensorFlow variable): pixel values of the cubical complex + card (int): maximum number of points in the persistence diagram + dim (int): homology dimension + """ + def __init__(self, X, dim=1, card=50): + super(CubicalModel, self).__init__() + self.X = X + self.dim = dim + self.card = card + + def call(self): + d, c, D = self.dim, self.card, len(self.X.shape) + XX = tf.reshape(self.X, [1, self.X.shape[0], self.X.shape[1]]) + + # Turn numpy function into tensorflow function + CbTF = lambda X: tf.numpy_function(_Cubical, [X, d, c], [tf.int32 for _ in range(2*D*c)]) + + # Compute pixels associated to positive and negative simplices + # Don't compute gradient for this operation + inds = tf.nest.map_structure(tf.stop_gradient, tf.map_fn(CbTF,XX,dtype=[tf.int32 for _ in range(2*D*c)])) + + # Get persistence diagram by simply picking the corresponding entries in the image + dgm = tf.reshape(tf.gather_nd(self.X, tf.reshape(inds, [-1,D])), [-1,2]) + return dgm diff --git a/src/python/test/simplextree.txt b/src/python/test/simplextree.txt new file mode 100644 index 00000000..e0dfcdd9 --- /dev/null +++ b/src/python/test/simplextree.txt @@ -0,0 +1,21 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +0 1 +1 2 +2 3 +3 4 +4 5 +5 6 +6 7 +7 8 +8 9 +9 10 diff --git a/src/python/test/test_diff.py b/src/python/test/test_diff.py new file mode 100644 index 00000000..56277b74 --- /dev/null +++ b/src/python/test/test_diff.py @@ -0,0 +1,41 @@ +from gudhi.differentiation import * +import numpy as np +import tensorflow as tf + +def test_rips_diff(): + + Xinit = np.array([[1.,1.],[2.,2.]], dtype=np.float32) + X = tf.Variable(initial_value=Xinit, trainable=True) + model = RipsModel(X=X, mel=2., dim=0, card=10) + + with tf.GradientTape() as tape: + dgm = model.call() + loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) + grads = tape.gradient(loss, [X]) + assert np.abs(grads[0].numpy()-np.array([[-.5,-.5],[.5,.5]])).sum() <= 1e-6 + + +def test_cubical_diff(): + + Xinit = np.array([[0.,2.,2.],[2.,2.,2.],[2.,2.,1.]], dtype=np.float32) + X = tf.Variable(initial_value=Xinit, trainable=True) + model = CubicalModel(X, dim=0, card=10) + + with tf.GradientTape() as tape: + dgm = model.call() + loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) + grads = tape.gradient(loss, [X]) + assert np.abs(grads[0].numpy()-np.array([[0.,0.,0.],[0.,.5,0.],[0.,0.,-.5]])).sum() <= 1e-6 + +def test_st_diff(): + + Finit = np.array([6.,4.,3.,4.,5.,4.,3.,2.,3.,4.,5.], dtype=np.float32) + F = tf.Variable(initial_value=Finit, trainable=True) + model = SimplexTreeModel(F, stbase="simplextree.txt", dim=0, card=10) + + with tf.GradientTape() as tape: + dgm = model.call() + loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) + grads = tape.gradient(loss, [F]) + assert np.array_equal(np.array(grads[0].indices), np.array([2,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])) + assert np.array_equal(np.array(grads[0].values), np.array([-1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])) -- cgit v1.2.3 From e1cb6364264e04404d49b8279edcd085bab3e5f4 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Mon, 7 Dec 2020 21:03:22 +0100 Subject: added doc --- src/python/CMakeLists.txt | 6 ++++++ src/python/doc/differentiation.rst | 18 ++++++++++++++++++ src/python/doc/differentiation_sum.inc | 14 ++++++++++++++ src/python/doc/img/ripsTF.png | Bin 0 -> 38696 bytes 4 files changed, 38 insertions(+) create mode 100644 src/python/doc/differentiation.rst create mode 100644 src/python/doc/differentiation_sum.inc create mode 100644 src/python/doc/img/ripsTF.png (limited to 'src') diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 45530e41..d9af9ec5 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -65,6 +65,7 @@ if(PYTHONINTERP_FOUND) 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}'representations', ") + set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'differentiation', ") set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'wasserstein', ") set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'point_cloud', ") set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'weighted_rips_complex', ") @@ -509,6 +510,11 @@ if(PYTHONINTERP_FOUND) add_gudhi_py_test(test_representations) endif() + # Differentiation + if(TENSORFLOW_FOUND) + add_gudhi_py_test(test_diff) + endif() + # Time Delay add_gudhi_py_test(test_time_delay) diff --git a/src/python/doc/differentiation.rst b/src/python/doc/differentiation.rst new file mode 100644 index 00000000..906a9965 --- /dev/null +++ b/src/python/doc/differentiation.rst @@ -0,0 +1,18 @@ +:orphan: + +.. To get rid of WARNING: document isn't included in any toctree + +====================== +Differentiation manual +====================== + +.. include:: differentiation_sum.inc + +In this module, we provide neural network models for computing persistent homology. In particular, we provide TensorFlow 2 models that allow to compute persistence diagrams from complexes available in the Gudhi library, including simplex trees, cubical complexes and Vietoris-Rips complexes. These models can be incorporated at each step of a given neural network architecture, and can be used in addition to `PersLay `_ to produce topological features. + +TensorFlow models +----------------- +.. automodule:: gudhi.differentiation + :members: + :special-members: + :show-inheritance: diff --git a/src/python/doc/differentiation_sum.inc b/src/python/doc/differentiation_sum.inc new file mode 100644 index 00000000..30188e0b --- /dev/null +++ b/src/python/doc/differentiation_sum.inc @@ -0,0 +1,14 @@ +.. table:: + :widths: 30 40 30 + + +------------------------------------------------------------------+----------------------------------------------------------------+-------------------------------------------------------------+ + | .. figure:: | Deep learning models for differentiating persistence diagrams. | :Author: Mathieu Carrière | + | img/ripsTF.png | | | + | | | :Since: GUDHI 3.1.0 | + | | | | + | | | :License: MIT | + | | | | + | | | :Requires: `TensorFlow 2 `_ | + +------------------------------------------------------------------+----------------------------------------------------------------+-------------------------------------------------------------+ + | * :doc:`differentiation` | + +------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------+ diff --git a/src/python/doc/img/ripsTF.png b/src/python/doc/img/ripsTF.png new file mode 100644 index 00000000..3c5c77a7 Binary files /dev/null and b/src/python/doc/img/ripsTF.png differ -- cgit v1.2.3 From 75721d65e870162df3ab0fa59c46fc357302a58a Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Tue, 15 Dec 2020 20:31:36 +0100 Subject: small change of model name + doc --- src/python/gudhi/differentiation/__init__.py | 2 +- src/python/gudhi/differentiation/tensorflow.py | 6 +++--- src/python/test/test_diff.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/differentiation/__init__.py b/src/python/gudhi/differentiation/__init__.py index 0260ed97..6793e904 100644 --- a/src/python/gudhi/differentiation/__init__.py +++ b/src/python/gudhi/differentiation/__init__.py @@ -1,3 +1,3 @@ from .tensorflow import * -__all__ = ["SimplexTreeModel", "RipsModel", "CubicalModel"] +__all__ = ["LowerStarSimplexTreeModel", "RipsModel", "CubicalModel"] diff --git a/src/python/gudhi/differentiation/tensorflow.py b/src/python/gudhi/differentiation/tensorflow.py index d3a35e62..372172c4 100644 --- a/src/python/gudhi/differentiation/tensorflow.py +++ b/src/python/gudhi/differentiation/tensorflow.py @@ -65,13 +65,13 @@ def _SimplexTree(stbase, fct, dim, card): indices = indices[:2*card] + [0 for _ in range(0,max(0,2*card-len(indices)))] return list(np.array(indices, dtype=np.int32)) -class SimplexTreeModel(tf.keras.Model): +class LowerStarSimplexTreeModel(tf.keras.Model): """ - TensorFlow model for computing lower-star persistence out of a simplex tree. + TensorFlow model for computing lower-star persistence out of a simplex tree. Since simplex trees cannot be easily encoded as TensorFlow variables, the model takes as input a path to a file containing the simplex tree simplices, and read it each time the simplex tree is required for computations. Attributes: F (TensorFlow variable): filter function values over the vertices of the simplex tree - stbase (string): path to the file containing the simplex tree + stbase (string): path to the file containing the simplex tree. Each line of the file should represent a simplex as a sequence of integers separated by spaces card (int): maximum number of points in the persistence diagram dim (int): homology dimension """ diff --git a/src/python/test/test_diff.py b/src/python/test/test_diff.py index 56277b74..de738579 100644 --- a/src/python/test/test_diff.py +++ b/src/python/test/test_diff.py @@ -31,7 +31,7 @@ def test_st_diff(): Finit = np.array([6.,4.,3.,4.,5.,4.,3.,2.,3.,4.,5.], dtype=np.float32) F = tf.Variable(initial_value=Finit, trainable=True) - model = SimplexTreeModel(F, stbase="simplextree.txt", dim=0, card=10) + model = LowerStarSimplexTreeModel(F, stbase="simplextree.txt", dim=0, card=10) with tf.GradientTape() as tape: dgm = model.call() -- cgit v1.2.3 From 6a062a47633694a5120e6991086d53b85898ac19 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Tue, 2 Feb 2021 20:55:37 +0100 Subject: small fix on Cubical --- src/python/gudhi/differentiation/tensorflow.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/differentiation/tensorflow.py b/src/python/gudhi/differentiation/tensorflow.py index 372172c4..b01a91f8 100644 --- a/src/python/gudhi/differentiation/tensorflow.py +++ b/src/python/gudhi/differentiation/tensorflow.py @@ -202,13 +202,17 @@ def _Cubical(X, dim, card): # Compute the persistence pairs with Gudhi cc = CubicalComplex(dimensions=X.shape, top_dimensional_cells=X.flatten()) cc.persistence() - cof = cc.cofaces_of_persistence_pairs()[0][dim] - - # Sort points with distance-to-diagonal - Xs = X.shape - pers = [X[np.unravel_index(cof[idx,1], Xs)] - X[np.unravel_index(cof[idx,0], Xs)] for idx in range(len(cof))] - perm = np.argsort(pers) - cof = cof[perm[::-1]] + try: + cof = cc.cofaces_of_persistence_pairs()[0][dim] + except IndexError: + cof = np.array([]) + + if len(cof) > 0: + # Sort points with distance-to-diagonal + Xs = X.shape + pers = [X[np.unravel_index(cof[idx,1], Xs)] - X[np.unravel_index(cof[idx,0], Xs)] for idx in range(len(cof))] + perm = np.argsort(pers) + cof = cof[perm[::-1]] # Retrieve and ouput image indices/pixels corresponding to positive and negative simplices D = len(Xs) -- cgit v1.2.3 From ec0f360a3a4dc11195e3351fd76bef012d233ee6 Mon Sep 17 00:00:00 2001 From: Mathieu Carrière Date: Wed, 3 Feb 2021 14:19:36 +0100 Subject: error fix --- src/python/gudhi/differentiation/tensorflow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/python/gudhi/differentiation/tensorflow.py b/src/python/gudhi/differentiation/tensorflow.py index b01a91f8..b7d68fae 100644 --- a/src/python/gudhi/differentiation/tensorflow.py +++ b/src/python/gudhi/differentiation/tensorflow.py @@ -76,7 +76,7 @@ class LowerStarSimplexTreeModel(tf.keras.Model): dim (int): homology dimension """ def __init__(self, F, stbase="simplextree.txt", dim=0, card=50): - super(SimplexTreeModel, self).__init__() + super(LowerStarSimplexTreeModel, self).__init__() self.F = F self.dim = dim self.card = card -- cgit v1.2.3 From c080d71bae7f2239f0a85910efc67e65da3ba36e Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Fri, 5 Feb 2021 00:19:03 +0100 Subject: avoid tensorflow addons dependency --- src/python/gudhi/differentiation/tensorflow.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/differentiation/tensorflow.py b/src/python/gudhi/differentiation/tensorflow.py index b7d68fae..b8f6461f 100644 --- a/src/python/gudhi/differentiation/tensorflow.py +++ b/src/python/gudhi/differentiation/tensorflow.py @@ -1,6 +1,5 @@ import numpy as np import tensorflow as tf -import tensorflow_addons as tfa from ..simplex_tree import SimplexTree from ..rips_complex import RipsComplex from ..cubical_complex import CubicalComplex @@ -166,7 +165,7 @@ class RipsModel(tf.keras.Model): m, d, c = self.mel, self.dim, self.card # Compute distance matrix - DX = tfa.losses.metric_learning.pairwise_distance(self.X) + DX = tf.math.sqrt(tf.reduce_sum((tf.expand_dims(self.X, 1)-tf.expand_dims(self.X, 0))**2, 2)) DXX = tf.reshape(DX, [1, DX.shape[0], DX.shape[1]]) # Turn numpy function into tensorflow function -- cgit v1.2.3 From f926b2c1e8782a47f2b79342c388f4f68bd5d2ca Mon Sep 17 00:00:00 2001 From: Mathieu Carriere Date: Sat, 13 Feb 2021 23:35:31 +0100 Subject: new try to fix errors --- data/filtered_simplicial_complex/simplextree.txt | 21 +++++++++++++++++++++ src/python/gudhi/differentiation/tensorflow.py | 6 +++++- src/python/test/simplextree.txt | 21 --------------------- src/python/test/test_diff.py | 2 +- 4 files changed, 27 insertions(+), 23 deletions(-) create mode 100644 data/filtered_simplicial_complex/simplextree.txt delete mode 100644 src/python/test/simplextree.txt (limited to 'src') diff --git a/data/filtered_simplicial_complex/simplextree.txt b/data/filtered_simplicial_complex/simplextree.txt new file mode 100644 index 00000000..e0dfcdd9 --- /dev/null +++ b/data/filtered_simplicial_complex/simplextree.txt @@ -0,0 +1,21 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +0 1 +1 2 +2 3 +3 4 +4 5 +5 6 +6 7 +7 8 +8 9 +9 10 diff --git a/src/python/gudhi/differentiation/tensorflow.py b/src/python/gudhi/differentiation/tensorflow.py index b8f6461f..4e08216d 100644 --- a/src/python/gudhi/differentiation/tensorflow.py +++ b/src/python/gudhi/differentiation/tensorflow.py @@ -176,7 +176,11 @@ class RipsModel(tf.keras.Model): ids = tf.nest.map_structure(tf.stop_gradient, tf.map_fn(RipsTF,DXX,dtype=[tf.int32 for _ in range(4*c)])) # Get persistence diagram by simply picking the corresponding entries in the distance matrix - dgm = tf.reshape(tf.gather_nd(DX, tf.reshape(ids, [2*c,2])), [c,2]) + if d > 0: + dgm = tf.reshape(tf.gather_nd(DX, tf.reshape(ids, [2*c,2])), [c,2]) + else: + ids = tf.reshape(ids, [2*c,2])[1::2,:] + dgm = tf.concat([tf.zeros([c,1]), tf.reshape(tf.gather_nd(DX, ids), [c,1])], axis=1) return dgm diff --git a/src/python/test/simplextree.txt b/src/python/test/simplextree.txt deleted file mode 100644 index e0dfcdd9..00000000 --- a/src/python/test/simplextree.txt +++ /dev/null @@ -1,21 +0,0 @@ -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -0 1 -1 2 -2 3 -3 4 -4 5 -5 6 -6 7 -7 8 -8 9 -9 10 diff --git a/src/python/test/test_diff.py b/src/python/test/test_diff.py index de738579..d42e25cd 100644 --- a/src/python/test/test_diff.py +++ b/src/python/test/test_diff.py @@ -31,7 +31,7 @@ def test_st_diff(): Finit = np.array([6.,4.,3.,4.,5.,4.,3.,2.,3.,4.,5.], dtype=np.float32) F = tf.Variable(initial_value=Finit, trainable=True) - model = LowerStarSimplexTreeModel(F, stbase="simplextree.txt", dim=0, card=10) + model = LowerStarSimplexTreeModel(F, stbase="../../../data/filtered_simplicial_complex/simplextree.txt", dim=0, card=10) with tf.GradientTape() as tape: dgm = model.call() -- cgit v1.2.3 From 2bd2f8134daeb65a9fff730fef75c323320faefb Mon Sep 17 00:00:00 2001 From: Hind-M Date: Tue, 8 Jun 2021 10:47:45 +0200 Subject: Fix incorrect comments relative to Cech --- src/Cech_complex/concept/SimplicialComplexForCech.h | 4 ++-- src/Cech_complex/example/cech_complex_step_by_step.cpp | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/concept/SimplicialComplexForCech.h b/src/Cech_complex/concept/SimplicialComplexForCech.h index 00c7df3a..6202fe92 100644 --- a/src/Cech_complex/concept/SimplicialComplexForCech.h +++ b/src/Cech_complex/concept/SimplicialComplexForCech.h @@ -47,8 +47,8 @@ struct SimplicialComplexForCech { }; -} // namespace alpha_complex +} // namespace cech_complex } // namespace Gudhi -#endif // CONCEPT_ALPHA_COMPLEX_SIMPLICIAL_COMPLEX_FOR_ALPHA_H_ +#endif // CONCEPT_CECH_COMPLEX_SIMPLICIAL_COMPLEX_FOR_CECH_H_ diff --git a/src/Cech_complex/example/cech_complex_step_by_step.cpp b/src/Cech_complex/example/cech_complex_step_by_step.cpp index f59f0293..60ae9712 100644 --- a/src/Cech_complex/example/cech_complex_step_by_step.cpp +++ b/src/Cech_complex/example/cech_complex_step_by_step.cpp @@ -24,9 +24,9 @@ #include // ---------------------------------------------------------------------------- -// rips_persistence_step_by_step is an example of each step that is required to -// build a Rips over a Simplex_tree. Please refer to rips_persistence to see -// how to do the same thing with the Rips_complex wrapper for less detailed +// cech_complex_step_by_step is an example of each step that is required to +// build a Cech over a Simplex_tree. Please refer to cech_complex_example_from_points to see +// how to do the same thing with the Cech complex wrapper for less detailed // steps. // ---------------------------------------------------------------------------- @@ -89,7 +89,7 @@ int main(int argc, char* argv[]) { Proximity_graph prox_graph = Gudhi::compute_proximity_graph(off_reader.get_point_cloud(), max_radius, Gudhi::Minimal_enclosing_ball_radius()); - // Construct the Rips complex in a Simplex Tree + // Construct the Cech complex in a Simplex Tree Simplex_tree st; // insert the proximity graph in the simplex tree st.insert_graph(prox_graph); @@ -129,9 +129,9 @@ void program_options(int argc, char* argv[], std::string& off_file_points, Filtr visible.add_options()("help,h", "produce help message")( "max-radius,r", po::value(&max_radius)->default_value(std::numeric_limits::infinity()), - "Maximal length of an edge for the Rips complex construction.")( + "Maximal length of an edge for the Cech complex construction.")( "cpx-dimension,d", po::value(&dim_max)->default_value(1), - "Maximal dimension of the Rips complex we want to compute."); + "Maximal dimension of the Cech complex we want to compute."); po::positional_options_description pos; pos.add("input-file", 1); -- cgit v1.2.3 From 6e2b5caf7fe0f255dbafa70d6cad62ec4d7277a3 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Mon, 5 Jul 2021 17:42:54 +0200 Subject: removed padding --- data/filtered_simplicial_complex/simplextree.txt | 21 --- src/python/gudhi/differentiation/__init__.py | 2 +- src/python/gudhi/differentiation/tensorflow.py | 225 ++++++++++------------- src/python/test/test_diff.py | 52 ++++-- 4 files changed, 139 insertions(+), 161 deletions(-) delete mode 100644 data/filtered_simplicial_complex/simplextree.txt (limited to 'src') diff --git a/data/filtered_simplicial_complex/simplextree.txt b/data/filtered_simplicial_complex/simplextree.txt deleted file mode 100644 index e0dfcdd9..00000000 --- a/data/filtered_simplicial_complex/simplextree.txt +++ /dev/null @@ -1,21 +0,0 @@ -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -0 1 -1 2 -2 3 -3 4 -4 5 -5 6 -6 7 -7 8 -8 9 -9 10 diff --git a/src/python/gudhi/differentiation/__init__.py b/src/python/gudhi/differentiation/__init__.py index 6793e904..3b7790e4 100644 --- a/src/python/gudhi/differentiation/__init__.py +++ b/src/python/gudhi/differentiation/__init__.py @@ -1,3 +1,3 @@ from .tensorflow import * -__all__ = ["LowerStarSimplexTreeModel", "RipsModel", "CubicalModel"] +__all__ = ["LowerStarSimplexTreeLayer", "RipsLayer", "CubicalLayer"] diff --git a/src/python/gudhi/differentiation/tensorflow.py b/src/python/gudhi/differentiation/tensorflow.py index 4e08216d..0f5df9a2 100644 --- a/src/python/gudhi/differentiation/tensorflow.py +++ b/src/python/gudhi/differentiation/tensorflow.py @@ -1,6 +1,5 @@ import numpy as np import tensorflow as tf -from ..simplex_tree import SimplexTree from ..rips_complex import RipsComplex from ..cubical_complex import CubicalComplex @@ -16,85 +15,69 @@ from ..cubical_complex import CubicalComplex # The parameters of the model are the vertex function values of the simplex tree. -def _SimplexTree(stbase, fct, dim, card): - # Parameters: stbase (array containing the name of the file where the simplex tree is located) - # fct (function values on the vertices of stbase), - # dim (homological dimension), - # card (number of persistence diagram points, sorted by distance-to-diagonal) - - # Copy stbase in another simplex tree st - st = SimplexTree() - f = open(stbase[0], "r") - for line in f: - ints = line.split(" ") - s = [int(v) for v in ints[:-1]] - st.insert(s, -1e10) - f.close() - +def _LowerStarSimplexTree(simplextree, filtration, dimension): + # Parameters: simplextree (simplex tree on which to compute persistence) + # filtration (function values on the vertices of st), + # dimension (homology dimension), + # Assign new filtration values - for i in range(st.num_vertices()): - st.assign_filtration([i], fct[i]) - st.make_filtration_non_decreasing() + for i in range(simplextree.num_vertices()): + simplextree.assign_filtration([i], filtration[i]) + simplextree.make_filtration_non_decreasing() # Compute persistence diagram - dgm = st.persistence() + dgm = simplextree.persistence() # Get vertex pairs for optimization. First, get all simplex pairs - pairs = st.persistence_pairs() + pairs = simplextree.persistence_pairs() # Then, loop over all simplex pairs indices, pers = [], [] for s1, s2 in pairs: # Select pairs with good homological dimension and finite lifetime - if len(s1) == dim+1 and len(s2) > 0: + if len(s1) == dimension+1 and len(s2) > 0: # Get IDs of the vertices corresponding to the filtration values of the simplices l1, l2 = np.array(s1), np.array(s2) - i1 = l1[np.argmax(fct[l1])] - i2 = l2[np.argmax(fct[l2])] + i1 = l1[np.argmax(filtration[l1])] + i2 = l2[np.argmax(filtration[l2])] indices.append(i1) indices.append(i2) # Compute lifetime - pers.append(st.filtration(s2) - st.filtration(s1)) + pers.append(simplextree.filtration(s2)-simplextree.filtration(s1)) # Sort vertex pairs wrt lifetime perm = np.argsort(pers) - indices = list(np.reshape(indices, [-1,2])[perm][::-1,:].flatten()) + indices = np.reshape(indices, [-1,2])[perm][::-1,:].flatten() - # Pad vertex pairs - indices = indices[:2*card] + [0 for _ in range(0,max(0,2*card-len(indices)))] - return list(np.array(indices, dtype=np.int32)) + return np.array(indices, dtype=np.int32) -class LowerStarSimplexTreeModel(tf.keras.Model): +class LowerStarSimplexTreeLayer(tf.keras.layers.Layer): """ - TensorFlow model for computing lower-star persistence out of a simplex tree. Since simplex trees cannot be easily encoded as TensorFlow variables, the model takes as input a path to a file containing the simplex tree simplices, and read it each time the simplex tree is required for computations. + TensorFlow layer for computing lower-star persistence out of a simplex tree Attributes: - F (TensorFlow variable): filter function values over the vertices of the simplex tree - stbase (string): path to the file containing the simplex tree. Each line of the file should represent a simplex as a sequence of integers separated by spaces - card (int): maximum number of points in the persistence diagram - dim (int): homology dimension + simplextree (gudhi.SimplexTree()): underlying simplex tree + dimension (int): homology dimension """ - def __init__(self, F, stbase="simplextree.txt", dim=0, card=50): - super(LowerStarSimplexTreeModel, self).__init__() - self.F = F - self.dim = dim - self.card = card - self.st = stbase - - def call(self): - d, c = self.dim, self.card - st, fct = self.st, self.F + def __init__(self, simplextree, dimension=0, **kwargs): + super().__init__(dynamic=True, **kwargs) + self.dimension = dimension + self.simplextree = simplextree + + def build(self): + super.build() + + def call(self, filtration): + """ + Compute lower-star persistence diagram associated to a function defined on the vertices of the simplex tree - # Turn STPers into a numpy function - SimplexTreeTF = lambda fct: tf.numpy_function(_SimplexTree, [np.array([st], dtype=str), fct, d, c], [tf.int32 for _ in range(2*c)]) - + Parameters: + F (TensorFlow variable): filter function values over the vertices of the simplex tree + """ # Don't try to compute gradients for the vertex pairs - fcts = tf.reshape(fct, [1, self.F.shape[0]]) - inds = tf.nest.map_structure(tf.stop_gradient, tf.map_fn(SimplexTreeTF, - fcts, dtype=[tf.int32 for _ in range(2*c)])) - + indices = tf.stop_gradient(_LowerStarSimplexTree(self.simplextree, filtration.numpy(), self.dimension)) # Get persistence diagram - self.dgm = tf.reshape(tf.gather_nd(self.F, inds), [c,2]) + self.dgm = tf.reshape(tf.gather(filtration, indices), [-1,2]) return self.dgm @@ -105,22 +88,20 @@ class LowerStarSimplexTreeModel(tf.keras.Model): - ############################ # Vietoris-Rips filtration # ############################ # The parameters of the model are the point coordinates. -def _Rips(DX, mel, dim, card): +def _Rips(DX, max_edge, dimension): # Parameters: DX (distance matrix), - # mel (maximum edge length for Rips filtration), - # dim (homological dimension), - # card (number of persistence diagram points, sorted by distance-to-diagonal) + # max_edge (maximum edge length for Rips filtration), + # dimension (homology dimension) # Compute the persistence pairs with Gudhi - rc = RipsComplex(distance_matrix=DX, max_edge_length=mel) - st = rc.create_simplex_tree(max_dimension=dim+1) + rc = RipsComplex(distance_matrix=DX, max_edge_length=max_edge) + st = rc.create_simplex_tree(max_dimension=dimension+1) dgm = st.persistence() pairs = st.persistence_pairs() @@ -128,59 +109,54 @@ def _Rips(DX, mel, dim, card): # distance among all pairwise distances between the simplex vertices indices, pers = [], [] for s1, s2 in pairs: - if len(s1) == dim+1 and len(s2) > 0: + if len(s1) == dimension+1 and len(s2) > 0: l1, l2 = np.array(s1), np.array(s2) - i1 = [s1[v] for v in np.unravel_index(np.argmax(DX[l1,:][:,l1]),[len(s1), len(s1)])] - i2 = [s2[v] for v in np.unravel_index(np.argmax(DX[l2,:][:,l2]),[len(s2), len(s2)])] - indices += i1 - indices += i2 - pers.append(st.filtration(s2) - st.filtration(s1)) + i1 = [l1[v] for v in np.unravel_index(np.argmax(DX[l1,:][:,l1]),[len(l1), len(l1)])] + i2 = [l2[v] for v in np.unravel_index(np.argmax(DX[l2,:][:,l2]),[len(l2), len(l2)])] + indices.append(i1) + indices.append(i2) + pers.append(st.filtration(s2)-st.filtration(s1)) # Sort points with distance-to-diagonal perm = np.argsort(pers) - indices = list(np.reshape(indices, [-1,4])[perm][::-1,:].flatten()) - - # Output indices - indices = indices[:4*card] + [0 for _ in range(0,max(0,4*card-len(indices)))] - return list(np.array(indices, dtype=np.int32)) + indices = np.reshape(indices, [-1,4])[perm][::-1,:].flatten() + + return np.array(indices, dtype=np.int32) -class RipsModel(tf.keras.Model): +class RipsLayer(tf.keras.layers.Layer): """ - TensorFlow model for computing Rips persistence out of a point cloud. + TensorFlow layer for computing Rips persistence out of a point cloud Attributes: - X (TensorFlow variable): point cloud of shape [number of points, number of dimensions] - mel (float): maximum edge length for the Rips complex - card (int): maximum number of points in the persistence diagram - dim (int): homology dimension + maximum_edge_length (float): maximum edge length for the Rips complex + dimension (int): homology dimension """ - def __init__(self, X, mel=12, dim=1, card=50): - super(RipsModel, self).__init__() - self.X = X - self.mel = mel - self.dim = dim - self.card = card - - def call(self): - m, d, c = self.mel, self.dim, self.card + def __init__(self, maximum_edge_length=12, dimension=1, **kwargs): + super().__init__(dynamic=True, **kwargs) + self.max_edge = maximum_edge_length + self.dimension = dimension + + def build(self): + super.build() + def call(self, X): + """ + Compute Rips persistence diagram associated to a point cloud + + Parameters: + X (TensorFlow variable): point cloud of shape [number of points, number of dimensions] + """ # Compute distance matrix - DX = tf.math.sqrt(tf.reduce_sum((tf.expand_dims(self.X, 1)-tf.expand_dims(self.X, 0))**2, 2)) - DXX = tf.reshape(DX, [1, DX.shape[0], DX.shape[1]]) - - # Turn numpy function into tensorflow function - RipsTF = lambda DX: tf.numpy_function(_Rips, [DX, m, d, c], [tf.int32 for _ in range(4*c)]) - + DX = tf.math.sqrt(tf.reduce_sum((tf.expand_dims(X, 1)-tf.expand_dims(X, 0))**2, 2)) # Compute vertices associated to positive and negative simplices # Don't compute gradient for this operation - ids = tf.nest.map_structure(tf.stop_gradient, tf.map_fn(RipsTF,DXX,dtype=[tf.int32 for _ in range(4*c)])) - + indices = tf.stop_gradient(_Rips(DX.numpy(), self.max_edge, self.dimension)) # Get persistence diagram by simply picking the corresponding entries in the distance matrix - if d > 0: - dgm = tf.reshape(tf.gather_nd(DX, tf.reshape(ids, [2*c,2])), [c,2]) + if self.dimension > 0: + dgm = tf.reshape(tf.gather_nd(DX, tf.reshape(indices, [-1,2])), [-1,2]) else: - ids = tf.reshape(ids, [2*c,2])[1::2,:] - dgm = tf.concat([tf.zeros([c,1]), tf.reshape(tf.gather_nd(DX, ids), [c,1])], axis=1) + indices = tf.reshape(indices, [-1,2])[1::2,:] + dgm = tf.concat([tf.zeros([indices.shape[0],1]), tf.reshape(tf.gather_nd(DX, indices), [-1,1])], axis=1) return dgm @@ -197,16 +173,15 @@ class RipsModel(tf.keras.Model): # The parameters of the model are the pixel values. -def _Cubical(X, dim, card): +def _Cubical(X, dimension): # Parameters: X (image), - # dim (homological dimension), - # card (number of persistence diagram points, sorted by distance-to-diagonal) + # dimension (homology dimension) # Compute the persistence pairs with Gudhi cc = CubicalComplex(dimensions=X.shape, top_dimensional_cells=X.flatten()) cc.persistence() try: - cof = cc.cofaces_of_persistence_pairs()[0][dim] + cof = cc.cofaces_of_persistence_pairs()[0][dimension] except IndexError: cof = np.array([]) @@ -218,41 +193,39 @@ def _Cubical(X, dim, card): cof = cof[perm[::-1]] # Retrieve and ouput image indices/pixels corresponding to positive and negative simplices - D = len(Xs) - ocof = np.array([0 for _ in range(D*card*2)]) + D = len(Xs) if len(cof) > 0 else 1 + ocof = np.array([0 for _ in range(D*2*cof.shape[0])]) count = 0 - for idx in range(0,min(2*card, 2*cof.shape[0]),2): + for idx in range(0,2*cof.shape[0],2): ocof[D*idx:D*(idx+1)] = np.unravel_index(cof[count,0], Xs) ocof[D*(idx+1):D*(idx+2)] = np.unravel_index(cof[count,1], Xs) count += 1 - return list(np.array(ocof, dtype=np.int32)) + return np.array(ocof, dtype=np.int32) -class CubicalModel(tf.keras.Model): +class CubicalLayer(tf.keras.layers.Layer): """ - TensorFlow model for computing cubical persistence out of a cubical complex. + TensorFlow layer for computing cubical persistence out of a cubical complex Attributes: - X (TensorFlow variable): pixel values of the cubical complex - card (int): maximum number of points in the persistence diagram - dim (int): homology dimension + dimension (int): homology dimension """ - def __init__(self, X, dim=1, card=50): - super(CubicalModel, self).__init__() - self.X = X - self.dim = dim - self.card = card - - def call(self): - d, c, D = self.dim, self.card, len(self.X.shape) - XX = tf.reshape(self.X, [1, self.X.shape[0], self.X.shape[1]]) - - # Turn numpy function into tensorflow function - CbTF = lambda X: tf.numpy_function(_Cubical, [X, d, c], [tf.int32 for _ in range(2*D*c)]) + def __init__(self, dimension=1, **kwargs): + super().__init__(dynamic=True, **kwargs) + self.dimension = dimension + + def build(self): + super.build() + def call(self, X): + """ + Compute persistence diagram associated to a cubical complex filtered by some pixel values + + Parameters: + X (TensorFlow variable): pixel values of the cubical complex + """ # Compute pixels associated to positive and negative simplices # Don't compute gradient for this operation - inds = tf.nest.map_structure(tf.stop_gradient, tf.map_fn(CbTF,XX,dtype=[tf.int32 for _ in range(2*D*c)])) - + indices = tf.stop_gradient(_Cubical(X.numpy(), self.dimension)) # Get persistence diagram by simply picking the corresponding entries in the image - dgm = tf.reshape(tf.gather_nd(self.X, tf.reshape(inds, [-1,D])), [-1,2]) + dgm = tf.reshape(tf.gather_nd(X, tf.reshape(indices, [-1,len(X.shape)])), [-1,2]) return dgm diff --git a/src/python/test/test_diff.py b/src/python/test/test_diff.py index d42e25cd..129b9f03 100644 --- a/src/python/test/test_diff.py +++ b/src/python/test/test_diff.py @@ -1,41 +1,67 @@ from gudhi.differentiation import * import numpy as np import tensorflow as tf +import gudhi as gd def test_rips_diff(): Xinit = np.array([[1.,1.],[2.,2.]], dtype=np.float32) X = tf.Variable(initial_value=Xinit, trainable=True) - model = RipsModel(X=X, mel=2., dim=0, card=10) + rl = RipsLayer(maximum_edge_length=2., dimension=0) with tf.GradientTape() as tape: - dgm = model.call() + dgm = rl.call(X) loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) - grads = tape.gradient(loss, [X]) - assert np.abs(grads[0].numpy()-np.array([[-.5,-.5],[.5,.5]])).sum() <= 1e-6 + grads = tape.gradient(loss, [X]) + assert np.abs(grads[0].numpy()-np.array([[-.5,-.5],[.5,.5]])).sum() <= 1e-6 def test_cubical_diff(): Xinit = np.array([[0.,2.,2.],[2.,2.,2.],[2.,2.,1.]], dtype=np.float32) X = tf.Variable(initial_value=Xinit, trainable=True) - model = CubicalModel(X, dim=0, card=10) + cl = CubicalLayer(dimension=0) with tf.GradientTape() as tape: - dgm = model.call() + dgm = cl.call(X) loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) - grads = tape.gradient(loss, [X]) - assert np.abs(grads[0].numpy()-np.array([[0.,0.,0.],[0.,.5,0.],[0.,0.,-.5]])).sum() <= 1e-6 + grads = tape.gradient(loss, [X]) + assert np.abs(grads[0].numpy()-np.array([[0.,0.,0.],[0.,.5,0.],[0.,0.,-.5]])).sum() <= 1e-6 def test_st_diff(): + st = gd.SimplexTree() + st.insert([0]) + st.insert([1]) + st.insert([2]) + st.insert([3]) + st.insert([4]) + st.insert([5]) + st.insert([6]) + st.insert([7]) + st.insert([8]) + st.insert([9]) + st.insert([10]) + st.insert([0, 1]) + st.insert([1, 2]) + st.insert([2, 3]) + st.insert([3, 4]) + st.insert([4, 5]) + st.insert([5, 6]) + st.insert([6, 7]) + st.insert([7, 8]) + st.insert([8, 9]) + st.insert([9, 10]) + Finit = np.array([6.,4.,3.,4.,5.,4.,3.,2.,3.,4.,5.], dtype=np.float32) F = tf.Variable(initial_value=Finit, trainable=True) - model = LowerStarSimplexTreeModel(F, stbase="../../../data/filtered_simplicial_complex/simplextree.txt", dim=0, card=10) + sl = LowerStarSimplexTreeLayer(simplextree=st, dimension=0) with tf.GradientTape() as tape: - dgm = model.call() + dgm = sl.call(F) loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) - grads = tape.gradient(loss, [F]) - assert np.array_equal(np.array(grads[0].indices), np.array([2,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])) - assert np.array_equal(np.array(grads[0].values), np.array([-1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])) + grads = tape.gradient(loss, [F]) + + assert np.array_equal(np.array(grads[0].indices), np.array([2,4])) + assert np.array_equal(np.array(grads[0].values), np.array([-1,1])) + -- cgit v1.2.3 From a384882db85e75f8d4bdf8df1b293b9d91e0c406 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Wed, 7 Jul 2021 15:11:24 +0200 Subject: small fix --- src/python/gudhi/differentiation/tensorflow.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/python/gudhi/differentiation/tensorflow.py b/src/python/gudhi/differentiation/tensorflow.py index 0f5df9a2..15d5811e 100644 --- a/src/python/gudhi/differentiation/tensorflow.py +++ b/src/python/gudhi/differentiation/tensorflow.py @@ -19,6 +19,9 @@ def _LowerStarSimplexTree(simplextree, filtration, dimension): # Parameters: simplextree (simplex tree on which to compute persistence) # filtration (function values on the vertices of st), # dimension (homology dimension), + + for s,_ in simplextree.get_filtration(): + simplextree.assign_filtration(s, -1e10) # Assign new filtration values for i in range(simplextree.num_vertices()): -- cgit v1.2.3 From 91b0ff839b8058d3f5767e6ed80b93c23be2c98a Mon Sep 17 00:00:00 2001 From: Hind-M Date: Mon, 9 Aug 2021 16:21:24 +0200 Subject: First version of cech enhancement --- .../benchmark/cech_complex_benchmark.cpp | 5 +- .../example/cech_complex_example_from_points.cpp | 71 ++++++++++--- .../example/cech_complex_step_by_step.cpp | 18 ++-- src/Cech_complex/include/gudhi/Cech_complex.h | 26 +++-- .../include/gudhi/Cech_complex_blocker.h | 110 ++++++++++++++++++++- src/Cech_complex/test/test_cech_complex.cpp | 86 +++++++++++----- src/Cech_complex/utilities/cech_persistence.cpp | 10 +- src/Simplex_tree/include/gudhi/Simplex_tree.h | 4 + src/common/include/gudhi/distance_functions.h | 42 ++++++++ .../include/gudhi/graph_simplicial_complex.h | 3 + 10 files changed, 311 insertions(+), 64 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/benchmark/cech_complex_benchmark.cpp b/src/Cech_complex/benchmark/cech_complex_benchmark.cpp index e489e8a4..c332c656 100644 --- a/src/Cech_complex/benchmark/cech_complex_benchmark.cpp +++ b/src/Cech_complex/benchmark/cech_complex_benchmark.cpp @@ -17,6 +17,8 @@ #include #include +#include // For EXACT or SAFE version + #include "boost/filesystem.hpp" // includes all needed Boost.Filesystem declarations #include @@ -30,7 +32,8 @@ using Point_cloud = std::vector; using Points_off_reader = Gudhi::Points_off_reader; using Proximity_graph = Gudhi::Proximity_graph; using Rips_complex = Gudhi::rips_complex::Rips_complex; -using Cech_complex = Gudhi::cech_complex::Cech_complex; +using Kernel = CGAL::Epeck_d; +using Cech_complex = Gudhi::cech_complex::Cech_complex; class Minimal_enclosing_ball_radius { public: diff --git a/src/Cech_complex/example/cech_complex_example_from_points.cpp b/src/Cech_complex/example/cech_complex_example_from_points.cpp index 1a1f708c..ac17fc73 100644 --- a/src/Cech_complex/example/cech_complex_example_from_points.cpp +++ b/src/Cech_complex/example/cech_complex_example_from_points.cpp @@ -1,6 +1,8 @@ #include #include +#include // For EXACT or SAFE version + #include #include #include @@ -8,32 +10,71 @@ int main() { // Type definitions - using Point_cloud = std::vector>; +// using Point_cloud = std::vector>; using Simplex_tree = Gudhi::Simplex_tree; using Filtration_value = Simplex_tree::Filtration_value; - using Cech_complex = Gudhi::cech_complex::Cech_complex; + using Kernel = CGAL::Epeck_d; + using FT = typename Kernel::FT; + using Point = typename Kernel::Point_d; + using Point_cloud = std::vector; + using Cech_complex = Gudhi::cech_complex::Cech_complex; Point_cloud points; - points.push_back({1., 0.}); // 0 - points.push_back({0., 1.}); // 1 - points.push_back({2., 1.}); // 2 - points.push_back({3., 2.}); // 3 - points.push_back({0., 3.}); // 4 - points.push_back({3. + std::sqrt(3.), 3.}); // 5 - points.push_back({1., 4.}); // 6 - points.push_back({3., 4.}); // 7 - points.push_back({2., 4. + std::sqrt(3.)}); // 8 - points.push_back({0., 4.}); // 9 - points.push_back({-0.5, 2.}); // 10 +// points.push_back({1., 0.}); // 0 +// points.push_back({0., 1.}); // 1 +// points.push_back({2., 1.}); // 2 +// points.push_back({3., 2.}); // 3 +// points.push_back({0., 3.}); // 4 +// points.push_back({3. + std::sqrt(3.), 3.}); // 5 + +// std::vector point({0.0, 0.0, 0.0, 0.0}); +// points.emplace_back(point.begin(), point.end()); + + std::vector point0({1., 0.}); + points.emplace_back(point0.begin(), point0.end()); + std::vector point1({0., 1.}); + points.emplace_back(point1.begin(), point1.end()); + std::vector point2({2., 1.}); + points.emplace_back(point2.begin(), point2.end()); + std::vector point3({3., 2.}); + points.emplace_back(point3.begin(), point3.end()); + std::vector point4({0., 3.}); + points.emplace_back(point4.begin(), point4.end()); + std::vector point5({3. + std::sqrt(3.), 3.}); + points.emplace_back(point5.begin(), point5.end()); + +// points.emplace_back(Point(std::vector({1., 0.}))); +// points.emplace_back(Point(std::vector({0., 1.}))); +// points.emplace_back(Point(std::vector({2., 1.}))); +// points.emplace_back(Point(std::vector({3., 2.}))); +// points.emplace_back(Point(std::vector({0., 3.}))); +// points.emplace_back(Point(std::vector({3. + std::sqrt(3.), 3.}))); + + +// points.push_back(Point(1.0, 0.0)); +// points.push_back(Point(0.0, 1.0)); +// points.push_back(Point(2.0, 1.0)); +// points.push_back(Point(3.0, 2.0)); +// points.push_back(Point(0.0, 3.0)); +// points.push_back(Point(3.0 + std::sqrt(3.0), 3.0)); + + +// points.push_back({1., 4.}); // 6 +// points.push_back({3., 4.}); // 7 +// points.push_back({2., 4. + std::sqrt(3.)}); // 8 +// points.push_back({0., 4.}); // 9 +// points.push_back({-0.5, 2.}); // 10 // ---------------------------------------------------------------------------- // Init of a Cech complex from points // ---------------------------------------------------------------------------- - Filtration_value max_radius = 1.; + Filtration_value max_radius = 10.; + std::clog << "Hind: Just before the Cech constructor" << std::endl; Cech_complex cech_complex_from_points(points, max_radius); + std::clog << "Hind: Just after the Cech constructor" << std::endl; Simplex_tree stree; - cech_complex_from_points.create_complex(stree, 2); + cech_complex_from_points.create_complex(stree, 3); // ---------------------------------------------------------------------------- // Display information about the one skeleton Cech complex // ---------------------------------------------------------------------------- diff --git a/src/Cech_complex/example/cech_complex_step_by_step.cpp b/src/Cech_complex/example/cech_complex_step_by_step.cpp index 60ae9712..ac08e6cc 100644 --- a/src/Cech_complex/example/cech_complex_step_by_step.cpp +++ b/src/Cech_complex/example/cech_complex_step_by_step.cpp @@ -13,7 +13,9 @@ #include #include -#include +#include // TODO to remove ? + +#include #include @@ -34,16 +36,18 @@ using Simplex_tree = Gudhi::Simplex_tree<>; using Simplex_handle = Simplex_tree::Simplex_handle; using Filtration_value = Simplex_tree::Filtration_value; -using Point = std::vector; +// using Point = std::vector; +using Kernel = CGAL::Epeck_d; +using Point = typename Kernel::Point_d; using Points_off_reader = Gudhi::Points_off_reader; using Proximity_graph = Gudhi::Proximity_graph; class Cech_blocker { private: using Point_cloud = std::vector; - using Point_iterator = Point_cloud::const_iterator; - using Coordinate_iterator = Point::const_iterator; - using Min_sphere = Gudhi::Miniball::Miniball>; +// using Point_iterator = Point_cloud::const_iterator; +// using Coordinate_iterator = Point::const_iterator; +// using Min_sphere = Gudhi::Miniball::Miniball>; public: bool operator()(Simplex_handle sh) { @@ -63,14 +67,14 @@ class Cech_blocker { } Cech_blocker(Simplex_tree& simplex_tree, Filtration_value max_radius, const std::vector& point_cloud) : simplex_tree_(simplex_tree), max_radius_(max_radius), point_cloud_(point_cloud) { - dimension_ = point_cloud_[0].size(); +// dimension_ = point_cloud_[0].size(); } private: Simplex_tree simplex_tree_; Filtration_value max_radius_; std::vector point_cloud_; - int dimension_; +// int dimension_; }; void program_options(int argc, char* argv[], std::string& off_file_points, Filtration_value& max_radius, int& dim_max); diff --git a/src/Cech_complex/include/gudhi/Cech_complex.h b/src/Cech_complex/include/gudhi/Cech_complex.h index b0871e10..2c6f202a 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex.h +++ b/src/Cech_complex/include/gudhi/Cech_complex.h @@ -40,7 +40,7 @@ namespace cech_complex { * \tparam ForwardPointRange must be a range for which `std::begin()` and `std::end()` methods return input * iterators on a point. `std::begin()` and `std::end()` methods are also required for a point. */ -template +template class Cech_complex { private: // Required by compute_proximity_graph @@ -49,14 +49,15 @@ class Cech_complex { using Proximity_graph = Gudhi::Proximity_graph; // Retrieve Coordinate type from ForwardPointRange - using Point_from_range_iterator = typename boost::range_const_iterator::type; - using Point_from_range = typename std::iterator_traits::value_type; - using Coordinate_iterator = typename boost::range_const_iterator::type; - using Coordinate = typename std::iterator_traits::value_type; +// using Point_from_range_iterator = typename boost::range_const_iterator::type; +// using Point_from_range = typename std::iterator_traits::value_type; +// using Coordinate_iterator = typename boost::range_const_iterator::type; +// using Coordinate = typename std::iterator_traits::value_type; public: // Point and Point_cloud type definition - using Point = std::vector; + //using Point = std::vector; + using Point = typename Kernel::Point_d; using Point_cloud = std::vector; public: @@ -70,9 +71,13 @@ class Cech_complex { */ Cech_complex(const ForwardPointRange& points, Filtration_value max_radius) : max_radius_(max_radius) { // Point cloud deep copy - point_cloud_.reserve(boost::size(points)); - for (auto&& point : points) point_cloud_.emplace_back(std::begin(point), std::end(point)); +// point_cloud_.reserve(boost::size(points)); +// for (auto&& point : points) point_cloud_.emplace_back(point.cartesian_begin(), point.cartesian_end()); + + point_cloud_.assign(points.begin(), points.end()); + + std::clog << "Hind: Just before the graph compute" << std::endl; cech_skeleton_graph_ = Gudhi::compute_proximity_graph( point_cloud_, max_radius_, Gudhi::Minimal_enclosing_ball_radius()); } @@ -87,14 +92,17 @@ class Cech_complex { */ template void create_complex(SimplicialComplexForCechComplex& complex, int dim_max) { + std::clog << "Hind: in create complex" << std::endl; GUDHI_CHECK(complex.num_vertices() == 0, std::invalid_argument("Cech_complex::create_complex - simplicial complex is not empty")); // insert the proximity graph in the simplicial complex + std::clog << "Hind: before insert_graph" << std::endl; complex.insert_graph(cech_skeleton_graph_); // expand the graph until dimension dim_max + std::clog << "Hind: before expansion_with_blockers" << std::endl; complex.expansion_with_blockers(dim_max, - Cech_blocker(&complex, this)); + Cech_blocker(&complex, this)); } /** @return max_radius value given at construction. */ diff --git a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h index 31b9aab5..f2cf5ccc 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h +++ b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h @@ -11,8 +11,15 @@ #ifndef CECH_COMPLEX_BLOCKER_H_ #define CECH_COMPLEX_BLOCKER_H_ +// TODO to remove #include // for Gudhi::Minimal_enclosing_ball_radius +#include // for CGAL::to_double +#include // +// #include +#include // For EXACT or SAFE version +#include + #include #include #include // for std::sqrt @@ -35,28 +42,120 @@ namespace cech_complex { * * \tparam Chech_complex is required by the blocker. */ -template +template class Cech_blocker { private: - using Point_cloud = typename Cech_complex::Point_cloud; +// using Point_cloud = typename Cech_complex::Point_cloud; using Simplex_handle = typename SimplicialComplexForCech::Simplex_handle; using Filtration_value = typename SimplicialComplexForCech::Filtration_value; public: + + using Point_d = typename Kernel::Point_d; + // Numeric type of coordinates in the kernel + using FT = typename Kernel::FT; + // Sphere is a pair of point and squared radius. + using Sphere = typename std::pair; + + + // Add an int in TDS to save point index in the structure +// using TDS = CGAL::Triangulation_data_structure, +// CGAL::Triangulation_full_cell >; +// +// /** \brief A (Weighted or not) Delaunay triangulation of a set of points in \f$ \mathbb{R}^D\f$.*/ +// using Triangulation = CGAL::Delaunay_triangulation; +// // Vertex_iterator type from CGAL. +// using CGAL_vertex_iterator = typename Triangulation::Vertex_iterator; + + // Structure to switch from simplex tree vertex handle to CGAL vertex iterator. + //using Vector_vertex_iterator = std::vector< CGAL_vertex_iterator >; + + + + /** \brief get_point_ returns the point corresponding to the vertex given as parameter. + * Only for internal use for faster access. + * + * @param[in] vertex Vertex handle of the point to retrieve. + * @return The point found. + */ +/* const Point_d& get_point_(std::size_t vertex) const { + return vertex_handle_to_iterator_[vertex]->point(); + } */ + + /** \internal \brief TODO + * \param[in] + * \return */ + template + FT get_squared_radius(PointIterator begin, PointIterator end) const { + return kernel_.compute_squared_radius_d_object()(begin, end); + } + + /** \internal \brief TODO + * \param[in] + * \return */ + template + Sphere get_sphere(PointIterator begin, PointIterator end) const { + Point_d c = kernel_.construct_circumcenter_d_object()(begin, end); + FT r = kernel_.squared_distance_d_object()(c, *begin); + return std::make_pair(std::move(c), std::move(r)); + } + + /** \internal \brief Čech complex blocker operator() - the oracle - assigns the filtration value from the simplex * radius and returns if the simplex expansion must be blocked. * \param[in] sh The Simplex_handle. * \return true if the simplex radius is greater than the Cech_complex max_radius*/ bool operator()(Simplex_handle sh) { + using Point_cloud = std::vector; Point_cloud points; + + // for each face of simplex sh, test outsider point is indeed inside enclosing ball, if yes, take it and exit loop, otherwise, new sphere is circumsphere of all vertices + for (auto face : sc_ptr_->simplex_vertex_range(sh)) { + ///////////////////////////////////////////////////////////////////// + + + + + /////////////////////////////////// + } + for (auto vertex : sc_ptr_->simplex_vertex_range(sh)) { - points.push_back(cc_ptr_->get_point(vertex)); + points.push_back(cc_ptr_->get_point(vertex)); +// points.push_back(get_point_(vertex)); #ifdef DEBUG_TRACES std::clog << "#(" << vertex << ")#"; #endif // DEBUG_TRACES } - Filtration_value radius = Gudhi::Minimal_enclosing_ball_radius()(points); + // TODO to remove + //Filtration_value radius = Gudhi::Minimal_enclosing_ball_radius()(points); + // Hind: Here change the algo of the enclosing Minimal_enclosing_ball_radius + auto point_to_be_inserted = points.back(); + Sphere sph = get_sphere(points.cbegin(), points.cend()-1); + +// Sphere sph = get_sphere(points.cbegin(), points.cend()-1); + CGAL::NT_converter cast_to_double; +// CGAL::NT_converter cast_point_d_to_double; + + std::clog << "circumcenter: " << sph.first << ", radius: " << std::sqrt(cast_to_double(sph.second))<< std::endl; + // TODO to remove + // Filtration_value test = std::sqrt(CGAL::to_double(sph.second)); + + + // Check that the point to be inserted is already included in the sphere of the simplex containing the preceding points + // TODO instead of Euclidean_distance ; use kernel_.squared_distance_d_object()(c, *begin); + // Add a loop on the three faces to check sphere before computing the circumsphere + // Add the computed sphere as cache; a vector of spheres depending on the number of faces ? + // +// if (Gudhi::Euclidean_distance()(cast_point_d_to_double(sph.first), point_to_be_inserted) > std::sqrt(cast_to_double(sph.second))) +// FT r = kernel_.squared_distance_d_object()(sph.first, sph.first); //*(points.cend()-1)); + if (kernel_.squared_distance_d_object()(sph.first, point_to_be_inserted) > sph.second) + sph = get_sphere(points.cbegin(), points.cend()); + + Filtration_value radius = std::sqrt(cast_to_double(sph.second)); + + #ifdef DEBUG_TRACES if (radius > cc_ptr_->max_radius()) std::clog << "radius > max_radius => expansion is blocked\n"; #endif // DEBUG_TRACES @@ -70,6 +169,9 @@ class Cech_blocker { private: SimplicialComplexForCech* sc_ptr_; Cech_complex* cc_ptr_; + Kernel kernel_; + //Vector_vertex_iterator vertex_handle_to_iterator_; + }; } // namespace cech_complex diff --git a/src/Cech_complex/test/test_cech_complex.cpp b/src/Cech_complex/test/test_cech_complex.cpp index 6e00d7b5..81efd6ae 100644 --- a/src/Cech_complex/test/test_cech_complex.cpp +++ b/src/Cech_complex/test/test_cech_complex.cpp @@ -26,17 +26,24 @@ #include #include +#include // For EXACT or SAFE version + + // Type definitions using Simplex_tree = Gudhi::Simplex_tree<>; using Filtration_value = Simplex_tree::Filtration_value; -using Point = std::vector; +//using Point = std::vector; +using Kernel = CGAL::Epeck_d; +using FT = typename Kernel::FT; +using Point = typename Kernel::Point_d; + using Point_cloud = std::vector; using Points_off_reader = Gudhi::Points_off_reader; -using Cech_complex = Gudhi::cech_complex::Cech_complex; +using Cech_complex = Gudhi::cech_complex::Cech_complex; -using Point_iterator = Point_cloud::const_iterator; -using Coordinate_iterator = Point::const_iterator; -using Min_sphere = Gudhi::Miniball::Miniball>; +// using Point_iterator = Point_cloud::const_iterator; +// using Coordinate_iterator = Point::const_iterator; +// using Min_sphere = Gudhi::Miniball::Miniball>; BOOST_AUTO_TEST_CASE(Cech_complex_for_documentation) { // ---------------------------------------------------------------------------- @@ -45,17 +52,41 @@ BOOST_AUTO_TEST_CASE(Cech_complex_for_documentation) { // // ---------------------------------------------------------------------------- Point_cloud points; - points.push_back({1., 0.}); // 0 - points.push_back({0., 1.}); // 1 - points.push_back({2., 1.}); // 2 - points.push_back({3., 2.}); // 3 - points.push_back({0., 3.}); // 4 - points.push_back({3. + std::sqrt(3.), 3.}); // 5 - points.push_back({1., 4.}); // 6 - points.push_back({3., 4.}); // 7 - points.push_back({2., 4. + std::sqrt(3.)}); // 8 - points.push_back({0., 4.}); // 9 - points.push_back({-0.5, 2.}); // 10 +// points.push_back({1., 0.}); // 0 +// points.push_back({0., 1.}); // 1 +// points.push_back({2., 1.}); // 2 +// points.push_back({3., 2.}); // 3 +// points.push_back({0., 3.}); // 4 +// points.push_back({3. + std::sqrt(3.), 3.}); // 5 +// points.push_back({1., 4.}); // 6 +// points.push_back({3., 4.}); // 7 +// points.push_back({2., 4. + std::sqrt(3.)}); // 8 +// points.push_back({0., 4.}); // 9 +// points.push_back({-0.5, 2.}); // 10 + + + std::vector point0({1., 0.}); + points.emplace_back(point0.begin(), point0.end()); + std::vector point1({0., 1.}); + points.emplace_back(point1.begin(), point1.end()); + std::vector point2({2., 1.}); + points.emplace_back(point2.begin(), point2.end()); + std::vector point3({3., 2.}); + points.emplace_back(point3.begin(), point3.end()); + std::vector point4({0., 3.}); + points.emplace_back(point4.begin(), point4.end()); + std::vector point5({3. + std::sqrt(3.), 3.}); + points.emplace_back(point5.begin(), point5.end()); + std::vector point6({1., 4.}); + points.emplace_back(point6.begin(), point6.end()); + std::vector point7({3., 4.}); + points.emplace_back(point7.begin(), point7.end()); + std::vector point8({2., 4. + std::sqrt(3.)}); + points.emplace_back(point8.begin(), point8.end()); + std::vector point9({0., 4.}); + points.emplace_back(point9.begin(), point9.end()); + std::vector point10({-0.5, 2.}); + points.emplace_back(point10.begin(), point10.end()); Filtration_value max_radius = 1.0; std::clog << "========== NUMBER OF POINTS = " << points.size() << " - Cech max_radius = " << max_radius @@ -125,35 +156,38 @@ BOOST_AUTO_TEST_CASE(Cech_complex_for_documentation) { for (std::size_t vertex = 0; vertex <= 2; vertex++) { points012.push_back(cech_complex_for_doc.get_point(vertex)); } - std::size_t dimension = points[0].end() - points[0].begin(); - Min_sphere ms012(dimension, points012.begin(), points012.end()); +// std::size_t dimension = points[0].end() - points[0].begin(); +// Min_sphere ms012(dimension, points012.begin(), points012.end()); + Kernel kern; Simplex_tree::Filtration_value f012 = st2.filtration(st2.find({0, 1, 2})); - std::clog << "f012= " << f012 << " | ms012_radius= " << std::sqrt(ms012.squared_radius()) << std::endl; + CGAL::NT_converter cast_to_double; + std::clog << "f012= " << f012 << " | points012_radius= " << std::sqrt(cast_to_double(kern.compute_squared_radius_d_object()(points012.begin(), points012.end()))) << std::endl; + - GUDHI_TEST_FLOAT_EQUALITY_CHECK(f012, std::sqrt(ms012.squared_radius())); + GUDHI_TEST_FLOAT_EQUALITY_CHECK(f012, std::sqrt(cast_to_double(kern.compute_squared_radius_d_object()(points012.begin(), points012.end())))); Point_cloud points1410; points1410.push_back(cech_complex_for_doc.get_point(1)); points1410.push_back(cech_complex_for_doc.get_point(4)); points1410.push_back(cech_complex_for_doc.get_point(10)); - Min_sphere ms1410(dimension, points1410.begin(), points1410.end()); +// Min_sphere ms1410(dimension, points1410.begin(), points1410.end()); Simplex_tree::Filtration_value f1410 = st2.filtration(st2.find({1, 4, 10})); - std::clog << "f1410= " << f1410 << " | ms1410_radius= " << std::sqrt(ms1410.squared_radius()) << std::endl; + std::clog << "f1410= " << f1410 << " | points1410_radius= " << std::sqrt(cast_to_double(kern.compute_squared_radius_d_object()(points1410.begin(), points1410.end()))) << std::endl; - GUDHI_TEST_FLOAT_EQUALITY_CHECK(f1410, std::sqrt(ms1410.squared_radius())); + GUDHI_TEST_FLOAT_EQUALITY_CHECK(f1410, std::sqrt(cast_to_double(kern.compute_squared_radius_d_object()(points1410.begin(), points1410.end())))); Point_cloud points469; points469.push_back(cech_complex_for_doc.get_point(4)); points469.push_back(cech_complex_for_doc.get_point(6)); points469.push_back(cech_complex_for_doc.get_point(9)); - Min_sphere ms469(dimension, points469.begin(), points469.end()); +// Min_sphere ms469(dimension, points469.begin(), points469.end()); Simplex_tree::Filtration_value f469 = st2.filtration(st2.find({4, 6, 9})); - std::clog << "f469= " << f469 << " | ms469_radius= " << std::sqrt(ms469.squared_radius()) << std::endl; + std::clog << "f469= " << f469 << " | points469_radius= " << std::sqrt(cast_to_double(kern.compute_squared_radius_d_object()(points469.begin(), points469.end()))) << std::endl; - GUDHI_TEST_FLOAT_EQUALITY_CHECK(f469, std::sqrt(ms469.squared_radius())); + GUDHI_TEST_FLOAT_EQUALITY_CHECK(f469, std::sqrt(cast_to_double(kern.compute_squared_radius_d_object()(points469.begin(), points469.end())))); BOOST_CHECK((st2.find({6, 7, 8}) == st2.null_simplex())); BOOST_CHECK((st2.find({3, 5, 7}) == st2.null_simplex())); diff --git a/src/Cech_complex/utilities/cech_persistence.cpp b/src/Cech_complex/utilities/cech_persistence.cpp index daea08e2..ccd7d453 100644 --- a/src/Cech_complex/utilities/cech_persistence.cpp +++ b/src/Cech_complex/utilities/cech_persistence.cpp @@ -16,6 +16,8 @@ #include +#include // For EXACT or SAFE version + #include #include #include // infinity @@ -23,10 +25,14 @@ // Types definition using Simplex_tree = Gudhi::Simplex_tree; using Filtration_value = Simplex_tree::Filtration_value; -using Point = std::vector; +// using Point = std::vector; +// using Point_cloud = std::vector; + +using Kernel = CGAL::Epeck_d; +using Point = typename Kernel::Point_d; using Point_cloud = std::vector; using Points_off_reader = Gudhi::Points_off_reader; -using Cech_complex = Gudhi::cech_complex::Cech_complex; +using Cech_complex = Gudhi::cech_complex::Cech_complex; using Field_Zp = Gudhi::persistent_cohomology::Field_Zp; using Persistent_cohomology = Gudhi::persistent_cohomology::Persistent_cohomology; diff --git a/src/Simplex_tree/include/gudhi/Simplex_tree.h b/src/Simplex_tree/include/gudhi/Simplex_tree.h index 85790baf..f69ed6ec 100644 --- a/src/Simplex_tree/include/gudhi/Simplex_tree.h +++ b/src/Simplex_tree/include/gudhi/Simplex_tree.h @@ -1278,8 +1278,10 @@ class Simplex_tree { intersection.emplace_back(next->first, Node(nullptr, filt)); } } + std::clog << "Hind: after intersection insertion" << std::endl; if (intersection.size() != 0) { // Reverse the order to insert + std::clog << "Hind: declare new siblings" << std::endl; Siblings * new_sib = new Siblings(siblings, // oncles simplex->first, // parent boost::adaptors::reverse(intersection)); // boost::container::ordered_unique_range_t @@ -1288,10 +1290,12 @@ class Simplex_tree { for (auto new_sib_member = new_sib->members().begin(); new_sib_member != new_sib->members().end(); new_sib_member++) { + std::clog << "Hind: check the blocker result" << std::endl; bool blocker_result = block_simplex(new_sib_member); // new_sib member has been blocked by the blocker function // add it to the list to be removed - do not perform it while looping on it if (blocker_result) { + std::clog << "Hind: add to list of blocked sib to be removed" << std::endl; blocked_new_sib_vertex_list.push_back(new_sib_member->first); } } diff --git a/src/common/include/gudhi/distance_functions.h b/src/common/include/gudhi/distance_functions.h index 9bbc62b7..ae5168aa 100644 --- a/src/common/include/gudhi/distance_functions.h +++ b/src/common/include/gudhi/distance_functions.h @@ -18,6 +18,8 @@ #include #include +#include + #include // for std::sqrt #include // for std::decay #include // for std::begin, std::end @@ -63,6 +65,23 @@ class Euclidean_distance { * The points are assumed to have the same dimension. */ class Minimal_enclosing_ball_radius { public: + /** \brief TODO + * + * @param[in] point_1 + * @param[in] point_2 + * @return + * \tparam Point + * + */ + //typename FT = typename Kernel::FT, + template< typename Kernel = CGAL::Epeck_d, + typename Point= typename Kernel::Point_d> + double operator()(const Point& point_1, const Point& point_2) const { + std::clog << "Added template: distance betw points 1 and 2" << std::endl; + Kernel kernel_; + return std::sqrt(CGAL::to_double(kernel_.squared_distance_d_object()(point_1, point_2))) / 2.; + } + /** \brief Minimal_enclosing_ball_radius from two points. * * @param[in] point_1 First point. @@ -75,8 +94,29 @@ class Minimal_enclosing_ball_radius { template< typename Point > typename std::iterator_traits::type>::value_type operator()(const Point& point_1, const Point& point_2) const { + std::clog << "Hind: Minimal_enclosing_ball_radius point1 et 2; Euclidean" << std::endl; + std::clog << "#" << *point_1.begin() << "##" << *point_2.begin() << std::endl; return Euclidean_distance()(point_1, point_2) / 2.; } + + + /** \brief TODO + * + * @param[in] point_cloud The points. + * @return + * \tparam Point_cloud + * + */ + //typename FT = typename Kernel::FT, + template< typename Kernel = CGAL::Epeck_d, + typename Point= typename Kernel::Point_d, + typename Point_cloud = std::vector> + double operator()(const Point_cloud& point_cloud) const { + std::clog << "Added template: distance in point cloud" << std::endl; + Kernel kernel_; + return std::sqrt(CGAL::to_double(kernel_.compute_squared_radius_d_object()(point_cloud.begin(), point_cloud.end()))); + } + /** \brief Minimal_enclosing_ball_radius from a point cloud. * * @param[in] point_cloud The points. @@ -93,6 +133,8 @@ class Minimal_enclosing_ball_radius { typename Coordinate = typename std::iterator_traits::value_type> Coordinate operator()(const Point_cloud& point_cloud) const { + std::clog << "Hind: Minimal_enclosing_ball_radius point cloud; Miniball" << std::endl; + using Min_sphere = Miniball::Miniball>; Min_sphere ms(boost::size(*point_cloud.begin()), point_cloud.begin(), point_cloud.end()); diff --git a/src/common/include/gudhi/graph_simplicial_complex.h b/src/common/include/gudhi/graph_simplicial_complex.h index da9dee7d..9190182c 100644 --- a/src/common/include/gudhi/graph_simplicial_complex.h +++ b/src/common/include/gudhi/graph_simplicial_complex.h @@ -18,6 +18,8 @@ #include #include // for std::tie +#include + namespace Gudhi { /** @file * @brief Graph simplicial complex methods @@ -76,6 +78,7 @@ Proximity_graph compute_proximity_graph( for (auto it_u = points.begin(); it_u != points.end(); ++it_u) { idx_v = idx_u + 1; for (auto it_v = it_u + 1; it_v != points.end(); ++it_v, ++idx_v) { + std::clog << "#idx_u" << idx_u << "#idx_v " << idx_v << std::endl; fil = distance(*it_u, *it_v); if (fil <= threshold) { edges.emplace_back(idx_u, idx_v); -- cgit v1.2.3 From bc28892cbae3d9a9fcc19a0fbcfcc98bb9195ff7 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Wed, 18 Aug 2021 17:45:51 +0200 Subject: Modify the algorithm to get the minimal enclosing ball --- .../include/gudhi/Cech_complex_blocker.h | 167 +++++++++++++-------- 1 file changed, 102 insertions(+), 65 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h index f2cf5ccc..acb53143 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h +++ b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h @@ -22,6 +22,9 @@ #include #include +#include +#include +#include #include // for std::sqrt namespace Gudhi { @@ -57,32 +60,7 @@ class Cech_blocker { using FT = typename Kernel::FT; // Sphere is a pair of point and squared radius. using Sphere = typename std::pair; - - - // Add an int in TDS to save point index in the structure -// using TDS = CGAL::Triangulation_data_structure, -// CGAL::Triangulation_full_cell >; -// -// /** \brief A (Weighted or not) Delaunay triangulation of a set of points in \f$ \mathbb{R}^D\f$.*/ -// using Triangulation = CGAL::Delaunay_triangulation; -// // Vertex_iterator type from CGAL. -// using CGAL_vertex_iterator = typename Triangulation::Vertex_iterator; - - // Structure to switch from simplex tree vertex handle to CGAL vertex iterator. - //using Vector_vertex_iterator = std::vector< CGAL_vertex_iterator >; - - - /** \brief get_point_ returns the point corresponding to the vertex given as parameter. - * Only for internal use for faster access. - * - * @param[in] vertex Vertex handle of the point to retrieve. - * @return The point found. - */ -/* const Point_d& get_point_(std::size_t vertex) const { - return vertex_handle_to_iterator_[vertex]->point(); - } */ /** \internal \brief TODO * \param[in] @@ -102,6 +80,19 @@ class Cech_blocker { return std::make_pair(std::move(c), std::move(r)); } + /** \internal \brief TODO + * \param[in] + * \return */ + template + class CompareSpheresRadii + { + public: + CGAL::NT_converter cast_to_double; + bool operator()(const Sphere& firstSphere, const Sphere& secondSphere) + { + return cast_to_double(firstSphere.second) < cast_to_double(secondSphere.second); + } + }; /** \internal \brief Čech complex blocker operator() - the oracle - assigns the filtration value from the simplex * radius and returns if the simplex expansion must be blocked. @@ -109,51 +100,98 @@ class Cech_blocker { * \return true if the simplex radius is greater than the Cech_complex max_radius*/ bool operator()(Simplex_handle sh) { using Point_cloud = std::vector; - Point_cloud points; + CGAL::NT_converter cast_to_double; + Filtration_value radius = 0.; + std::string key_to_permute; // for each face of simplex sh, test outsider point is indeed inside enclosing ball, if yes, take it and exit loop, otherwise, new sphere is circumsphere of all vertices - for (auto face : sc_ptr_->simplex_vertex_range(sh)) { - ///////////////////////////////////////////////////////////////////// - - + // std::set enclosing_ball_radii; + std::set > enclosing_ball_spheres; + for (auto face : sc_ptr_->boundary_simplex_range(sh)) { + // Find which vertex of sh is missing in face. We rely on the fact that simplex_vertex_range is sorted. + auto longlist = sc_ptr_->simplex_vertex_range(sh); + auto shortlist = sc_ptr_->simplex_vertex_range(face); + + std::clog << "Hind debug: within FACE loop "<< std::endl; + // TODO to remove + for (auto i = std::begin(longlist); i != std::end(longlist);++i) + std::clog << "Hind debug: longlist: " << cc_ptr_->get_point(*i) << std::endl; + for (auto i = std::begin(shortlist); i != std::end(shortlist);++i) + std::clog << "Hind debug: shortlist: " << cc_ptr_->get_point(*i) << std::endl; + + auto longiter = std::begin(longlist); + auto shortiter = std::begin(shortlist); + auto enditer = std::end(shortlist); + while(shortiter != enditer && *longiter == *shortiter) { ++longiter; ++shortiter; } + auto extra = *longiter; // Vertex_handle + + std::clog << "Hind debug: extra vertex: " << cc_ptr_->get_point(extra) << std::endl; - /////////////////////////////////// + Point_cloud face_points; + std::string key, key_extra; + for (auto vertex : sc_ptr_->simplex_vertex_range(face)) { + face_points.push_back(cc_ptr_->get_point(vertex)); + key.append(std::to_string(vertex)); + #ifdef DEBUG_TRACES + std::clog << "#(" << vertex << ")#"; + #endif // DEBUG_TRACES + } + key_extra = key; + key_extra.append(std::to_string(extra)); + key_to_permute = key_extra; + std::clog << "END OF VERTICES " << std::endl; + std::clog << "KEY is: " << key << std::endl; + std::clog << "KEY extra is: " << key_extra << std::endl; + Sphere sph; + auto it = cache_.find(key); + if(it != cache_.end()) + sph = it->second; + else { + sph = get_sphere(face_points.cbegin(), face_points.cend()); + } + if (kernel_.squared_distance_d_object()(sph.first, cc_ptr_->get_point(extra)) <= sph.second) { + radius = std::sqrt(cast_to_double(sph.second)); + #ifdef DEBUG_TRACES + std::clog << "circumcenter: " << sph.first << ", radius: " << radius << std::endl; + #endif // DEBUG_TRACES + std::clog << "distance FYI: " << kernel_.squared_distance_d_object()(sph.first, cc_ptr_->get_point(extra)) << " < " << cast_to_double(sph.second) << std::endl; + // enclosing_ball_radii.insert(radius); + enclosing_ball_spheres.insert(sph); + cache_[key_extra] = sph; + } + else {// TODO to remove + std::clog << "vertex not included BECAUSE DISTANCE: "<< kernel_.squared_distance_d_object()(sph.first, cc_ptr_->get_point(extra)) << " AND RAD SPHERE: " << sph.second << std::endl; + } } - - for (auto vertex : sc_ptr_->simplex_vertex_range(sh)) { - points.push_back(cc_ptr_->get_point(vertex)); -// points.push_back(get_point_(vertex)); -#ifdef DEBUG_TRACES - std::clog << "#(" << vertex << ")#"; -#endif // DEBUG_TRACES + // Get the minimal radius of all faces enclosing balls if exists + if (!enclosing_ball_spheres.empty()) { + // radius = *enclosing_ball_radii.begin(); + Sphere sph_min = *enclosing_ball_spheres.begin(); + radius = std::sqrt(cast_to_double(sph_min.second)); + // std::clog << "CHECK that radius of min sphere is min radius: " << std::sqrt(cast_to_double(sph_min.second)) << "; and RADIUS min: " << radius << std::endl; + // Set all key_to_permute permutations to min sphere in cache + do + { + cache_[key_to_permute] = sph_min; + } while(std::next_permutation(key_to_permute.begin(), key_to_permute.end())); } - // TODO to remove - //Filtration_value radius = Gudhi::Minimal_enclosing_ball_radius()(points); - // Hind: Here change the algo of the enclosing Minimal_enclosing_ball_radius - auto point_to_be_inserted = points.back(); - Sphere sph = get_sphere(points.cbegin(), points.cend()-1); - -// Sphere sph = get_sphere(points.cbegin(), points.cend()-1); - CGAL::NT_converter cast_to_double; -// CGAL::NT_converter cast_point_d_to_double; + std::clog << "END OF FACES ; radius = " << radius << std::endl; - std::clog << "circumcenter: " << sph.first << ", radius: " << std::sqrt(cast_to_double(sph.second))<< std::endl; - // TODO to remove - // Filtration_value test = std::sqrt(CGAL::to_double(sph.second)); - - - // Check that the point to be inserted is already included in the sphere of the simplex containing the preceding points - // TODO instead of Euclidean_distance ; use kernel_.squared_distance_d_object()(c, *begin); - // Add a loop on the three faces to check sphere before computing the circumsphere - // Add the computed sphere as cache; a vector of spheres depending on the number of faces ? - // -// if (Gudhi::Euclidean_distance()(cast_point_d_to_double(sph.first), point_to_be_inserted) > std::sqrt(cast_to_double(sph.second))) -// FT r = kernel_.squared_distance_d_object()(sph.first, sph.first); //*(points.cend()-1)); - if (kernel_.squared_distance_d_object()(sph.first, point_to_be_inserted) > sph.second) - sph = get_sphere(points.cbegin(), points.cend()); - - Filtration_value radius = std::sqrt(cast_to_double(sph.second)); + if (radius == 0.) { // Spheres of each face don't contain the whole simplex + Point_cloud points; + for (auto vertex : sc_ptr_->simplex_vertex_range(sh)) { + points.push_back(cc_ptr_->get_point(vertex)); + } + Sphere sph = get_sphere(points.cbegin(), points.cend()); + radius = std::sqrt(cast_to_double(sph.second)); + std::clog << "GLOBAL SPHERE radius = " << radius << std::endl; + // Set all key_to_permute permutations to sphere in cache + do + { + cache_[key_to_permute] = sph; + } while(std::next_permutation(key_to_permute.begin(), key_to_permute.end())); + } #ifdef DEBUG_TRACES @@ -170,8 +208,7 @@ class Cech_blocker { SimplicialComplexForCech* sc_ptr_; Cech_complex* cc_ptr_; Kernel kernel_; - //Vector_vertex_iterator vertex_handle_to_iterator_; - + std::map cache_; }; } // namespace cech_complex -- cgit v1.2.3 From 839093da012bda0ee16744f1e340a8a8eb04f0af Mon Sep 17 00:00:00 2001 From: Hind-M Date: Mon, 6 Sep 2021 17:19:32 +0200 Subject: Remove unnecessary key permutations storage in cache --- .../example/cech_complex_example_from_points.cpp | 16 ++++- .../include/gudhi/Cech_complex_blocker.h | 70 ++++++++++++++-------- 2 files changed, 59 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/example/cech_complex_example_from_points.cpp b/src/Cech_complex/example/cech_complex_example_from_points.cpp index ac17fc73..e78ad51d 100644 --- a/src/Cech_complex/example/cech_complex_example_from_points.cpp +++ b/src/Cech_complex/example/cech_complex_example_from_points.cpp @@ -43,6 +43,18 @@ int main() { std::vector point5({3. + std::sqrt(3.), 3.}); points.emplace_back(point5.begin(), point5.end()); +/* + std::vector point6({1., 4.}); + points.emplace_back(point6.begin(), point6.end()); + std::vector point7({3., 4.}); + points.emplace_back(point7.begin(), point7.end()); + std::vector point8({2., 4. + std::sqrt(3.)}); + points.emplace_back(point8.begin(), point8.end()); + std::vector point9({0., 4.}); + points.emplace_back(point9.begin(), point9.end()); + std::vector point10({-0.5, 2.}); + points.emplace_back(point10.begin(), point10.end()); +*/ // points.emplace_back(Point(std::vector({1., 0.}))); // points.emplace_back(Point(std::vector({0., 1.}))); // points.emplace_back(Point(std::vector({2., 1.}))); @@ -68,13 +80,13 @@ int main() { // ---------------------------------------------------------------------------- // Init of a Cech complex from points // ---------------------------------------------------------------------------- - Filtration_value max_radius = 10.; + Filtration_value max_radius = 10.; //100.; std::clog << "Hind: Just before the Cech constructor" << std::endl; Cech_complex cech_complex_from_points(points, max_radius); std::clog << "Hind: Just after the Cech constructor" << std::endl; Simplex_tree stree; - cech_complex_from_points.create_complex(stree, 3); + cech_complex_from_points.create_complex(stree, 3); //6 // ---------------------------------------------------------------------------- // Display information about the one skeleton Cech complex // ---------------------------------------------------------------------------- diff --git a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h index acb53143..0fc76c6d 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h +++ b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h @@ -102,7 +102,8 @@ class Cech_blocker { using Point_cloud = std::vector; CGAL::NT_converter cast_to_double; Filtration_value radius = 0.; - std::string key_to_permute; +// std::string key_to_permute; + std::vector faces_keys; // for each face of simplex sh, test outsider point is indeed inside enclosing ball, if yes, take it and exit loop, otherwise, new sphere is circumsphere of all vertices // std::set enclosing_ball_radii; @@ -113,12 +114,12 @@ class Cech_blocker { auto longlist = sc_ptr_->simplex_vertex_range(sh); auto shortlist = sc_ptr_->simplex_vertex_range(face); - std::clog << "Hind debug: within FACE loop "<< std::endl; +// std::clog << "Hind debug: within FACE loop "<< std::endl; // TODO to remove - for (auto i = std::begin(longlist); i != std::end(longlist);++i) - std::clog << "Hind debug: longlist: " << cc_ptr_->get_point(*i) << std::endl; - for (auto i = std::begin(shortlist); i != std::end(shortlist);++i) - std::clog << "Hind debug: shortlist: " << cc_ptr_->get_point(*i) << std::endl; +// for (auto i = std::begin(longlist); i != std::end(longlist);++i) +// std::clog << "Hind debug: longlist: " << cc_ptr_->get_point(*i) << std::endl; +// for (auto i = std::begin(shortlist); i != std::end(shortlist);++i) +// std::clog << "Hind debug: shortlist: " << cc_ptr_->get_point(*i) << std::endl; auto longiter = std::begin(longlist); auto shortiter = std::begin(shortlist); @@ -126,7 +127,7 @@ class Cech_blocker { while(shortiter != enditer && *longiter == *shortiter) { ++longiter; ++shortiter; } auto extra = *longiter; // Vertex_handle - std::clog << "Hind debug: extra vertex: " << cc_ptr_->get_point(extra) << std::endl; +// std::clog << "Hind debug: extra vertex: " << cc_ptr_->get_point(extra) << std::endl; Point_cloud face_points; std::string key, key_extra; @@ -139,10 +140,11 @@ class Cech_blocker { } key_extra = key; key_extra.append(std::to_string(extra)); - key_to_permute = key_extra; - std::clog << "END OF VERTICES " << std::endl; - std::clog << "KEY is: " << key << std::endl; - std::clog << "KEY extra is: " << key_extra << std::endl; + faces_keys.push_back(key_extra); +// key_to_permute = key_extra; +// std::clog << "END OF VERTICES " << std::endl; +// std::clog << "KEY is: " << key << std::endl; +// std::clog << "KEY extra is: " << key_extra << std::endl; Sphere sph; auto it = cache_.find(key); if(it != cache_.end()) @@ -155,14 +157,14 @@ class Cech_blocker { #ifdef DEBUG_TRACES std::clog << "circumcenter: " << sph.first << ", radius: " << radius << std::endl; #endif // DEBUG_TRACES - std::clog << "distance FYI: " << kernel_.squared_distance_d_object()(sph.first, cc_ptr_->get_point(extra)) << " < " << cast_to_double(sph.second) << std::endl; +// std::clog << "distance FYI: " << kernel_.squared_distance_d_object()(sph.first, cc_ptr_->get_point(extra)) << " < " << cast_to_double(sph.second) << std::endl; // enclosing_ball_radii.insert(radius); enclosing_ball_spheres.insert(sph); cache_[key_extra] = sph; } - else {// TODO to remove - std::clog << "vertex not included BECAUSE DISTANCE: "<< kernel_.squared_distance_d_object()(sph.first, cc_ptr_->get_point(extra)) << " AND RAD SPHERE: " << sph.second << std::endl; - } +// else {// TODO to remove +// std::clog << "vertex not included BECAUSE DISTANCE: "<< kernel_.squared_distance_d_object()(sph.first, cc_ptr_->get_point(extra)) << " AND RAD SPHERE: " << sph.second << std::endl; +// } } // Get the minimal radius of all faces enclosing balls if exists if (!enclosing_ball_spheres.empty()) { @@ -171,12 +173,21 @@ class Cech_blocker { radius = std::sqrt(cast_to_double(sph_min.second)); // std::clog << "CHECK that radius of min sphere is min radius: " << std::sqrt(cast_to_double(sph_min.second)) << "; and RADIUS min: " << radius << std::endl; // Set all key_to_permute permutations to min sphere in cache - do - { - cache_[key_to_permute] = sph_min; - } while(std::next_permutation(key_to_permute.begin(), key_to_permute.end())); +// do +// { +// if (cache_.find(key_to_permute) != cache_.end()) { +// if (cast_to_double(cache_[key_to_permute].second) > cast_to_double(sph_min.second)) +// cache_[key_to_permute] = sph_min; +// } +// else { +// cache_[key_to_permute] = sph_min; +// } +// } while(std::next_permutation(key_to_permute.begin(), key_to_permute.end())); + for (auto k : faces_keys) { + cache_[k] = sph_min; + } } - std::clog << "END OF FACES ; radius = " << radius << std::endl; +// std::clog << "END OF FACES ; radius = " << radius << std::endl; if (radius == 0.) { // Spheres of each face don't contain the whole simplex Point_cloud points; @@ -185,12 +196,21 @@ class Cech_blocker { } Sphere sph = get_sphere(points.cbegin(), points.cend()); radius = std::sqrt(cast_to_double(sph.second)); - std::clog << "GLOBAL SPHERE radius = " << radius << std::endl; +// std::clog << "GLOBAL SPHERE radius = " << radius << std::endl; // Set all key_to_permute permutations to sphere in cache - do - { - cache_[key_to_permute] = sph; - } while(std::next_permutation(key_to_permute.begin(), key_to_permute.end())); +// do +// { +// // if (cache_.find(key_to_permute) != cache_.end()) { +// // if (cast_to_double(cache_[key_to_permute].second) > cast_to_double(sph.second)) +// // cache_[key_to_permute] = sph; +// // } +// // else { +// // cache_[key_to_permute] = sph; +// // } +// } while(std::next_permutation(key_to_permute.begin(), key_to_permute.end())); +// for (auto k : faces_keys) { +// cache_[k] = sph; +// } } -- cgit v1.2.3 From 44f754ee58aeee043891f4494892798b9807374b Mon Sep 17 00:00:00 2001 From: Hind-M Date: Wed, 8 Sep 2021 14:55:48 +0200 Subject: Change cache so that the index of the stored sphere is used as key --- .../include/gudhi/Cech_complex_blocker.h | 44 ++++++++++++++-------- 1 file changed, 29 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h index 0fc76c6d..3cac9ee2 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h +++ b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h @@ -103,7 +103,7 @@ class Cech_blocker { CGAL::NT_converter cast_to_double; Filtration_value radius = 0.; // std::string key_to_permute; - std::vector faces_keys; +// std::vector faces_keys; // for each face of simplex sh, test outsider point is indeed inside enclosing ball, if yes, take it and exit loop, otherwise, new sphere is circumsphere of all vertices // std::set enclosing_ball_radii; @@ -130,28 +130,35 @@ class Cech_blocker { // std::clog << "Hind debug: extra vertex: " << cc_ptr_->get_point(extra) << std::endl; Point_cloud face_points; - std::string key, key_extra; +// std::string key, key_extra; for (auto vertex : sc_ptr_->simplex_vertex_range(face)) { face_points.push_back(cc_ptr_->get_point(vertex)); - key.append(std::to_string(vertex)); +// key.append(std::to_string(vertex)); #ifdef DEBUG_TRACES std::clog << "#(" << vertex << ")#"; #endif // DEBUG_TRACES } - key_extra = key; - key_extra.append(std::to_string(extra)); - faces_keys.push_back(key_extra); +// key_extra = key; +// key_extra.append(std::to_string(extra)); +// faces_keys.push_back(key_extra); // key_to_permute = key_extra; // std::clog << "END OF VERTICES " << std::endl; // std::clog << "KEY is: " << key << std::endl; // std::clog << "KEY extra is: " << key_extra << std::endl; Sphere sph; - auto it = cache_.find(key); - if(it != cache_.end()) - sph = it->second; + auto k = sc_ptr_->key(sh); + if(k != sc_ptr_->null_key()) + sph = cache_[k]; else { sph = get_sphere(face_points.cbegin(), face_points.cend()); } +// auto it = cache_.find(key); +// if(it != cache_.end()) +// sph = it->second; +// else { +// sph = get_sphere(face_points.cbegin(), face_points.cend()); +// } + if (kernel_.squared_distance_d_object()(sph.first, cc_ptr_->get_point(extra)) <= sph.second) { radius = std::sqrt(cast_to_double(sph.second)); #ifdef DEBUG_TRACES @@ -160,7 +167,9 @@ class Cech_blocker { // std::clog << "distance FYI: " << kernel_.squared_distance_d_object()(sph.first, cc_ptr_->get_point(extra)) << " < " << cast_to_double(sph.second) << std::endl; // enclosing_ball_radii.insert(radius); enclosing_ball_spheres.insert(sph); - cache_[key_extra] = sph; +// cache_[key_extra] = sph; +// sc_ptr_->assign_key(sh, cache_.size()); +// cache_.push_back(sph); } // else {// TODO to remove // std::clog << "vertex not included BECAUSE DISTANCE: "<< kernel_.squared_distance_d_object()(sph.first, cc_ptr_->get_point(extra)) << " AND RAD SPHERE: " << sph.second << std::endl; @@ -174,7 +183,7 @@ class Cech_blocker { // std::clog << "CHECK that radius of min sphere is min radius: " << std::sqrt(cast_to_double(sph_min.second)) << "; and RADIUS min: " << radius << std::endl; // Set all key_to_permute permutations to min sphere in cache // do -// { +// { // if (cache_.find(key_to_permute) != cache_.end()) { // if (cast_to_double(cache_[key_to_permute].second) > cast_to_double(sph_min.second)) // cache_[key_to_permute] = sph_min; @@ -183,9 +192,11 @@ class Cech_blocker { // cache_[key_to_permute] = sph_min; // } // } while(std::next_permutation(key_to_permute.begin(), key_to_permute.end())); - for (auto k : faces_keys) { - cache_[k] = sph_min; - } +// for (auto k : faces_keys) { +// cache_[k] = sph_min; +// } + sc_ptr_->assign_key(sh, cache_.size()); + cache_.push_back(sph_min); } // std::clog << "END OF FACES ; radius = " << radius << std::endl; @@ -211,6 +222,8 @@ class Cech_blocker { // for (auto k : faces_keys) { // cache_[k] = sph; // } +// sc_ptr_->assign_key(sh, cache_.size()); +// cache_.push_back(sph); } @@ -228,7 +241,8 @@ class Cech_blocker { SimplicialComplexForCech* sc_ptr_; Cech_complex* cc_ptr_; Kernel kernel_; - std::map cache_; +// std::map cache_; + std::vector cache_; }; } // namespace cech_complex -- cgit v1.2.3 From 767d9fca5da2d3dd9698a5c27e9bedc159271f67 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Thu, 30 Sep 2021 11:05:17 +0200 Subject: Move minimal enclosing balls cache to Cech_complex.h instead of the blocker Modify cech test to be more relevant regarding the current algorithm Do some cleaning --- .../benchmark/cech_complex_benchmark.cpp | 2 +- .../example/cech_complex_example_from_points.cpp | 42 +------ .../example/cech_complex_step_by_step.cpp | 8 -- src/Cech_complex/include/gudhi/Cech_complex.h | 38 +++---- .../include/gudhi/Cech_complex_blocker.h | 121 +++------------------ src/Cech_complex/test/test_cech_complex.cpp | 37 ++----- src/Cech_complex/utilities/cech_persistence.cpp | 4 +- src/Simplex_tree/include/gudhi/Simplex_tree.h | 4 - src/common/include/gudhi/distance_functions.h | 20 +--- .../include/gudhi/graph_simplicial_complex.h | 3 - 10 files changed, 54 insertions(+), 225 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/benchmark/cech_complex_benchmark.cpp b/src/Cech_complex/benchmark/cech_complex_benchmark.cpp index c332c656..4a1aa06e 100644 --- a/src/Cech_complex/benchmark/cech_complex_benchmark.cpp +++ b/src/Cech_complex/benchmark/cech_complex_benchmark.cpp @@ -33,7 +33,7 @@ using Points_off_reader = Gudhi::Points_off_reader; using Proximity_graph = Gudhi::Proximity_graph; using Rips_complex = Gudhi::rips_complex::Rips_complex; using Kernel = CGAL::Epeck_d; -using Cech_complex = Gudhi::cech_complex::Cech_complex; +using Cech_complex = Gudhi::cech_complex::Cech_complex; class Minimal_enclosing_ball_radius { public: diff --git a/src/Cech_complex/example/cech_complex_example_from_points.cpp b/src/Cech_complex/example/cech_complex_example_from_points.cpp index e78ad51d..78861951 100644 --- a/src/Cech_complex/example/cech_complex_example_from_points.cpp +++ b/src/Cech_complex/example/cech_complex_example_from_points.cpp @@ -10,25 +10,15 @@ int main() { // Type definitions -// using Point_cloud = std::vector>; using Simplex_tree = Gudhi::Simplex_tree; using Filtration_value = Simplex_tree::Filtration_value; using Kernel = CGAL::Epeck_d; using FT = typename Kernel::FT; using Point = typename Kernel::Point_d; using Point_cloud = std::vector; - using Cech_complex = Gudhi::cech_complex::Cech_complex; + using Cech_complex = Gudhi::cech_complex::Cech_complex; Point_cloud points; -// points.push_back({1., 0.}); // 0 -// points.push_back({0., 1.}); // 1 -// points.push_back({2., 1.}); // 2 -// points.push_back({3., 2.}); // 3 -// points.push_back({0., 3.}); // 4 -// points.push_back({3. + std::sqrt(3.), 3.}); // 5 - -// std::vector point({0.0, 0.0, 0.0, 0.0}); -// points.emplace_back(point.begin(), point.end()); std::vector point0({1., 0.}); points.emplace_back(point0.begin(), point0.end()); @@ -42,8 +32,6 @@ int main() { points.emplace_back(point4.begin(), point4.end()); std::vector point5({3. + std::sqrt(3.), 3.}); points.emplace_back(point5.begin(), point5.end()); - -/* std::vector point6({1., 4.}); points.emplace_back(point6.begin(), point6.end()); std::vector point7({3., 4.}); @@ -54,39 +42,15 @@ int main() { points.emplace_back(point9.begin(), point9.end()); std::vector point10({-0.5, 2.}); points.emplace_back(point10.begin(), point10.end()); -*/ -// points.emplace_back(Point(std::vector({1., 0.}))); -// points.emplace_back(Point(std::vector({0., 1.}))); -// points.emplace_back(Point(std::vector({2., 1.}))); -// points.emplace_back(Point(std::vector({3., 2.}))); -// points.emplace_back(Point(std::vector({0., 3.}))); -// points.emplace_back(Point(std::vector({3. + std::sqrt(3.), 3.}))); - - -// points.push_back(Point(1.0, 0.0)); -// points.push_back(Point(0.0, 1.0)); -// points.push_back(Point(2.0, 1.0)); -// points.push_back(Point(3.0, 2.0)); -// points.push_back(Point(0.0, 3.0)); -// points.push_back(Point(3.0 + std::sqrt(3.0), 3.0)); - - -// points.push_back({1., 4.}); // 6 -// points.push_back({3., 4.}); // 7 -// points.push_back({2., 4. + std::sqrt(3.)}); // 8 -// points.push_back({0., 4.}); // 9 -// points.push_back({-0.5, 2.}); // 10 // ---------------------------------------------------------------------------- // Init of a Cech complex from points // ---------------------------------------------------------------------------- - Filtration_value max_radius = 10.; //100.; - std::clog << "Hind: Just before the Cech constructor" << std::endl; + Filtration_value max_radius = 100.; //100.; Cech_complex cech_complex_from_points(points, max_radius); - std::clog << "Hind: Just after the Cech constructor" << std::endl; Simplex_tree stree; - cech_complex_from_points.create_complex(stree, 3); //6 + cech_complex_from_points.create_complex(stree, 6); //6 // ---------------------------------------------------------------------------- // Display information about the one skeleton Cech complex // ---------------------------------------------------------------------------- diff --git a/src/Cech_complex/example/cech_complex_step_by_step.cpp b/src/Cech_complex/example/cech_complex_step_by_step.cpp index ac08e6cc..44e7f945 100644 --- a/src/Cech_complex/example/cech_complex_step_by_step.cpp +++ b/src/Cech_complex/example/cech_complex_step_by_step.cpp @@ -13,8 +13,6 @@ #include #include -#include // TODO to remove ? - #include #include @@ -36,7 +34,6 @@ using Simplex_tree = Gudhi::Simplex_tree<>; using Simplex_handle = Simplex_tree::Simplex_handle; using Filtration_value = Simplex_tree::Filtration_value; -// using Point = std::vector; using Kernel = CGAL::Epeck_d; using Point = typename Kernel::Point_d; using Points_off_reader = Gudhi::Points_off_reader; @@ -45,9 +42,6 @@ using Proximity_graph = Gudhi::Proximity_graph; class Cech_blocker { private: using Point_cloud = std::vector; -// using Point_iterator = Point_cloud::const_iterator; -// using Coordinate_iterator = Point::const_iterator; -// using Min_sphere = Gudhi::Miniball::Miniball>; public: bool operator()(Simplex_handle sh) { @@ -67,14 +61,12 @@ class Cech_blocker { } Cech_blocker(Simplex_tree& simplex_tree, Filtration_value max_radius, const std::vector& point_cloud) : simplex_tree_(simplex_tree), max_radius_(max_radius), point_cloud_(point_cloud) { -// dimension_ = point_cloud_[0].size(); } private: Simplex_tree simplex_tree_; Filtration_value max_radius_; std::vector point_cloud_; -// int dimension_; }; void program_options(int argc, char* argv[], std::string& off_file_points, Filtration_value& max_radius, int& dim_max); diff --git a/src/Cech_complex/include/gudhi/Cech_complex.h b/src/Cech_complex/include/gudhi/Cech_complex.h index 2c6f202a..32a78aec 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex.h +++ b/src/Cech_complex/include/gudhi/Cech_complex.h @@ -40,7 +40,7 @@ namespace cech_complex { * \tparam ForwardPointRange must be a range for which `std::begin()` and `std::end()` methods return input * iterators on a point. `std::begin()` and `std::end()` methods are also required for a point. */ -template +template class Cech_complex { private: // Required by compute_proximity_graph @@ -48,17 +48,17 @@ class Cech_complex { using Filtration_value = typename SimplicialComplexForProximityGraph::Filtration_value; using Proximity_graph = Gudhi::Proximity_graph; - // Retrieve Coordinate type from ForwardPointRange -// using Point_from_range_iterator = typename boost::range_const_iterator::type; -// using Point_from_range = typename std::iterator_traits::value_type; -// using Coordinate_iterator = typename boost::range_const_iterator::type; -// using Coordinate = typename std::iterator_traits::value_type; - public: - // Point and Point_cloud type definition - //using Point = std::vector; - using Point = typename Kernel::Point_d; - using Point_cloud = std::vector; + + using cech_blocker = Cech_blocker; + + using Point_d = typename cech_blocker::Point_d; + using Point_cloud = std::vector; + + // Numeric type of coordinates in the kernel + using FT = typename cech_blocker::FT; + // Sphere is a pair of point and squared radius. + using Sphere = typename std::pair; public: /** \brief Cech_complex constructor from a list of points. @@ -77,7 +77,6 @@ class Cech_complex { point_cloud_.assign(points.begin(), points.end()); - std::clog << "Hind: Just before the graph compute" << std::endl; cech_skeleton_graph_ = Gudhi::compute_proximity_graph( point_cloud_, max_radius_, Gudhi::Minimal_enclosing_ball_radius()); } @@ -90,19 +89,14 @@ class Cech_complex { * @exception std::invalid_argument In debug mode, if `complex.num_vertices()` does not return 0. * */ - template void create_complex(SimplicialComplexForCechComplex& complex, int dim_max) { - std::clog << "Hind: in create complex" << std::endl; GUDHI_CHECK(complex.num_vertices() == 0, std::invalid_argument("Cech_complex::create_complex - simplicial complex is not empty")); // insert the proximity graph in the simplicial complex - std::clog << "Hind: before insert_graph" << std::endl; complex.insert_graph(cech_skeleton_graph_); // expand the graph until dimension dim_max - std::clog << "Hind: before expansion_with_blockers" << std::endl; - complex.expansion_with_blockers(dim_max, - Cech_blocker(&complex, this)); + complex.expansion_with_blockers(dim_max, cech_blocker(&complex, this)); } /** @return max_radius value given at construction. */ @@ -111,12 +105,18 @@ class Cech_complex { /** @param[in] vertex Point position in the range. * @return The point. */ - const Point& get_point(Vertex_handle vertex) const { return point_cloud_[vertex]; } + const Point_d& get_point(Vertex_handle vertex) const { return point_cloud_[vertex]; } + + /** + * @return Vector of cached spheres. + */ + std::vector & get_cache() { return cache_; } private: Proximity_graph cech_skeleton_graph_; Filtration_value max_radius_; Point_cloud point_cloud_; + std::vector cache_; }; } // namespace cech_complex diff --git a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h index 3cac9ee2..5edd005d 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h +++ b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h @@ -11,20 +11,11 @@ #ifndef CECH_COMPLEX_BLOCKER_H_ #define CECH_COMPLEX_BLOCKER_H_ -// TODO to remove -#include // for Gudhi::Minimal_enclosing_ball_radius - -#include // for CGAL::to_double -#include // -// #include -#include // For EXACT or SAFE version -#include +#include // for casting from FT to double #include #include #include -#include -#include #include // for std::sqrt namespace Gudhi { @@ -48,7 +39,6 @@ namespace cech_complex { template class Cech_blocker { private: -// using Point_cloud = typename Cech_complex::Point_cloud; using Simplex_handle = typename SimplicialComplexForCech::Simplex_handle; using Filtration_value = typename SimplicialComplexForCech::Filtration_value; @@ -61,28 +51,18 @@ class Cech_blocker { // Sphere is a pair of point and squared radius. using Sphere = typename std::pair; - - /** \internal \brief TODO - * \param[in] - * \return */ template FT get_squared_radius(PointIterator begin, PointIterator end) const { return kernel_.compute_squared_radius_d_object()(begin, end); } - - /** \internal \brief TODO - * \param[in] - * \return */ + template Sphere get_sphere(PointIterator begin, PointIterator end) const { Point_d c = kernel_.construct_circumcenter_d_object()(begin, end); FT r = kernel_.squared_distance_d_object()(c, *begin); return std::make_pair(std::move(c), std::move(r)); } - - /** \internal \brief TODO - * \param[in] - * \return */ + template class CompareSpheresRadii { @@ -93,7 +73,7 @@ class Cech_blocker { return cast_to_double(firstSphere.second) < cast_to_double(secondSphere.second); } }; - + /** \internal \brief Čech complex blocker operator() - the oracle - assigns the filtration value from the simplex * radius and returns if the simplex expansion must be blocked. * \param[in] sh The Simplex_handle. @@ -102,104 +82,54 @@ class Cech_blocker { using Point_cloud = std::vector; CGAL::NT_converter cast_to_double; Filtration_value radius = 0.; -// std::string key_to_permute; -// std::vector faces_keys; - + // for each face of simplex sh, test outsider point is indeed inside enclosing ball, if yes, take it and exit loop, otherwise, new sphere is circumsphere of all vertices - // std::set enclosing_ball_radii; std::set > enclosing_ball_spheres; for (auto face : sc_ptr_->boundary_simplex_range(sh)) { - // Find which vertex of sh is missing in face. We rely on the fact that simplex_vertex_range is sorted. auto longlist = sc_ptr_->simplex_vertex_range(sh); auto shortlist = sc_ptr_->simplex_vertex_range(face); -// std::clog << "Hind debug: within FACE loop "<< std::endl; - // TODO to remove -// for (auto i = std::begin(longlist); i != std::end(longlist);++i) -// std::clog << "Hind debug: longlist: " << cc_ptr_->get_point(*i) << std::endl; -// for (auto i = std::begin(shortlist); i != std::end(shortlist);++i) -// std::clog << "Hind debug: shortlist: " << cc_ptr_->get_point(*i) << std::endl; - auto longiter = std::begin(longlist); auto shortiter = std::begin(shortlist); auto enditer = std::end(shortlist); while(shortiter != enditer && *longiter == *shortiter) { ++longiter; ++shortiter; } auto extra = *longiter; // Vertex_handle -// std::clog << "Hind debug: extra vertex: " << cc_ptr_->get_point(extra) << std::endl; - Point_cloud face_points; -// std::string key, key_extra; for (auto vertex : sc_ptr_->simplex_vertex_range(face)) { face_points.push_back(cc_ptr_->get_point(vertex)); -// key.append(std::to_string(vertex)); #ifdef DEBUG_TRACES std::clog << "#(" << vertex << ")#"; #endif // DEBUG_TRACES } -// key_extra = key; -// key_extra.append(std::to_string(extra)); -// faces_keys.push_back(key_extra); -// key_to_permute = key_extra; -// std::clog << "END OF VERTICES " << std::endl; -// std::clog << "KEY is: " << key << std::endl; -// std::clog << "KEY extra is: " << key_extra << std::endl; Sphere sph; - auto k = sc_ptr_->key(sh); - if(k != sc_ptr_->null_key()) - sph = cache_[k]; + auto face_sh = sc_ptr_->find(sc_ptr_->simplex_vertex_range(face)); + auto k = sc_ptr_->key(face_sh); + if(k != sc_ptr_->null_key()) { + sph = cc_ptr_->get_cache().at(k); + } else { sph = get_sphere(face_points.cbegin(), face_points.cend()); } -// auto it = cache_.find(key); -// if(it != cache_.end()) -// sph = it->second; -// else { -// sph = get_sphere(face_points.cbegin(), face_points.cend()); -// } if (kernel_.squared_distance_d_object()(sph.first, cc_ptr_->get_point(extra)) <= sph.second) { radius = std::sqrt(cast_to_double(sph.second)); #ifdef DEBUG_TRACES std::clog << "circumcenter: " << sph.first << ", radius: " << radius << std::endl; #endif // DEBUG_TRACES -// std::clog << "distance FYI: " << kernel_.squared_distance_d_object()(sph.first, cc_ptr_->get_point(extra)) << " < " << cast_to_double(sph.second) << std::endl; - // enclosing_ball_radii.insert(radius); enclosing_ball_spheres.insert(sph); -// cache_[key_extra] = sph; -// sc_ptr_->assign_key(sh, cache_.size()); -// cache_.push_back(sph); } -// else {// TODO to remove -// std::clog << "vertex not included BECAUSE DISTANCE: "<< kernel_.squared_distance_d_object()(sph.first, cc_ptr_->get_point(extra)) << " AND RAD SPHERE: " << sph.second << std::endl; -// } } // Get the minimal radius of all faces enclosing balls if exists if (!enclosing_ball_spheres.empty()) { - // radius = *enclosing_ball_radii.begin(); Sphere sph_min = *enclosing_ball_spheres.begin(); radius = std::sqrt(cast_to_double(sph_min.second)); - // std::clog << "CHECK that radius of min sphere is min radius: " << std::sqrt(cast_to_double(sph_min.second)) << "; and RADIUS min: " << radius << std::endl; - // Set all key_to_permute permutations to min sphere in cache -// do -// { -// if (cache_.find(key_to_permute) != cache_.end()) { -// if (cast_to_double(cache_[key_to_permute].second) > cast_to_double(sph_min.second)) -// cache_[key_to_permute] = sph_min; -// } -// else { -// cache_[key_to_permute] = sph_min; -// } -// } while(std::next_permutation(key_to_permute.begin(), key_to_permute.end())); -// for (auto k : faces_keys) { -// cache_[k] = sph_min; -// } - sc_ptr_->assign_key(sh, cache_.size()); - cache_.push_back(sph_min); + + sc_ptr_->assign_key(sh, cc_ptr_->get_cache().size()); + cc_ptr_->get_cache().push_back(sph_min); } -// std::clog << "END OF FACES ; radius = " << radius << std::endl; - + if (radius == 0.) { // Spheres of each face don't contain the whole simplex Point_cloud points; for (auto vertex : sc_ptr_->simplex_vertex_range(sh)) { @@ -207,25 +137,10 @@ class Cech_blocker { } Sphere sph = get_sphere(points.cbegin(), points.cend()); radius = std::sqrt(cast_to_double(sph.second)); -// std::clog << "GLOBAL SPHERE radius = " << radius << std::endl; - // Set all key_to_permute permutations to sphere in cache -// do -// { -// // if (cache_.find(key_to_permute) != cache_.end()) { -// // if (cast_to_double(cache_[key_to_permute].second) > cast_to_double(sph.second)) -// // cache_[key_to_permute] = sph; -// // } -// // else { -// // cache_[key_to_permute] = sph; -// // } -// } while(std::next_permutation(key_to_permute.begin(), key_to_permute.end())); -// for (auto k : faces_keys) { -// cache_[k] = sph; -// } -// sc_ptr_->assign_key(sh, cache_.size()); -// cache_.push_back(sph); - } + sc_ptr_->assign_key(sh, cc_ptr_->get_cache().size()); + cc_ptr_->get_cache().push_back(sph); + } #ifdef DEBUG_TRACES if (radius > cc_ptr_->max_radius()) std::clog << "radius > max_radius => expansion is blocked\n"; @@ -241,8 +156,6 @@ class Cech_blocker { SimplicialComplexForCech* sc_ptr_; Cech_complex* cc_ptr_; Kernel kernel_; -// std::map cache_; - std::vector cache_; }; } // namespace cech_complex diff --git a/src/Cech_complex/test/test_cech_complex.cpp b/src/Cech_complex/test/test_cech_complex.cpp index 81efd6ae..51b466da 100644 --- a/src/Cech_complex/test/test_cech_complex.cpp +++ b/src/Cech_complex/test/test_cech_complex.cpp @@ -24,26 +24,19 @@ #include #include #include -#include #include // For EXACT or SAFE version - // Type definitions using Simplex_tree = Gudhi::Simplex_tree<>; using Filtration_value = Simplex_tree::Filtration_value; -//using Point = std::vector; using Kernel = CGAL::Epeck_d; using FT = typename Kernel::FT; using Point = typename Kernel::Point_d; using Point_cloud = std::vector; using Points_off_reader = Gudhi::Points_off_reader; -using Cech_complex = Gudhi::cech_complex::Cech_complex; - -// using Point_iterator = Point_cloud::const_iterator; -// using Coordinate_iterator = Point::const_iterator; -// using Min_sphere = Gudhi::Miniball::Miniball>; +using Cech_complex = Gudhi::cech_complex::Cech_complex; BOOST_AUTO_TEST_CASE(Cech_complex_for_documentation) { // ---------------------------------------------------------------------------- @@ -52,18 +45,6 @@ BOOST_AUTO_TEST_CASE(Cech_complex_for_documentation) { // // ---------------------------------------------------------------------------- Point_cloud points; -// points.push_back({1., 0.}); // 0 -// points.push_back({0., 1.}); // 1 -// points.push_back({2., 1.}); // 2 -// points.push_back({3., 2.}); // 3 -// points.push_back({0., 3.}); // 4 -// points.push_back({3. + std::sqrt(3.), 3.}); // 5 -// points.push_back({1., 4.}); // 6 -// points.push_back({3., 4.}); // 7 -// points.push_back({2., 4. + std::sqrt(3.)}); // 8 -// points.push_back({0., 4.}); // 9 -// points.push_back({-0.5, 2.}); // 10 - std::vector point0({1., 0.}); points.emplace_back(point0.begin(), point0.end()); @@ -156,36 +137,32 @@ BOOST_AUTO_TEST_CASE(Cech_complex_for_documentation) { for (std::size_t vertex = 0; vertex <= 2; vertex++) { points012.push_back(cech_complex_for_doc.get_point(vertex)); } -// std::size_t dimension = points[0].end() - points[0].begin(); -// Min_sphere ms012(dimension, points012.begin(), points012.end()); Kernel kern; Simplex_tree::Filtration_value f012 = st2.filtration(st2.find({0, 1, 2})); - CGAL::NT_converter cast_to_double; - std::clog << "f012= " << f012 << " | points012_radius= " << std::sqrt(cast_to_double(kern.compute_squared_radius_d_object()(points012.begin(), points012.end()))) << std::endl; - + std::clog << "f012= " << f012 << std::endl; + CGAL::NT_converter cast_to_double; GUDHI_TEST_FLOAT_EQUALITY_CHECK(f012, std::sqrt(cast_to_double(kern.compute_squared_radius_d_object()(points012.begin(), points012.end())))); Point_cloud points1410; points1410.push_back(cech_complex_for_doc.get_point(1)); points1410.push_back(cech_complex_for_doc.get_point(4)); points1410.push_back(cech_complex_for_doc.get_point(10)); -// Min_sphere ms1410(dimension, points1410.begin(), points1410.end()); Simplex_tree::Filtration_value f1410 = st2.filtration(st2.find({1, 4, 10})); - std::clog << "f1410= " << f1410 << " | points1410_radius= " << std::sqrt(cast_to_double(kern.compute_squared_radius_d_object()(points1410.begin(), points1410.end()))) << std::endl; + std::clog << "f1410= " << f1410 << std::endl; - GUDHI_TEST_FLOAT_EQUALITY_CHECK(f1410, std::sqrt(cast_to_double(kern.compute_squared_radius_d_object()(points1410.begin(), points1410.end())))); + // In this case, the computed sphere using CGAL kernel does not match the minimal enclosing ball; the filtration value check is therefore done against a hardcoded value + GUDHI_TEST_FLOAT_EQUALITY_CHECK(f1410, 1.); Point_cloud points469; points469.push_back(cech_complex_for_doc.get_point(4)); points469.push_back(cech_complex_for_doc.get_point(6)); points469.push_back(cech_complex_for_doc.get_point(9)); -// Min_sphere ms469(dimension, points469.begin(), points469.end()); Simplex_tree::Filtration_value f469 = st2.filtration(st2.find({4, 6, 9})); - std::clog << "f469= " << f469 << " | points469_radius= " << std::sqrt(cast_to_double(kern.compute_squared_radius_d_object()(points469.begin(), points469.end()))) << std::endl; + std::clog << "f469= " << f469 << std::endl; GUDHI_TEST_FLOAT_EQUALITY_CHECK(f469, std::sqrt(cast_to_double(kern.compute_squared_radius_d_object()(points469.begin(), points469.end())))); diff --git a/src/Cech_complex/utilities/cech_persistence.cpp b/src/Cech_complex/utilities/cech_persistence.cpp index ccd7d453..0c945cad 100644 --- a/src/Cech_complex/utilities/cech_persistence.cpp +++ b/src/Cech_complex/utilities/cech_persistence.cpp @@ -25,14 +25,12 @@ // Types definition using Simplex_tree = Gudhi::Simplex_tree; using Filtration_value = Simplex_tree::Filtration_value; -// using Point = std::vector; -// using Point_cloud = std::vector; using Kernel = CGAL::Epeck_d; using Point = typename Kernel::Point_d; using Point_cloud = std::vector; using Points_off_reader = Gudhi::Points_off_reader; -using Cech_complex = Gudhi::cech_complex::Cech_complex; +using Cech_complex = Gudhi::cech_complex::Cech_complex; using Field_Zp = Gudhi::persistent_cohomology::Field_Zp; using Persistent_cohomology = Gudhi::persistent_cohomology::Persistent_cohomology; diff --git a/src/Simplex_tree/include/gudhi/Simplex_tree.h b/src/Simplex_tree/include/gudhi/Simplex_tree.h index f69ed6ec..85790baf 100644 --- a/src/Simplex_tree/include/gudhi/Simplex_tree.h +++ b/src/Simplex_tree/include/gudhi/Simplex_tree.h @@ -1278,10 +1278,8 @@ class Simplex_tree { intersection.emplace_back(next->first, Node(nullptr, filt)); } } - std::clog << "Hind: after intersection insertion" << std::endl; if (intersection.size() != 0) { // Reverse the order to insert - std::clog << "Hind: declare new siblings" << std::endl; Siblings * new_sib = new Siblings(siblings, // oncles simplex->first, // parent boost::adaptors::reverse(intersection)); // boost::container::ordered_unique_range_t @@ -1290,12 +1288,10 @@ class Simplex_tree { for (auto new_sib_member = new_sib->members().begin(); new_sib_member != new_sib->members().end(); new_sib_member++) { - std::clog << "Hind: check the blocker result" << std::endl; bool blocker_result = block_simplex(new_sib_member); // new_sib member has been blocked by the blocker function // add it to the list to be removed - do not perform it while looping on it if (blocker_result) { - std::clog << "Hind: add to list of blocked sib to be removed" << std::endl; blocked_new_sib_vertex_list.push_back(new_sib_member->first); } } diff --git a/src/common/include/gudhi/distance_functions.h b/src/common/include/gudhi/distance_functions.h index ae5168aa..a8ee4a75 100644 --- a/src/common/include/gudhi/distance_functions.h +++ b/src/common/include/gudhi/distance_functions.h @@ -65,19 +65,17 @@ class Euclidean_distance { * The points are assumed to have the same dimension. */ class Minimal_enclosing_ball_radius { public: - /** \brief TODO + /** \brief Enclosing ball radius from two points using CGAL. * * @param[in] point_1 * @param[in] point_2 - * @return - * \tparam Point + * @return Enclosing ball radius for the two points. + * \tparam Point must be a Kernel::Point_d from CGAL. * */ - //typename FT = typename Kernel::FT, template< typename Kernel = CGAL::Epeck_d, typename Point= typename Kernel::Point_d> double operator()(const Point& point_1, const Point& point_2) const { - std::clog << "Added template: distance betw points 1 and 2" << std::endl; Kernel kernel_; return std::sqrt(CGAL::to_double(kernel_.squared_distance_d_object()(point_1, point_2))) / 2.; } @@ -94,25 +92,21 @@ class Minimal_enclosing_ball_radius { template< typename Point > typename std::iterator_traits::type>::value_type operator()(const Point& point_1, const Point& point_2) const { - std::clog << "Hind: Minimal_enclosing_ball_radius point1 et 2; Euclidean" << std::endl; std::clog << "#" << *point_1.begin() << "##" << *point_2.begin() << std::endl; return Euclidean_distance()(point_1, point_2) / 2.; } - - /** \brief TODO + /** \brief Enclosing ball radius from a point cloud using CGAL. * * @param[in] point_cloud The points. - * @return - * \tparam Point_cloud + * @return Enclosing ball radius for the points. + * \tparam Point_cloud must be a range of Kernel::Point_d points from CGAL. * */ - //typename FT = typename Kernel::FT, template< typename Kernel = CGAL::Epeck_d, typename Point= typename Kernel::Point_d, typename Point_cloud = std::vector> double operator()(const Point_cloud& point_cloud) const { - std::clog << "Added template: distance in point cloud" << std::endl; Kernel kernel_; return std::sqrt(CGAL::to_double(kernel_.compute_squared_radius_d_object()(point_cloud.begin(), point_cloud.end()))); } @@ -133,8 +127,6 @@ class Minimal_enclosing_ball_radius { typename Coordinate = typename std::iterator_traits::value_type> Coordinate operator()(const Point_cloud& point_cloud) const { - std::clog << "Hind: Minimal_enclosing_ball_radius point cloud; Miniball" << std::endl; - using Min_sphere = Miniball::Miniball>; Min_sphere ms(boost::size(*point_cloud.begin()), point_cloud.begin(), point_cloud.end()); diff --git a/src/common/include/gudhi/graph_simplicial_complex.h b/src/common/include/gudhi/graph_simplicial_complex.h index 9190182c..da9dee7d 100644 --- a/src/common/include/gudhi/graph_simplicial_complex.h +++ b/src/common/include/gudhi/graph_simplicial_complex.h @@ -18,8 +18,6 @@ #include #include // for std::tie -#include - namespace Gudhi { /** @file * @brief Graph simplicial complex methods @@ -78,7 +76,6 @@ Proximity_graph compute_proximity_graph( for (auto it_u = points.begin(); it_u != points.end(); ++it_u) { idx_v = idx_u + 1; for (auto it_v = it_u + 1; it_v != points.end(); ++it_v, ++idx_v) { - std::clog << "#idx_u" << idx_u << "#idx_v " << idx_v << std::endl; fil = distance(*it_u, *it_v); if (fil <= threshold) { edges.emplace_back(idx_u, idx_v); -- cgit v1.2.3 From 65b32d167810a107cf807572f84cef082c76067d Mon Sep 17 00:00:00 2001 From: Hind-M Date: Thu, 30 Sep 2021 16:13:39 +0200 Subject: Add Cech tests, examples, utilities and benchmark to the build only if CGAL is present --- src/Cech_complex/benchmark/CMakeLists.txt | 18 ++++++++++-------- src/Cech_complex/example/CMakeLists.txt | 26 ++++++++++++++------------ src/Cech_complex/test/CMakeLists.txt | 19 +++++++++++-------- src/Cech_complex/utilities/CMakeLists.txt | 24 +++++++++++++----------- 4 files changed, 48 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/benchmark/CMakeLists.txt b/src/Cech_complex/benchmark/CMakeLists.txt index bc54c0f3..ccd1b324 100644 --- a/src/Cech_complex/benchmark/CMakeLists.txt +++ b/src/Cech_complex/benchmark/CMakeLists.txt @@ -1,13 +1,15 @@ project(Cech_complex_benchmark) -# Do not forget to copy test files in current binary dir -file(COPY "${CMAKE_SOURCE_DIR}/data/points/tore3D_1307.off" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) +if (CGAL_FOUND) + # Do not forget to copy test files in current binary dir + file(COPY "${CMAKE_SOURCE_DIR}/data/points/tore3D_1307.off" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) -if(TARGET Boost::filesystem) - add_executable(cech_complex_benchmark cech_complex_benchmark.cpp) - target_link_libraries(cech_complex_benchmark Boost::filesystem) + if(TARGET Boost::filesystem) + add_executable(cech_complex_benchmark cech_complex_benchmark.cpp) + target_link_libraries(cech_complex_benchmark Boost::filesystem) - if (TBB_FOUND) - target_link_libraries(cech_complex_benchmark ${TBB_LIBRARIES}) + if (TBB_FOUND) + target_link_libraries(cech_complex_benchmark ${TBB_LIBRARIES}) + endif() endif() -endif() \ No newline at end of file +endif() diff --git a/src/Cech_complex/example/CMakeLists.txt b/src/Cech_complex/example/CMakeLists.txt index 1b08c7cb..52dec63f 100644 --- a/src/Cech_complex/example/CMakeLists.txt +++ b/src/Cech_complex/example/CMakeLists.txt @@ -1,17 +1,19 @@ project(Cech_complex_examples) -if (TARGET Boost::program_options) - add_executable ( Cech_complex_example_step_by_step cech_complex_step_by_step.cpp ) - target_link_libraries(Cech_complex_example_step_by_step Boost::program_options) - if (TBB_FOUND) - target_link_libraries(Cech_complex_example_step_by_step ${TBB_LIBRARIES}) +if (CGAL_FOUND) + if (TARGET Boost::program_options) + add_executable ( Cech_complex_example_step_by_step cech_complex_step_by_step.cpp ) + target_link_libraries(Cech_complex_example_step_by_step Boost::program_options) + if (TBB_FOUND) + target_link_libraries(Cech_complex_example_step_by_step ${TBB_LIBRARIES}) + endif() + add_test(NAME Cech_complex_utility_from_rips_on_tore_3D COMMAND $ + "${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off" "-r" "0.25" "-d" "3") endif() - add_test(NAME Cech_complex_utility_from_rips_on_tore_3D COMMAND $ - "${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off" "-r" "0.25" "-d" "3") -endif() -add_executable ( Cech_complex_example_from_points cech_complex_example_from_points.cpp) -if (TBB_FOUND) - target_link_libraries(Cech_complex_example_from_points ${TBB_LIBRARIES}) + add_executable ( Cech_complex_example_from_points cech_complex_example_from_points.cpp) + if (TBB_FOUND) + target_link_libraries(Cech_complex_example_from_points ${TBB_LIBRARIES}) + endif() + add_test(NAME Cech_complex_example_from_points COMMAND $) endif() -add_test(NAME Cech_complex_example_from_points COMMAND $) diff --git a/src/Cech_complex/test/CMakeLists.txt b/src/Cech_complex/test/CMakeLists.txt index e6a2a18f..8e725f50 100644 --- a/src/Cech_complex/test/CMakeLists.txt +++ b/src/Cech_complex/test/CMakeLists.txt @@ -1,11 +1,14 @@ -include(GUDHI_boost_test) +if (CGAL_FOUND) + include(GUDHI_boost_test) -add_executable ( Cech_complex_test_unit test_cech_complex.cpp ) -if (TBB_FOUND) - target_link_libraries(Cech_complex_test_unit ${TBB_LIBRARIES}) -endif() + add_executable ( Cech_complex_test_unit test_cech_complex.cpp ) + if (TBB_FOUND) + target_link_libraries(Cech_complex_test_unit ${TBB_LIBRARIES}) + endif() + + # Do not forget to copy test files in current binary dir + file(COPY "${CMAKE_SOURCE_DIR}/data/points/alphacomplexdoc.off" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) -# Do not forget to copy test files in current binary dir -file(COPY "${CMAKE_SOURCE_DIR}/data/points/alphacomplexdoc.off" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) + gudhi_add_boost_test(Cech_complex_test_unit) -gudhi_add_boost_test(Cech_complex_test_unit) +endif() diff --git a/src/Cech_complex/utilities/CMakeLists.txt b/src/Cech_complex/utilities/CMakeLists.txt index b183c8d8..efe40e57 100644 --- a/src/Cech_complex/utilities/CMakeLists.txt +++ b/src/Cech_complex/utilities/CMakeLists.txt @@ -1,15 +1,17 @@ -project(Cech_complex_utilities) +if (CGAL_FOUND) + project(Cech_complex_utilities) -if (TARGET Boost::program_options) - add_executable(cech_persistence cech_persistence.cpp) - target_link_libraries(cech_persistence Boost::program_options) + if (TARGET Boost::program_options) + add_executable(cech_persistence cech_persistence.cpp) + target_link_libraries(cech_persistence Boost::program_options) - if (TBB_FOUND) - target_link_libraries(cech_persistence ${TBB_LIBRARIES}) - endif() + if (TBB_FOUND) + target_link_libraries(cech_persistence ${TBB_LIBRARIES}) + endif() - add_test(NAME Cech_complex_utility_from_rips_on_tore_3D COMMAND $ - "${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off" "-r" "0.25" "-m" "0.5" "-d" "3" "-p" "3") + add_test(NAME Cech_complex_utility_from_rips_on_tore_3D COMMAND $ + "${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off" "-r" "0.25" "-m" "0.5" "-d" "3" "-p" "3") - install(TARGETS cech_persistence DESTINATION bin) -endif() \ No newline at end of file + install(TARGETS cech_persistence DESTINATION bin) + endif() +endif() -- cgit v1.2.3 From d3970dbbc16993d348092899eb8fcd1ea1aceb8d Mon Sep 17 00:00:00 2001 From: Hind-M Date: Fri, 1 Oct 2021 15:47:58 +0200 Subject: Separate Minimal_enclosing_ball_radius class which uses CGAL from common distance_functions.h file --- .../benchmark/cech_complex_benchmark.cpp | 2 +- .../example/cech_complex_step_by_step.cpp | 2 +- src/Cech_complex/include/gudhi/Cech_complex.h | 2 +- .../include/gudhi/Cech_complex/Cech_kernel.h | 149 +++++++++++++++++++++ src/Cech_complex/test/test_cech_complex.cpp | 2 +- src/Cech_complex/utilities/cech_persistence.cpp | 2 +- src/common/include/gudhi/distance_functions.h | 83 ------------ 7 files changed, 154 insertions(+), 88 deletions(-) create mode 100644 src/Cech_complex/include/gudhi/Cech_complex/Cech_kernel.h (limited to 'src') diff --git a/src/Cech_complex/benchmark/cech_complex_benchmark.cpp b/src/Cech_complex/benchmark/cech_complex_benchmark.cpp index 4a1aa06e..cfeb0725 100644 --- a/src/Cech_complex/benchmark/cech_complex_benchmark.cpp +++ b/src/Cech_complex/benchmark/cech_complex_benchmark.cpp @@ -9,7 +9,7 @@ */ #include -#include +#include #include #include #include diff --git a/src/Cech_complex/example/cech_complex_step_by_step.cpp b/src/Cech_complex/example/cech_complex_step_by_step.cpp index 44e7f945..2d8321b1 100644 --- a/src/Cech_complex/example/cech_complex_step_by_step.cpp +++ b/src/Cech_complex/example/cech_complex_step_by_step.cpp @@ -9,7 +9,7 @@ */ #include -#include +#include #include #include diff --git a/src/Cech_complex/include/gudhi/Cech_complex.h b/src/Cech_complex/include/gudhi/Cech_complex.h index 32a78aec..7bbf97d1 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex.h +++ b/src/Cech_complex/include/gudhi/Cech_complex.h @@ -11,7 +11,7 @@ #ifndef CECH_COMPLEX_H_ #define CECH_COMPLEX_H_ -#include // for Gudhi::Minimal_enclosing_ball_radius +#include // for Gudhi::Minimal_enclosing_ball_radius #include // for Gudhi::Proximity_graph #include // for GUDHI_CHECK #include // for Gudhi::cech_complex::Cech_blocker diff --git a/src/Cech_complex/include/gudhi/Cech_complex/Cech_kernel.h b/src/Cech_complex/include/gudhi/Cech_complex/Cech_kernel.h new file mode 100644 index 00000000..93af90d2 --- /dev/null +++ b/src/Cech_complex/include/gudhi/Cech_complex/Cech_kernel.h @@ -0,0 +1,149 @@ +/* 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): Hind Montassif + * + * Copyright (C) 2021 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#ifndef CECH_KERNEL_H_ +#define CECH_KERNEL_H_ + +#include + +#include // for std::sqrt + +namespace Gudhi { + +// namespace cech_complex { + +/** @brief Compute the radius of the minimal enclosing ball between Points given by a range of coordinates. + * The points are assumed to have the same dimension. */ +class Minimal_enclosing_ball_radius { + public: + /** \brief Enclosing ball radius from two points using CGAL. + * + * @param[in] point_1 + * @param[in] point_2 + * @return Enclosing ball radius for the two points. + * \tparam Point must be a Kernel::Point_d from CGAL. + * + */ + template< typename Kernel = CGAL::Epeck_d, + typename Point= typename Kernel::Point_d> + double operator()(const Point& point_1, const Point& point_2) const { + Kernel kernel_; + return std::sqrt(CGAL::to_double(kernel_.squared_distance_d_object()(point_1, point_2))) / 2.; + } + + /** \brief Minimal_enclosing_ball_radius from two points. + * + * @param[in] point_1 First point. + * @param[in] point_2 second point. + * @return The minimal enclosing ball radius for the two points (aka. Euclidean distance / 2.). + * + * \tparam Point must be a range of Cartesian coordinates. + * + */ +// template< typename Point > +// typename std::iterator_traits::type>::value_type +// operator()(const Point& point_1, const Point& point_2) const { +// std::clog << "#" << *point_1.begin() << "##" << *point_2.begin() << std::endl; +// return Euclidean_distance()(point_1, point_2) / 2.; +// } + + /** \brief Enclosing ball radius from a point cloud using CGAL. + * + * @param[in] point_cloud The points. + * @return Enclosing ball radius for the points. + * \tparam Point_cloud must be a range of Kernel::Point_d points from CGAL. + * + */ + template< typename Kernel = CGAL::Epeck_d, + typename Point= typename Kernel::Point_d, + typename Point_cloud = std::vector> + double operator()(const Point_cloud& point_cloud) const { + Kernel kernel_; + return std::sqrt(CGAL::to_double(kernel_.compute_squared_radius_d_object()(point_cloud.begin(), point_cloud.end()))); + } + + /** \brief Minimal_enclosing_ball_radius from a point cloud. + * + * @param[in] point_cloud The points. + * @return The minimal enclosing ball radius for the points. + * + * \tparam Point_cloud must be a range of points with Cartesian coordinates. + * Point_cloud is a range over a range of Coordinate. + * + */ +// template< typename Point_cloud, +// typename Point_iterator = typename boost::range_const_iterator::type, +// typename Point = typename std::iterator_traits::value_type, +// typename Coordinate_iterator = typename boost::range_const_iterator::type, +// typename Coordinate = typename std::iterator_traits::value_type> +// Coordinate +// operator()(const Point_cloud& point_cloud) const { +// using Min_sphere = Miniball::Miniball>; +// +// Min_sphere ms(boost::size(*point_cloud.begin()), point_cloud.begin(), point_cloud.end()); +// #ifdef DEBUG_TRACES +// std::clog << "Minimal_enclosing_ball_radius = " << std::sqrt(ms.squared_radius()) << " | nb points = " +// << boost::size(point_cloud) << " | dimension = " +// << boost::size(*point_cloud.begin()) << std::endl; +// #endif // DEBUG_TRACES +// +// return std::sqrt(ms.squared_radius()); +// } +}; + +/** + * \class Cech_kernel + * \brief Cech complex kernel container. + * + * \details + * The Cech complex kernel container stores CGAL Kernel and dispatch basic computations. + */ + +// template < typename Kernel > +// class Cech_kernel { +// private: +// // Kernel for functions access. +// Kernel kernel_; +// public: +// using Point_d = typename Kernel::Point_d; +// // Numeric type of coordinates in the kernel +// using FT = typename Kernel::FT; +// // Sphere is a pair of point and squared radius. +// using Sphere = typename std::pair; +// +// int get_dimension(const Point_d& p0) const { +// return kernel_.point_dimension_d_object()(p0); +// } +// +// template +// Sphere get_sphere(PointIterator begin, PointIterator end) const { +// Point_d c = kernel_.construct_circumcenter_d_object()(begin, end); +// FT r = kernel_.squared_distance_d_object()(c, *begin); +// return std::make_pair(std::move(c), std::move(r)); +// } +// +// template +// FT get_squared_radius(PointIterator begin, PointIterator end) const { +// return kernel_.compute_squared_radius_d_object()(begin, end); +// } +// +// FT get_squared_radius(const Sphere& sph) const { +// return sph.second; +// } +// }; + + +//} // namespace cech_complex + +// namespace cechcomplex = cech_complex; + +} // namespace Gudhi + +#endif // CECH_KERNEL_H_ diff --git a/src/Cech_complex/test/test_cech_complex.cpp b/src/Cech_complex/test/test_cech_complex.cpp index 51b466da..7d8c3c22 100644 --- a/src/Cech_complex/test/test_cech_complex.cpp +++ b/src/Cech_complex/test/test_cech_complex.cpp @@ -22,7 +22,7 @@ // to construct Cech_complex from a OFF file of points #include #include -#include +#include #include #include // For EXACT or SAFE version diff --git a/src/Cech_complex/utilities/cech_persistence.cpp b/src/Cech_complex/utilities/cech_persistence.cpp index 0c945cad..ccf63e3e 100644 --- a/src/Cech_complex/utilities/cech_persistence.cpp +++ b/src/Cech_complex/utilities/cech_persistence.cpp @@ -9,7 +9,7 @@ */ #include -#include +#include #include #include #include diff --git a/src/common/include/gudhi/distance_functions.h b/src/common/include/gudhi/distance_functions.h index a8ee4a75..5e5a1e31 100644 --- a/src/common/include/gudhi/distance_functions.h +++ b/src/common/include/gudhi/distance_functions.h @@ -13,13 +13,9 @@ #include -#include - #include #include -#include - #include // for std::sqrt #include // for std::decay #include // for std::begin, std::end @@ -61,85 +57,6 @@ class Euclidean_distance { } }; -/** @brief Compute the radius of the minimal enclosing ball between Points given by a range of coordinates. - * The points are assumed to have the same dimension. */ -class Minimal_enclosing_ball_radius { - public: - /** \brief Enclosing ball radius from two points using CGAL. - * - * @param[in] point_1 - * @param[in] point_2 - * @return Enclosing ball radius for the two points. - * \tparam Point must be a Kernel::Point_d from CGAL. - * - */ - template< typename Kernel = CGAL::Epeck_d, - typename Point= typename Kernel::Point_d> - double operator()(const Point& point_1, const Point& point_2) const { - Kernel kernel_; - return std::sqrt(CGAL::to_double(kernel_.squared_distance_d_object()(point_1, point_2))) / 2.; - } - - /** \brief Minimal_enclosing_ball_radius from two points. - * - * @param[in] point_1 First point. - * @param[in] point_2 second point. - * @return The minimal enclosing ball radius for the two points (aka. Euclidean distance / 2.). - * - * \tparam Point must be a range of Cartesian coordinates. - * - */ - template< typename Point > - typename std::iterator_traits::type>::value_type - operator()(const Point& point_1, const Point& point_2) const { - std::clog << "#" << *point_1.begin() << "##" << *point_2.begin() << std::endl; - return Euclidean_distance()(point_1, point_2) / 2.; - } - - /** \brief Enclosing ball radius from a point cloud using CGAL. - * - * @param[in] point_cloud The points. - * @return Enclosing ball radius for the points. - * \tparam Point_cloud must be a range of Kernel::Point_d points from CGAL. - * - */ - template< typename Kernel = CGAL::Epeck_d, - typename Point= typename Kernel::Point_d, - typename Point_cloud = std::vector> - double operator()(const Point_cloud& point_cloud) const { - Kernel kernel_; - return std::sqrt(CGAL::to_double(kernel_.compute_squared_radius_d_object()(point_cloud.begin(), point_cloud.end()))); - } - - /** \brief Minimal_enclosing_ball_radius from a point cloud. - * - * @param[in] point_cloud The points. - * @return The minimal enclosing ball radius for the points. - * - * \tparam Point_cloud must be a range of points with Cartesian coordinates. - * Point_cloud is a range over a range of Coordinate. - * - */ - template< typename Point_cloud, - typename Point_iterator = typename boost::range_const_iterator::type, - typename Point = typename std::iterator_traits::value_type, - typename Coordinate_iterator = typename boost::range_const_iterator::type, - typename Coordinate = typename std::iterator_traits::value_type> - Coordinate - operator()(const Point_cloud& point_cloud) const { - using Min_sphere = Miniball::Miniball>; - - Min_sphere ms(boost::size(*point_cloud.begin()), point_cloud.begin(), point_cloud.end()); -#ifdef DEBUG_TRACES - std::clog << "Minimal_enclosing_ball_radius = " << std::sqrt(ms.squared_radius()) << " | nb points = " - << boost::size(point_cloud) << " | dimension = " - << boost::size(*point_cloud.begin()) << std::endl; -#endif // DEBUG_TRACES - - return std::sqrt(ms.squared_radius()); - } -}; - } // namespace Gudhi #endif // DISTANCE_FUNCTIONS_H_ -- cgit v1.2.3 From e9a676d7aa9d27595951f1f4f14ac419641234b4 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Wed, 6 Oct 2021 10:19:33 +0200 Subject: Replace CGAL_FOUND flag with version check of CGAL_WITH_EIGEN3_VERSION --- src/Cech_complex/benchmark/CMakeLists.txt | 2 +- src/Cech_complex/example/CMakeLists.txt | 2 +- src/Cech_complex/test/CMakeLists.txt | 2 +- src/Cech_complex/utilities/CMakeLists.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/benchmark/CMakeLists.txt b/src/Cech_complex/benchmark/CMakeLists.txt index ccd1b324..dd7f3f6c 100644 --- a/src/Cech_complex/benchmark/CMakeLists.txt +++ b/src/Cech_complex/benchmark/CMakeLists.txt @@ -1,6 +1,6 @@ project(Cech_complex_benchmark) -if (CGAL_FOUND) +if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) # Do not forget to copy test files in current binary dir file(COPY "${CMAKE_SOURCE_DIR}/data/points/tore3D_1307.off" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) diff --git a/src/Cech_complex/example/CMakeLists.txt b/src/Cech_complex/example/CMakeLists.txt index 52dec63f..2b6a29a3 100644 --- a/src/Cech_complex/example/CMakeLists.txt +++ b/src/Cech_complex/example/CMakeLists.txt @@ -1,6 +1,6 @@ project(Cech_complex_examples) -if (CGAL_FOUND) +if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) if (TARGET Boost::program_options) add_executable ( Cech_complex_example_step_by_step cech_complex_step_by_step.cpp ) target_link_libraries(Cech_complex_example_step_by_step Boost::program_options) diff --git a/src/Cech_complex/test/CMakeLists.txt b/src/Cech_complex/test/CMakeLists.txt index 8e725f50..66d796a7 100644 --- a/src/Cech_complex/test/CMakeLists.txt +++ b/src/Cech_complex/test/CMakeLists.txt @@ -1,4 +1,4 @@ -if (CGAL_FOUND) +if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) include(GUDHI_boost_test) add_executable ( Cech_complex_test_unit test_cech_complex.cpp ) diff --git a/src/Cech_complex/utilities/CMakeLists.txt b/src/Cech_complex/utilities/CMakeLists.txt index efe40e57..3c9c0f73 100644 --- a/src/Cech_complex/utilities/CMakeLists.txt +++ b/src/Cech_complex/utilities/CMakeLists.txt @@ -1,4 +1,4 @@ -if (CGAL_FOUND) +if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) project(Cech_complex_utilities) if (TARGET Boost::program_options) -- cgit v1.2.3 From 1db88dcd1f5f94d4ab9b560fd0f4399cf00fb304 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Mon, 11 Oct 2021 15:09:41 +0200 Subject: Change the minimal version of CGAL_WITH_EIGEN3 to 5.1.0 for cech use (tests, examples, etc) --- src/Cech_complex/benchmark/CMakeLists.txt | 2 +- src/Cech_complex/example/CMakeLists.txt | 2 +- src/Cech_complex/test/CMakeLists.txt | 2 +- src/Cech_complex/utilities/CMakeLists.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/benchmark/CMakeLists.txt b/src/Cech_complex/benchmark/CMakeLists.txt index dd7f3f6c..9c7fd26d 100644 --- a/src/Cech_complex/benchmark/CMakeLists.txt +++ b/src/Cech_complex/benchmark/CMakeLists.txt @@ -1,6 +1,6 @@ project(Cech_complex_benchmark) -if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) +if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 5.1.0) # Do not forget to copy test files in current binary dir file(COPY "${CMAKE_SOURCE_DIR}/data/points/tore3D_1307.off" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) diff --git a/src/Cech_complex/example/CMakeLists.txt b/src/Cech_complex/example/CMakeLists.txt index 2b6a29a3..cdbd44cb 100644 --- a/src/Cech_complex/example/CMakeLists.txt +++ b/src/Cech_complex/example/CMakeLists.txt @@ -1,6 +1,6 @@ project(Cech_complex_examples) -if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) +if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 5.1.0) if (TARGET Boost::program_options) add_executable ( Cech_complex_example_step_by_step cech_complex_step_by_step.cpp ) target_link_libraries(Cech_complex_example_step_by_step Boost::program_options) diff --git a/src/Cech_complex/test/CMakeLists.txt b/src/Cech_complex/test/CMakeLists.txt index 66d796a7..2926a656 100644 --- a/src/Cech_complex/test/CMakeLists.txt +++ b/src/Cech_complex/test/CMakeLists.txt @@ -1,4 +1,4 @@ -if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) +if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 5.1.0) include(GUDHI_boost_test) add_executable ( Cech_complex_test_unit test_cech_complex.cpp ) diff --git a/src/Cech_complex/utilities/CMakeLists.txt b/src/Cech_complex/utilities/CMakeLists.txt index 3c9c0f73..1a970679 100644 --- a/src/Cech_complex/utilities/CMakeLists.txt +++ b/src/Cech_complex/utilities/CMakeLists.txt @@ -1,4 +1,4 @@ -if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) +if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 5.1.0) project(Cech_complex_utilities) if (TARGET Boost::program_options) -- cgit v1.2.3 From 7845631fc2b4a32d4ca7c3e37bf49d40c22e226e Mon Sep 17 00:00:00 2001 From: Hind-M Date: Tue, 12 Oct 2021 10:08:10 +0200 Subject: Remove old implementation of Minimal_enclosing_ball_radius operator () --- .../include/gudhi/Cech_complex/Cech_kernel.h | 42 ---------------------- 1 file changed, 42 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/include/gudhi/Cech_complex/Cech_kernel.h b/src/Cech_complex/include/gudhi/Cech_complex/Cech_kernel.h index 93af90d2..348bb57d 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex/Cech_kernel.h +++ b/src/Cech_complex/include/gudhi/Cech_complex/Cech_kernel.h @@ -38,21 +38,6 @@ class Minimal_enclosing_ball_radius { return std::sqrt(CGAL::to_double(kernel_.squared_distance_d_object()(point_1, point_2))) / 2.; } - /** \brief Minimal_enclosing_ball_radius from two points. - * - * @param[in] point_1 First point. - * @param[in] point_2 second point. - * @return The minimal enclosing ball radius for the two points (aka. Euclidean distance / 2.). - * - * \tparam Point must be a range of Cartesian coordinates. - * - */ -// template< typename Point > -// typename std::iterator_traits::type>::value_type -// operator()(const Point& point_1, const Point& point_2) const { -// std::clog << "#" << *point_1.begin() << "##" << *point_2.begin() << std::endl; -// return Euclidean_distance()(point_1, point_2) / 2.; -// } /** \brief Enclosing ball radius from a point cloud using CGAL. * @@ -69,33 +54,6 @@ class Minimal_enclosing_ball_radius { return std::sqrt(CGAL::to_double(kernel_.compute_squared_radius_d_object()(point_cloud.begin(), point_cloud.end()))); } - /** \brief Minimal_enclosing_ball_radius from a point cloud. - * - * @param[in] point_cloud The points. - * @return The minimal enclosing ball radius for the points. - * - * \tparam Point_cloud must be a range of points with Cartesian coordinates. - * Point_cloud is a range over a range of Coordinate. - * - */ -// template< typename Point_cloud, -// typename Point_iterator = typename boost::range_const_iterator::type, -// typename Point = typename std::iterator_traits::value_type, -// typename Coordinate_iterator = typename boost::range_const_iterator::type, -// typename Coordinate = typename std::iterator_traits::value_type> -// Coordinate -// operator()(const Point_cloud& point_cloud) const { -// using Min_sphere = Miniball::Miniball>; -// -// Min_sphere ms(boost::size(*point_cloud.begin()), point_cloud.begin(), point_cloud.end()); -// #ifdef DEBUG_TRACES -// std::clog << "Minimal_enclosing_ball_radius = " << std::sqrt(ms.squared_radius()) << " | nb points = " -// << boost::size(point_cloud) << " | dimension = " -// << boost::size(*point_cloud.begin()) << std::endl; -// #endif // DEBUG_TRACES -// -// return std::sqrt(ms.squared_radius()); -// } }; /** -- cgit v1.2.3 From dafc12cf1a71d5515fbeb4a666f312adc7d82d63 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Wed, 13 Oct 2021 09:46:26 +0200 Subject: Change version of CGAL_WITH_EIGEN3 to 5.0.1 for cech use --- src/Cech_complex/benchmark/CMakeLists.txt | 2 +- src/Cech_complex/example/CMakeLists.txt | 2 +- src/Cech_complex/test/CMakeLists.txt | 2 +- src/Cech_complex/utilities/CMakeLists.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/benchmark/CMakeLists.txt b/src/Cech_complex/benchmark/CMakeLists.txt index 9c7fd26d..a6b3d70b 100644 --- a/src/Cech_complex/benchmark/CMakeLists.txt +++ b/src/Cech_complex/benchmark/CMakeLists.txt @@ -1,6 +1,6 @@ project(Cech_complex_benchmark) -if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 5.1.0) +if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 5.0.1) # Do not forget to copy test files in current binary dir file(COPY "${CMAKE_SOURCE_DIR}/data/points/tore3D_1307.off" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) diff --git a/src/Cech_complex/example/CMakeLists.txt b/src/Cech_complex/example/CMakeLists.txt index cdbd44cb..4d11ace2 100644 --- a/src/Cech_complex/example/CMakeLists.txt +++ b/src/Cech_complex/example/CMakeLists.txt @@ -1,6 +1,6 @@ project(Cech_complex_examples) -if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 5.1.0) +if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 5.0.1) if (TARGET Boost::program_options) add_executable ( Cech_complex_example_step_by_step cech_complex_step_by_step.cpp ) target_link_libraries(Cech_complex_example_step_by_step Boost::program_options) diff --git a/src/Cech_complex/test/CMakeLists.txt b/src/Cech_complex/test/CMakeLists.txt index 2926a656..2d736f27 100644 --- a/src/Cech_complex/test/CMakeLists.txt +++ b/src/Cech_complex/test/CMakeLists.txt @@ -1,4 +1,4 @@ -if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 5.1.0) +if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 5.0.1) include(GUDHI_boost_test) add_executable ( Cech_complex_test_unit test_cech_complex.cpp ) diff --git a/src/Cech_complex/utilities/CMakeLists.txt b/src/Cech_complex/utilities/CMakeLists.txt index 1a970679..e80a698e 100644 --- a/src/Cech_complex/utilities/CMakeLists.txt +++ b/src/Cech_complex/utilities/CMakeLists.txt @@ -1,4 +1,4 @@ -if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 5.1.0) +if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 5.0.1) project(Cech_complex_utilities) if (TARGET Boost::program_options) -- cgit v1.2.3 From 160c0a9832f84b6c12e507388982133ec81be278 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Mon, 18 Oct 2021 11:30:23 +0200 Subject: changed name differentiation ---> tensorflow and separated functions into different files --- src/python/CMakeLists.txt | 4 +- src/python/gudhi/differentiation/__init__.py | 3 - src/python/gudhi/differentiation/tensorflow.py | 234 --------------------- src/python/gudhi/tensorflow/CubicalLayer.py | 66 ++++++ .../gudhi/tensorflow/LowerStarSimplexTreeLayer.py | 77 +++++++ src/python/gudhi/tensorflow/RipsLayer.py | 75 +++++++ src/python/gudhi/tensorflow/__init__.py | 5 + src/python/test/test_diff.py | 2 +- 8 files changed, 226 insertions(+), 240 deletions(-) delete mode 100644 src/python/gudhi/differentiation/__init__.py delete mode 100644 src/python/gudhi/differentiation/tensorflow.py create mode 100644 src/python/gudhi/tensorflow/CubicalLayer.py create mode 100644 src/python/gudhi/tensorflow/LowerStarSimplexTreeLayer.py create mode 100644 src/python/gudhi/tensorflow/RipsLayer.py create mode 100644 src/python/gudhi/tensorflow/__init__.py (limited to 'src') diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 6c17223e..49ec3fea 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -67,7 +67,7 @@ if(PYTHONINTERP_FOUND) 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}'representations', ") - set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'differentiation', ") + set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'tensorflow', ") set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'wasserstein', ") set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'point_cloud', ") set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'weighted_rips_complex', ") @@ -271,7 +271,7 @@ if(PYTHONINTERP_FOUND) file(COPY "gudhi/persistence_graphical_tools.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") file(COPY "gudhi/representations" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi/") file(COPY "gudhi/wasserstein" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") - file(COPY "gudhi/differentiation" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") + file(COPY "gudhi/tensorflow" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") file(COPY "gudhi/point_cloud" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") file(COPY "gudhi/clustering" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi" FILES_MATCHING PATTERN "*.py") file(COPY "gudhi/weighted_rips_complex.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") diff --git a/src/python/gudhi/differentiation/__init__.py b/src/python/gudhi/differentiation/__init__.py deleted file mode 100644 index 3b7790e4..00000000 --- a/src/python/gudhi/differentiation/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .tensorflow import * - -__all__ = ["LowerStarSimplexTreeLayer", "RipsLayer", "CubicalLayer"] diff --git a/src/python/gudhi/differentiation/tensorflow.py b/src/python/gudhi/differentiation/tensorflow.py deleted file mode 100644 index 15d5811e..00000000 --- a/src/python/gudhi/differentiation/tensorflow.py +++ /dev/null @@ -1,234 +0,0 @@ -import numpy as np -import tensorflow as tf -from ..rips_complex import RipsComplex -from ..cubical_complex import CubicalComplex - -# In this file, we write functions based on the Gudhi library that compute persistence diagrams associated to -# different filtrations (lower star, Rips, cubical), as well as the corresponding positive and negative -# simplices. We also wrap these functions into Tensorflow models. - - - -######################################### -# Lower star filtration on simplex tree # -######################################### - -# The parameters of the model are the vertex function values of the simplex tree. - -def _LowerStarSimplexTree(simplextree, filtration, dimension): - # Parameters: simplextree (simplex tree on which to compute persistence) - # filtration (function values on the vertices of st), - # dimension (homology dimension), - - for s,_ in simplextree.get_filtration(): - simplextree.assign_filtration(s, -1e10) - - # Assign new filtration values - for i in range(simplextree.num_vertices()): - simplextree.assign_filtration([i], filtration[i]) - simplextree.make_filtration_non_decreasing() - - # Compute persistence diagram - dgm = simplextree.persistence() - - # Get vertex pairs for optimization. First, get all simplex pairs - pairs = simplextree.persistence_pairs() - - # Then, loop over all simplex pairs - indices, pers = [], [] - for s1, s2 in pairs: - # Select pairs with good homological dimension and finite lifetime - if len(s1) == dimension+1 and len(s2) > 0: - # Get IDs of the vertices corresponding to the filtration values of the simplices - l1, l2 = np.array(s1), np.array(s2) - i1 = l1[np.argmax(filtration[l1])] - i2 = l2[np.argmax(filtration[l2])] - indices.append(i1) - indices.append(i2) - # Compute lifetime - pers.append(simplextree.filtration(s2)-simplextree.filtration(s1)) - - # Sort vertex pairs wrt lifetime - perm = np.argsort(pers) - indices = np.reshape(indices, [-1,2])[perm][::-1,:].flatten() - - return np.array(indices, dtype=np.int32) - -class LowerStarSimplexTreeLayer(tf.keras.layers.Layer): - """ - TensorFlow layer for computing lower-star persistence out of a simplex tree - - Attributes: - simplextree (gudhi.SimplexTree()): underlying simplex tree - dimension (int): homology dimension - """ - def __init__(self, simplextree, dimension=0, **kwargs): - super().__init__(dynamic=True, **kwargs) - self.dimension = dimension - self.simplextree = simplextree - - def build(self): - super.build() - - def call(self, filtration): - """ - Compute lower-star persistence diagram associated to a function defined on the vertices of the simplex tree - - Parameters: - F (TensorFlow variable): filter function values over the vertices of the simplex tree - """ - # Don't try to compute gradients for the vertex pairs - indices = tf.stop_gradient(_LowerStarSimplexTree(self.simplextree, filtration.numpy(), self.dimension)) - # Get persistence diagram - self.dgm = tf.reshape(tf.gather(filtration, indices), [-1,2]) - return self.dgm - - - - - - - - - -############################ -# Vietoris-Rips filtration # -############################ - -# The parameters of the model are the point coordinates. - -def _Rips(DX, max_edge, dimension): - # Parameters: DX (distance matrix), - # max_edge (maximum edge length for Rips filtration), - # dimension (homology dimension) - - # Compute the persistence pairs with Gudhi - rc = RipsComplex(distance_matrix=DX, max_edge_length=max_edge) - st = rc.create_simplex_tree(max_dimension=dimension+1) - dgm = st.persistence() - pairs = st.persistence_pairs() - - # Retrieve vertices v_a and v_b by picking the ones achieving the maximal - # distance among all pairwise distances between the simplex vertices - indices, pers = [], [] - for s1, s2 in pairs: - if len(s1) == dimension+1 and len(s2) > 0: - l1, l2 = np.array(s1), np.array(s2) - i1 = [l1[v] for v in np.unravel_index(np.argmax(DX[l1,:][:,l1]),[len(l1), len(l1)])] - i2 = [l2[v] for v in np.unravel_index(np.argmax(DX[l2,:][:,l2]),[len(l2), len(l2)])] - indices.append(i1) - indices.append(i2) - pers.append(st.filtration(s2)-st.filtration(s1)) - - # Sort points with distance-to-diagonal - perm = np.argsort(pers) - indices = np.reshape(indices, [-1,4])[perm][::-1,:].flatten() - - return np.array(indices, dtype=np.int32) - -class RipsLayer(tf.keras.layers.Layer): - """ - TensorFlow layer for computing Rips persistence out of a point cloud - - Attributes: - maximum_edge_length (float): maximum edge length for the Rips complex - dimension (int): homology dimension - """ - def __init__(self, maximum_edge_length=12, dimension=1, **kwargs): - super().__init__(dynamic=True, **kwargs) - self.max_edge = maximum_edge_length - self.dimension = dimension - - def build(self): - super.build() - - def call(self, X): - """ - Compute Rips persistence diagram associated to a point cloud - - Parameters: - X (TensorFlow variable): point cloud of shape [number of points, number of dimensions] - """ - # Compute distance matrix - DX = tf.math.sqrt(tf.reduce_sum((tf.expand_dims(X, 1)-tf.expand_dims(X, 0))**2, 2)) - # Compute vertices associated to positive and negative simplices - # Don't compute gradient for this operation - indices = tf.stop_gradient(_Rips(DX.numpy(), self.max_edge, self.dimension)) - # Get persistence diagram by simply picking the corresponding entries in the distance matrix - if self.dimension > 0: - dgm = tf.reshape(tf.gather_nd(DX, tf.reshape(indices, [-1,2])), [-1,2]) - else: - indices = tf.reshape(indices, [-1,2])[1::2,:] - dgm = tf.concat([tf.zeros([indices.shape[0],1]), tf.reshape(tf.gather_nd(DX, indices), [-1,1])], axis=1) - return dgm - - - - - - - - - -###################### -# Cubical filtration # -###################### - -# The parameters of the model are the pixel values. - -def _Cubical(X, dimension): - # Parameters: X (image), - # dimension (homology dimension) - - # Compute the persistence pairs with Gudhi - cc = CubicalComplex(dimensions=X.shape, top_dimensional_cells=X.flatten()) - cc.persistence() - try: - cof = cc.cofaces_of_persistence_pairs()[0][dimension] - except IndexError: - cof = np.array([]) - - if len(cof) > 0: - # Sort points with distance-to-diagonal - Xs = X.shape - pers = [X[np.unravel_index(cof[idx,1], Xs)] - X[np.unravel_index(cof[idx,0], Xs)] for idx in range(len(cof))] - perm = np.argsort(pers) - cof = cof[perm[::-1]] - - # Retrieve and ouput image indices/pixels corresponding to positive and negative simplices - D = len(Xs) if len(cof) > 0 else 1 - ocof = np.array([0 for _ in range(D*2*cof.shape[0])]) - count = 0 - for idx in range(0,2*cof.shape[0],2): - ocof[D*idx:D*(idx+1)] = np.unravel_index(cof[count,0], Xs) - ocof[D*(idx+1):D*(idx+2)] = np.unravel_index(cof[count,1], Xs) - count += 1 - return np.array(ocof, dtype=np.int32) - -class CubicalLayer(tf.keras.layers.Layer): - """ - TensorFlow layer for computing cubical persistence out of a cubical complex - - Attributes: - dimension (int): homology dimension - """ - def __init__(self, dimension=1, **kwargs): - super().__init__(dynamic=True, **kwargs) - self.dimension = dimension - - def build(self): - super.build() - - def call(self, X): - """ - Compute persistence diagram associated to a cubical complex filtered by some pixel values - - Parameters: - X (TensorFlow variable): pixel values of the cubical complex - """ - # Compute pixels associated to positive and negative simplices - # Don't compute gradient for this operation - indices = tf.stop_gradient(_Cubical(X.numpy(), self.dimension)) - # Get persistence diagram by simply picking the corresponding entries in the image - dgm = tf.reshape(tf.gather_nd(X, tf.reshape(indices, [-1,len(X.shape)])), [-1,2]) - return dgm diff --git a/src/python/gudhi/tensorflow/CubicalLayer.py b/src/python/gudhi/tensorflow/CubicalLayer.py new file mode 100644 index 00000000..e36adec5 --- /dev/null +++ b/src/python/gudhi/tensorflow/CubicalLayer.py @@ -0,0 +1,66 @@ +import numpy as np +import tensorflow as tf +from ..cubical_complex import CubicalComplex + +###################### +# Cubical filtration # +###################### + +# The parameters of the model are the pixel values. + +def _Cubical(X, dimension): + # Parameters: X (image), + # dimension (homology dimension) + + # Compute the persistence pairs with Gudhi + cc = CubicalComplex(dimensions=X.shape, top_dimensional_cells=X.flatten()) + cc.persistence() + try: + cof = cc.cofaces_of_persistence_pairs()[0][dimension] + except IndexError: + cof = np.array([]) + + if len(cof) > 0: + # Sort points with distance-to-diagonal + Xs = X.shape + pers = [X[np.unravel_index(cof[idx,1], Xs)] - X[np.unravel_index(cof[idx,0], Xs)] for idx in range(len(cof))] + perm = np.argsort(pers) + cof = cof[perm[::-1]] + + # Retrieve and ouput image indices/pixels corresponding to positive and negative simplices + D = len(Xs) if len(cof) > 0 else 1 + ocof = np.array([0 for _ in range(D*2*cof.shape[0])]) + count = 0 + for idx in range(0,2*cof.shape[0],2): + ocof[D*idx:D*(idx+1)] = np.unravel_index(cof[count,0], Xs) + ocof[D*(idx+1):D*(idx+2)] = np.unravel_index(cof[count,1], Xs) + count += 1 + return np.array(ocof, dtype=np.int32) + +class CubicalLayer(tf.keras.layers.Layer): + """ + TensorFlow layer for computing cubical persistence out of a cubical complex + + Attributes: + dimension (int): homology dimension + """ + def __init__(self, dimension=1, **kwargs): + super().__init__(dynamic=True, **kwargs) + self.dimension = dimension + + def build(self): + super.build() + + def call(self, X): + """ + Compute persistence diagram associated to a cubical complex filtered by some pixel values + + Parameters: + X (TensorFlow variable): pixel values of the cubical complex + """ + # Compute pixels associated to positive and negative simplices + # Don't compute gradient for this operation + indices = tf.stop_gradient(_Cubical(X.numpy(), self.dimension)) + # Get persistence diagram by simply picking the corresponding entries in the image + dgm = tf.reshape(tf.gather_nd(X, tf.reshape(indices, [-1,len(X.shape)])), [-1,2]) + return dgm diff --git a/src/python/gudhi/tensorflow/LowerStarSimplexTreeLayer.py b/src/python/gudhi/tensorflow/LowerStarSimplexTreeLayer.py new file mode 100644 index 00000000..fc963d2f --- /dev/null +++ b/src/python/gudhi/tensorflow/LowerStarSimplexTreeLayer.py @@ -0,0 +1,77 @@ +import numpy as np +import tensorflow as tf + +######################################### +# Lower star filtration on simplex tree # +######################################### + +# The parameters of the model are the vertex function values of the simplex tree. + +def _LowerStarSimplexTree(simplextree, filtration, dimension): + # Parameters: simplextree (simplex tree on which to compute persistence) + # filtration (function values on the vertices of st), + # dimension (homology dimension), + + for s,_ in simplextree.get_filtration(): + simplextree.assign_filtration(s, -1e10) + + # Assign new filtration values + for i in range(simplextree.num_vertices()): + simplextree.assign_filtration([i], filtration[i]) + simplextree.make_filtration_non_decreasing() + + # Compute persistence diagram + dgm = simplextree.persistence() + + # Get vertex pairs for optimization. First, get all simplex pairs + pairs = simplextree.persistence_pairs() + + # Then, loop over all simplex pairs + indices, pers = [], [] + for s1, s2 in pairs: + # Select pairs with good homological dimension and finite lifetime + if len(s1) == dimension+1 and len(s2) > 0: + # Get IDs of the vertices corresponding to the filtration values of the simplices + l1, l2 = np.array(s1), np.array(s2) + i1 = l1[np.argmax(filtration[l1])] + i2 = l2[np.argmax(filtration[l2])] + indices.append(i1) + indices.append(i2) + # Compute lifetime + pers.append(simplextree.filtration(s2)-simplextree.filtration(s1)) + + # Sort vertex pairs wrt lifetime + perm = np.argsort(pers) + indices = np.reshape(indices, [-1,2])[perm][::-1,:].flatten() + + return np.array(indices, dtype=np.int32) + +class LowerStarSimplexTreeLayer(tf.keras.layers.Layer): + """ + TensorFlow layer for computing lower-star persistence out of a simplex tree + + Attributes: + simplextree (gudhi.SimplexTree()): underlying simplex tree + dimension (int): homology dimension + """ + def __init__(self, simplextree, dimension=0, **kwargs): + super().__init__(dynamic=True, **kwargs) + self.dimension = dimension + self.simplextree = simplextree + + def build(self): + super.build() + + def call(self, filtration): + """ + Compute lower-star persistence diagram associated to a function defined on the vertices of the simplex tree + + Parameters: + F (TensorFlow variable): filter function values over the vertices of the simplex tree + """ + # Don't try to compute gradients for the vertex pairs + indices = tf.stop_gradient(_LowerStarSimplexTree(self.simplextree, filtration.numpy(), self.dimension)) + # Get persistence diagram + self.dgm = tf.reshape(tf.gather(filtration, indices), [-1,2]) + return self.dgm + diff --git a/src/python/gudhi/tensorflow/RipsLayer.py b/src/python/gudhi/tensorflow/RipsLayer.py new file mode 100644 index 00000000..373e021e --- /dev/null +++ b/src/python/gudhi/tensorflow/RipsLayer.py @@ -0,0 +1,75 @@ +import numpy as np +import tensorflow as tf +from ..rips_complex import RipsComplex + +############################ +# Vietoris-Rips filtration # +############################ + +# The parameters of the model are the point coordinates. + +def _Rips(DX, max_edge, dimension): + # Parameters: DX (distance matrix), + # max_edge (maximum edge length for Rips filtration), + # dimension (homology dimension) + + # Compute the persistence pairs with Gudhi + rc = RipsComplex(distance_matrix=DX, max_edge_length=max_edge) + st = rc.create_simplex_tree(max_dimension=dimension+1) + dgm = st.persistence() + pairs = st.persistence_pairs() + + # Retrieve vertices v_a and v_b by picking the ones achieving the maximal + # distance among all pairwise distances between the simplex vertices + indices, pers = [], [] + for s1, s2 in pairs: + if len(s1) == dimension+1 and len(s2) > 0: + l1, l2 = np.array(s1), np.array(s2) + i1 = [l1[v] for v in np.unravel_index(np.argmax(DX[l1,:][:,l1]),[len(l1), len(l1)])] + i2 = [l2[v] for v in np.unravel_index(np.argmax(DX[l2,:][:,l2]),[len(l2), len(l2)])] + indices.append(i1) + indices.append(i2) + pers.append(st.filtration(s2)-st.filtration(s1)) + + # Sort points with distance-to-diagonal + perm = np.argsort(pers) + indices = np.reshape(indices, [-1,4])[perm][::-1,:].flatten() + + return np.array(indices, dtype=np.int32) + +class RipsLayer(tf.keras.layers.Layer): + """ + TensorFlow layer for computing Rips persistence out of a point cloud + + Attributes: + maximum_edge_length (float): maximum edge length for the Rips complex + dimension (int): homology dimension + """ + def __init__(self, maximum_edge_length=12, dimension=1, **kwargs): + super().__init__(dynamic=True, **kwargs) + self.max_edge = maximum_edge_length + self.dimension = dimension + + def build(self): + super.build() + + def call(self, X): + """ + Compute Rips persistence diagram associated to a point cloud + + Parameters: + X (TensorFlow variable): point cloud of shape [number of points, number of dimensions] + """ + # Compute distance matrix + DX = tf.math.sqrt(tf.reduce_sum((tf.expand_dims(X, 1)-tf.expand_dims(X, 0))**2, 2)) + # Compute vertices associated to positive and negative simplices + # Don't compute gradient for this operation + indices = tf.stop_gradient(_Rips(DX.numpy(), self.max_edge, self.dimension)) + # Get persistence diagram by simply picking the corresponding entries in the distance matrix + if self.dimension > 0: + dgm = tf.reshape(tf.gather_nd(DX, tf.reshape(indices, [-1,2])), [-1,2]) + else: + indices = tf.reshape(indices, [-1,2])[1::2,:] + dgm = tf.concat([tf.zeros([indices.shape[0],1]), tf.reshape(tf.gather_nd(DX, indices), [-1,1])], axis=1) + return dgm + diff --git a/src/python/gudhi/tensorflow/__init__.py b/src/python/gudhi/tensorflow/__init__.py new file mode 100644 index 00000000..47335a25 --- /dev/null +++ b/src/python/gudhi/tensorflow/__init__.py @@ -0,0 +1,5 @@ +from .CubicalLayer import CubicalLayer +from .LowerStarSimplexTreeLayer import LowerStarSimplexTreeLayer +from .RipsLayer import RipsLayer + +__all__ = ["LowerStarSimplexTreeLayer", "RipsLayer", "CubicalLayer"] diff --git a/src/python/test/test_diff.py b/src/python/test/test_diff.py index 129b9f03..73a03697 100644 --- a/src/python/test/test_diff.py +++ b/src/python/test/test_diff.py @@ -1,4 +1,4 @@ -from gudhi.differentiation import * +from gudhi.tensorflow import * import numpy as np import tensorflow as tf import gudhi as gd -- cgit v1.2.3 From fc1c33d19c7d50d01bacb61529badbde8217ce7e Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Tue, 19 Oct 2021 12:27:06 +0200 Subject: update on python documentation --- src/python/doc/cubical_complex_sum.inc | 25 ++++++----- src/python/doc/cubical_complex_tflow_itf_ref.rst | 33 ++++++++++++++ src/python/doc/differentiation.rst | 18 -------- src/python/doc/differentiation_sum.inc | 22 ++++----- src/python/doc/ls_simplex_tree_tflow_itf_ref.rst | 57 ++++++++++++++++++++++++ src/python/doc/rips_complex_sum.inc | 5 ++- src/python/doc/rips_complex_tflow_itf_ref.rst | 33 ++++++++++++++ src/python/doc/simplex_tree_sum.inc | 23 +++++----- 8 files changed, 163 insertions(+), 53 deletions(-) create mode 100644 src/python/doc/cubical_complex_tflow_itf_ref.rst delete mode 100644 src/python/doc/differentiation.rst create mode 100644 src/python/doc/ls_simplex_tree_tflow_itf_ref.rst create mode 100644 src/python/doc/rips_complex_tflow_itf_ref.rst (limited to 'src') diff --git a/src/python/doc/cubical_complex_sum.inc b/src/python/doc/cubical_complex_sum.inc index 87db184d..90ec9fc2 100644 --- a/src/python/doc/cubical_complex_sum.inc +++ b/src/python/doc/cubical_complex_sum.inc @@ -1,14 +1,17 @@ .. table:: :widths: 30 40 30 - +--------------------------------------------------------------------------+----------------------------------------------------------------------+-----------------------------+ - | .. figure:: | The cubical complex represents a grid as a cell complex with | :Author: Pawel Dlotko | - | ../../doc/Bitmap_cubical_complex/Cubical_complex_representation.png | cells of all dimensions. | | - | :alt: Cubical complex representation | | :Since: GUDHI 2.0.0 | - | :figclass: align-center | | | - | | | :License: MIT | - | | | | - +--------------------------------------------------------------------------+----------------------------------------------------------------------+-----------------------------+ - | * :doc:`cubical_complex_user` | * :doc:`cubical_complex_ref` | - | | * :doc:`periodic_cubical_complex_ref` | - +--------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ + +--------------------------------------------------------------------------+----------------------------------------------------------------------+---------------------------------------------------------+ + | .. figure:: | The cubical complex represents a grid as a cell complex with | :Author: Pawel Dlotko | + | ../../doc/Bitmap_cubical_complex/Cubical_complex_representation.png | cells of all dimensions. | | + | :alt: Cubical complex representation | | :Since: GUDHI 2.0.0 | + | :figclass: align-center | | | + | | | :License: MIT | + | | | | + +--------------------------------------------------------------------------+----------------------------------------------------------------------+---------------------------------------------------------+ + | * :doc:`cubical_complex_user` | * :doc:`cubical_complex_ref` | + | | * :doc:`periodic_cubical_complex_ref` | + +--------------------------------------------------------------------------+----------------------------------------------------------------------+---------------------------------------------------------+ + | | * :doc:`cubical_complex_tflow_itf_ref` | :requires: `TensorFlow `_ | + | | | | + +--------------------------------------------------------------------------+----------------------------------------------------------------------+---------------------------------------------------------+ diff --git a/src/python/doc/cubical_complex_tflow_itf_ref.rst b/src/python/doc/cubical_complex_tflow_itf_ref.rst new file mode 100644 index 00000000..8991b638 --- /dev/null +++ b/src/python/doc/cubical_complex_tflow_itf_ref.rst @@ -0,0 +1,33 @@ +:orphan: + +.. To get rid of WARNING: document isn't included in any toctree + +TensorFlow layer for cubical persistence +######################################## + +.. include:: differentiation_sum.inc + +Example of gradient computed from cubical persistence +----------------------------------------------------- + +.. code-block:: python + from gudhi.tensorflow import * + import numpy as np + import tensorflow as tf + + Xinit = np.array([[0.,2.,2.],[2.,2.,2.],[2.,2.,1.]], dtype=np.float32) + X = tf.Variable(initial_value=Xinit, trainable=True) + cl = CubicalLayer(dimension=0) + + with tf.GradientTape() as tape: + dgm = cl.call(X) + loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) + grads = tape.gradient(loss, [X]) + +Documentation for CubicalLayer +------------------------------ + +.. autoclass:: gudhi.tensorflow.CubicalLayer + :members: + :special-members: __init__ + :show-inheritance: diff --git a/src/python/doc/differentiation.rst b/src/python/doc/differentiation.rst deleted file mode 100644 index 906a9965..00000000 --- a/src/python/doc/differentiation.rst +++ /dev/null @@ -1,18 +0,0 @@ -:orphan: - -.. To get rid of WARNING: document isn't included in any toctree - -====================== -Differentiation manual -====================== - -.. include:: differentiation_sum.inc - -In this module, we provide neural network models for computing persistent homology. In particular, we provide TensorFlow 2 models that allow to compute persistence diagrams from complexes available in the Gudhi library, including simplex trees, cubical complexes and Vietoris-Rips complexes. These models can be incorporated at each step of a given neural network architecture, and can be used in addition to `PersLay `_ to produce topological features. - -TensorFlow models ------------------ -.. automodule:: gudhi.differentiation - :members: - :special-members: - :show-inheritance: diff --git a/src/python/doc/differentiation_sum.inc b/src/python/doc/differentiation_sum.inc index 30188e0b..7340d24d 100644 --- a/src/python/doc/differentiation_sum.inc +++ b/src/python/doc/differentiation_sum.inc @@ -1,14 +1,10 @@ -.. table:: - :widths: 30 40 30 +. list-table:: + :widths: 40 30 30 + :header-rows: 0 - +------------------------------------------------------------------+----------------------------------------------------------------+-------------------------------------------------------------+ - | .. figure:: | Deep learning models for differentiating persistence diagrams. | :Author: Mathieu Carrière | - | img/ripsTF.png | | | - | | | :Since: GUDHI 3.1.0 | - | | | | - | | | :License: MIT | - | | | | - | | | :Requires: `TensorFlow 2 `_ | - +------------------------------------------------------------------+----------------------------------------------------------------+-------------------------------------------------------------+ - | * :doc:`differentiation` | - +------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------+ + * - :Since: GUDHI 3.5.0 + - :License: MIT + - :Requires: `TensorFlow `_ + +We provide TensorFlow 2 models that can handle automatic differentiation for the computation of persistence diagrams from complexes available in the Gudhi library. +This includes simplex trees, cubical complexes and Vietoris-Rips complexes. diff --git a/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst b/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst new file mode 100644 index 00000000..bb9c61c6 --- /dev/null +++ b/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst @@ -0,0 +1,57 @@ +:orphan: + +.. To get rid of WARNING: document isn't included in any toctree + +TensorFlow layer for lower-star persistence on simplex trees +############################################################ + +.. include:: differentiation_sum.inc + +Example of gradient computed from lower-star filtration of a simplex tree +------------------------------------------------------------------------- + +.. code-block:: python + from gudhi.tensorflow import * + import numpy as np + import tensorflow as tf + import gudhi as gd + + st = gd.SimplexTree() + st.insert([0]) + st.insert([1]) + st.insert([2]) + st.insert([3]) + st.insert([4]) + st.insert([5]) + st.insert([6]) + st.insert([7]) + st.insert([8]) + st.insert([9]) + st.insert([10]) + st.insert([0, 1]) + st.insert([1, 2]) + st.insert([2, 3]) + st.insert([3, 4]) + st.insert([4, 5]) + st.insert([5, 6]) + st.insert([6, 7]) + st.insert([7, 8]) + st.insert([8, 9]) + st.insert([9, 10]) + + Finit = np.array([6.,4.,3.,4.,5.,4.,3.,2.,3.,4.,5.], dtype=np.float32) + F = tf.Variable(initial_value=Finit, trainable=True) + sl = LowerStarSimplexTreeLayer(simplextree=st, dimension=0) + + with tf.GradientTape() as tape: + dgm = sl.call(F) + loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) + grads = tape.gradient(loss, [F]) + +Documentation for LowerStarSimplexTreeLayer +------------------------------------------- + +.. autoclass:: gudhi.tensorflow.LowerStarSimplexTreeLayer + :members: + :special-members: __init__ + :show-inheritance: diff --git a/src/python/doc/rips_complex_sum.inc b/src/python/doc/rips_complex_sum.inc index 2cb24990..6931ebee 100644 --- a/src/python/doc/rips_complex_sum.inc +++ b/src/python/doc/rips_complex_sum.inc @@ -11,4 +11,7 @@ | | | | +----------------------------------------------------------------+------------------------------------------------------------------------+----------------------------------------------------------------------------------+ | * :doc:`rips_complex_user` | * :doc:`rips_complex_ref` | - +----------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ + +----------------------------------------------------------------+------------------------------------------------------------------------+----------------------------------------------------------------------------------+ + | | * :doc:`rips_complex_tflow_itf_ref` | :requires: `TensorFlow `_ | + | | | | + +----------------------------------------------------------------+------------------------------------------------------------------------+----------------------------------------------------------------------------------+ diff --git a/src/python/doc/rips_complex_tflow_itf_ref.rst b/src/python/doc/rips_complex_tflow_itf_ref.rst new file mode 100644 index 00000000..db8c64ff --- /dev/null +++ b/src/python/doc/rips_complex_tflow_itf_ref.rst @@ -0,0 +1,33 @@ +:orphan: + +.. To get rid of WARNING: document isn't included in any toctree + +TensorFlow layer for Vietoris-Rips persistence +############################################## + +.. include:: differentiation_sum.inc + +Example of gradient computed from Vietoris-Rips persistence +----------------------------------------------------------- + +.. code-block:: python + from gudhi.tensorflow import * + import numpy as np + import tensorflow as tf + + Xinit = np.array([[1.,1.],[2.,2.]], dtype=np.float32) + X = tf.Variable(initial_value=Xinit, trainable=True) + rl = RipsLayer(maximum_edge_length=2., dimension=0) + + with tf.GradientTape() as tape: + dgm = rl.call(X) + loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) + grads = tape.gradient(loss, [X]) + +Documentation for RipsLayer +--------------------------- + +.. autoclass:: gudhi.tensorflow.RipsLayer + :members: + :special-members: __init__ + :show-inheritance: diff --git a/src/python/doc/simplex_tree_sum.inc b/src/python/doc/simplex_tree_sum.inc index a8858f16..3ad1292c 100644 --- a/src/python/doc/simplex_tree_sum.inc +++ b/src/python/doc/simplex_tree_sum.inc @@ -1,13 +1,16 @@ .. table:: :widths: 30 40 30 - +----------------------------------------------------------------+------------------------------------------------------------------------+-----------------------------+ - | .. figure:: | The simplex tree is an efficient and flexible data structure for | :Author: Clément Maria | - | ../../doc/Simplex_tree/Simplex_tree_representation.png | representing general (filtered) simplicial complexes. | | - | :alt: Simplex tree representation | | :Since: GUDHI 2.0.0 | - | :figclass: align-center | The data structure is described in | | - | | :cite:`boissonnatmariasimplextreealgorithmica` | :License: MIT | - | | | | - +----------------------------------------------------------------+------------------------------------------------------------------------+-----------------------------+ - | * :doc:`simplex_tree_user` | * :doc:`simplex_tree_ref` | - +----------------------------------------------------------------+------------------------------------------------------------------------------------------------------+ + +----------------------------------------------------------------+------------------------------------------------------------------------+---------------------------------------------------------+ + | .. figure:: | The simplex tree is an efficient and flexible data structure for | :Author: Clément Maria | + | ../../doc/Simplex_tree/Simplex_tree_representation.png | representing general (filtered) simplicial complexes. | | + | :alt: Simplex tree representation | | :Since: GUDHI 2.0.0 | + | :figclass: align-center | The data structure is described in | | + | | :cite:`boissonnatmariasimplextreealgorithmica` | :License: MIT | + | | | | + +----------------------------------------------------------------+------------------------------------------------------------------------+---------------------------------------------------------+ + | * :doc:`simplex_tree_user` | * :doc:`simplex_tree_ref` | + +----------------------------------------------------------------+------------------------------------------------------------------------+---------------------------------------------------------+ + | | * :doc:`ls_simplex_tree_tflow_itf_ref` | :requires: `TensorFlow `_ | + | | | | + +----------------------------------------------------------------+------------------------------------------------------------------------+---------------------------------------------------------+ -- cgit v1.2.3 From 423c4be21968fd42c5470a9132d0e332c73ec2b9 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Tue, 19 Oct 2021 14:37:39 +0200 Subject: fix python documentation --- src/python/doc/cubical_complex_tflow_itf_ref.rst | 2 ++ src/python/doc/differentiation_sum.inc | 5 +++-- src/python/doc/ls_simplex_tree_tflow_itf_ref.rst | 2 ++ src/python/doc/rips_complex_tflow_itf_ref.rst | 2 ++ 4 files changed, 9 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/python/doc/cubical_complex_tflow_itf_ref.rst b/src/python/doc/cubical_complex_tflow_itf_ref.rst index 8991b638..e85cfdc6 100644 --- a/src/python/doc/cubical_complex_tflow_itf_ref.rst +++ b/src/python/doc/cubical_complex_tflow_itf_ref.rst @@ -11,6 +11,7 @@ Example of gradient computed from cubical persistence ----------------------------------------------------- .. code-block:: python + from gudhi.tensorflow import * import numpy as np import tensorflow as tf @@ -23,6 +24,7 @@ Example of gradient computed from cubical persistence dgm = cl.call(X) loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) grads = tape.gradient(loss, [X]) + print(grads[0].numpy()) Documentation for CubicalLayer ------------------------------ diff --git a/src/python/doc/differentiation_sum.inc b/src/python/doc/differentiation_sum.inc index 7340d24d..3dd8e59c 100644 --- a/src/python/doc/differentiation_sum.inc +++ b/src/python/doc/differentiation_sum.inc @@ -1,4 +1,4 @@ -. list-table:: +.. list-table:: :widths: 40 30 30 :header-rows: 0 @@ -7,4 +7,5 @@ - :Requires: `TensorFlow `_ We provide TensorFlow 2 models that can handle automatic differentiation for the computation of persistence diagrams from complexes available in the Gudhi library. -This includes simplex trees, cubical complexes and Vietoris-Rips complexes. +This includes simplex trees, cubical complexes and Vietoris-Rips complexes. Detailed example on how to use these layers in practice are available +in the following `notebook `_. diff --git a/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst b/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst index bb9c61c6..7baf611c 100644 --- a/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst +++ b/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst @@ -11,6 +11,7 @@ Example of gradient computed from lower-star filtration of a simplex tree ------------------------------------------------------------------------- .. code-block:: python + from gudhi.tensorflow import * import numpy as np import tensorflow as tf @@ -47,6 +48,7 @@ Example of gradient computed from lower-star filtration of a simplex tree dgm = sl.call(F) loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) grads = tape.gradient(loss, [F]) + print(grads[0].numpy()) Documentation for LowerStarSimplexTreeLayer ------------------------------------------- diff --git a/src/python/doc/rips_complex_tflow_itf_ref.rst b/src/python/doc/rips_complex_tflow_itf_ref.rst index db8c64ff..15ba4c8e 100644 --- a/src/python/doc/rips_complex_tflow_itf_ref.rst +++ b/src/python/doc/rips_complex_tflow_itf_ref.rst @@ -11,6 +11,7 @@ Example of gradient computed from Vietoris-Rips persistence ----------------------------------------------------------- .. code-block:: python + from gudhi.tensorflow import * import numpy as np import tensorflow as tf @@ -23,6 +24,7 @@ Example of gradient computed from Vietoris-Rips persistence dgm = rl.call(X) loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) grads = tape.gradient(loss, [X]) + print(grads[0].numpy()) Documentation for RipsLayer --------------------------- -- cgit v1.2.3 From 10be82856aee6eb7f4e704757b70c9dab6fe28b8 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Tue, 19 Oct 2021 16:09:08 +0200 Subject: cleanup --- src/python/doc/img/ripsTF.png | Bin 38696 -> 0 bytes src/python/gudhi/tensorflow/CubicalLayer.py | 66 ------------------ .../gudhi/tensorflow/LowerStarSimplexTreeLayer.py | 77 --------------------- src/python/gudhi/tensorflow/RipsLayer.py | 75 -------------------- src/python/gudhi/tensorflow/__init__.py | 6 +- src/python/gudhi/tensorflow/cubical_layer.py | 66 ++++++++++++++++++ .../tensorflow/lower_star_simplex_tree_layer.py | 77 +++++++++++++++++++++ src/python/gudhi/tensorflow/rips_layer.py | 75 ++++++++++++++++++++ 8 files changed, 221 insertions(+), 221 deletions(-) delete mode 100644 src/python/doc/img/ripsTF.png delete mode 100644 src/python/gudhi/tensorflow/CubicalLayer.py delete mode 100644 src/python/gudhi/tensorflow/LowerStarSimplexTreeLayer.py delete mode 100644 src/python/gudhi/tensorflow/RipsLayer.py create mode 100644 src/python/gudhi/tensorflow/cubical_layer.py create mode 100644 src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py create mode 100644 src/python/gudhi/tensorflow/rips_layer.py (limited to 'src') diff --git a/src/python/doc/img/ripsTF.png b/src/python/doc/img/ripsTF.png deleted file mode 100644 index 3c5c77a7..00000000 Binary files a/src/python/doc/img/ripsTF.png and /dev/null differ diff --git a/src/python/gudhi/tensorflow/CubicalLayer.py b/src/python/gudhi/tensorflow/CubicalLayer.py deleted file mode 100644 index e36adec5..00000000 --- a/src/python/gudhi/tensorflow/CubicalLayer.py +++ /dev/null @@ -1,66 +0,0 @@ -import numpy as np -import tensorflow as tf -from ..cubical_complex import CubicalComplex - -###################### -# Cubical filtration # -###################### - -# The parameters of the model are the pixel values. - -def _Cubical(X, dimension): - # Parameters: X (image), - # dimension (homology dimension) - - # Compute the persistence pairs with Gudhi - cc = CubicalComplex(dimensions=X.shape, top_dimensional_cells=X.flatten()) - cc.persistence() - try: - cof = cc.cofaces_of_persistence_pairs()[0][dimension] - except IndexError: - cof = np.array([]) - - if len(cof) > 0: - # Sort points with distance-to-diagonal - Xs = X.shape - pers = [X[np.unravel_index(cof[idx,1], Xs)] - X[np.unravel_index(cof[idx,0], Xs)] for idx in range(len(cof))] - perm = np.argsort(pers) - cof = cof[perm[::-1]] - - # Retrieve and ouput image indices/pixels corresponding to positive and negative simplices - D = len(Xs) if len(cof) > 0 else 1 - ocof = np.array([0 for _ in range(D*2*cof.shape[0])]) - count = 0 - for idx in range(0,2*cof.shape[0],2): - ocof[D*idx:D*(idx+1)] = np.unravel_index(cof[count,0], Xs) - ocof[D*(idx+1):D*(idx+2)] = np.unravel_index(cof[count,1], Xs) - count += 1 - return np.array(ocof, dtype=np.int32) - -class CubicalLayer(tf.keras.layers.Layer): - """ - TensorFlow layer for computing cubical persistence out of a cubical complex - - Attributes: - dimension (int): homology dimension - """ - def __init__(self, dimension=1, **kwargs): - super().__init__(dynamic=True, **kwargs) - self.dimension = dimension - - def build(self): - super.build() - - def call(self, X): - """ - Compute persistence diagram associated to a cubical complex filtered by some pixel values - - Parameters: - X (TensorFlow variable): pixel values of the cubical complex - """ - # Compute pixels associated to positive and negative simplices - # Don't compute gradient for this operation - indices = tf.stop_gradient(_Cubical(X.numpy(), self.dimension)) - # Get persistence diagram by simply picking the corresponding entries in the image - dgm = tf.reshape(tf.gather_nd(X, tf.reshape(indices, [-1,len(X.shape)])), [-1,2]) - return dgm diff --git a/src/python/gudhi/tensorflow/LowerStarSimplexTreeLayer.py b/src/python/gudhi/tensorflow/LowerStarSimplexTreeLayer.py deleted file mode 100644 index fc963d2f..00000000 --- a/src/python/gudhi/tensorflow/LowerStarSimplexTreeLayer.py +++ /dev/null @@ -1,77 +0,0 @@ -import numpy as np -import tensorflow as tf - -######################################### -# Lower star filtration on simplex tree # -######################################### - -# The parameters of the model are the vertex function values of the simplex tree. - -def _LowerStarSimplexTree(simplextree, filtration, dimension): - # Parameters: simplextree (simplex tree on which to compute persistence) - # filtration (function values on the vertices of st), - # dimension (homology dimension), - - for s,_ in simplextree.get_filtration(): - simplextree.assign_filtration(s, -1e10) - - # Assign new filtration values - for i in range(simplextree.num_vertices()): - simplextree.assign_filtration([i], filtration[i]) - simplextree.make_filtration_non_decreasing() - - # Compute persistence diagram - dgm = simplextree.persistence() - - # Get vertex pairs for optimization. First, get all simplex pairs - pairs = simplextree.persistence_pairs() - - # Then, loop over all simplex pairs - indices, pers = [], [] - for s1, s2 in pairs: - # Select pairs with good homological dimension and finite lifetime - if len(s1) == dimension+1 and len(s2) > 0: - # Get IDs of the vertices corresponding to the filtration values of the simplices - l1, l2 = np.array(s1), np.array(s2) - i1 = l1[np.argmax(filtration[l1])] - i2 = l2[np.argmax(filtration[l2])] - indices.append(i1) - indices.append(i2) - # Compute lifetime - pers.append(simplextree.filtration(s2)-simplextree.filtration(s1)) - - # Sort vertex pairs wrt lifetime - perm = np.argsort(pers) - indices = np.reshape(indices, [-1,2])[perm][::-1,:].flatten() - - return np.array(indices, dtype=np.int32) - -class LowerStarSimplexTreeLayer(tf.keras.layers.Layer): - """ - TensorFlow layer for computing lower-star persistence out of a simplex tree - - Attributes: - simplextree (gudhi.SimplexTree()): underlying simplex tree - dimension (int): homology dimension - """ - def __init__(self, simplextree, dimension=0, **kwargs): - super().__init__(dynamic=True, **kwargs) - self.dimension = dimension - self.simplextree = simplextree - - def build(self): - super.build() - - def call(self, filtration): - """ - Compute lower-star persistence diagram associated to a function defined on the vertices of the simplex tree - - Parameters: - F (TensorFlow variable): filter function values over the vertices of the simplex tree - """ - # Don't try to compute gradients for the vertex pairs - indices = tf.stop_gradient(_LowerStarSimplexTree(self.simplextree, filtration.numpy(), self.dimension)) - # Get persistence diagram - self.dgm = tf.reshape(tf.gather(filtration, indices), [-1,2]) - return self.dgm - diff --git a/src/python/gudhi/tensorflow/RipsLayer.py b/src/python/gudhi/tensorflow/RipsLayer.py deleted file mode 100644 index 373e021e..00000000 --- a/src/python/gudhi/tensorflow/RipsLayer.py +++ /dev/null @@ -1,75 +0,0 @@ -import numpy as np -import tensorflow as tf -from ..rips_complex import RipsComplex - -############################ -# Vietoris-Rips filtration # -############################ - -# The parameters of the model are the point coordinates. - -def _Rips(DX, max_edge, dimension): - # Parameters: DX (distance matrix), - # max_edge (maximum edge length for Rips filtration), - # dimension (homology dimension) - - # Compute the persistence pairs with Gudhi - rc = RipsComplex(distance_matrix=DX, max_edge_length=max_edge) - st = rc.create_simplex_tree(max_dimension=dimension+1) - dgm = st.persistence() - pairs = st.persistence_pairs() - - # Retrieve vertices v_a and v_b by picking the ones achieving the maximal - # distance among all pairwise distances between the simplex vertices - indices, pers = [], [] - for s1, s2 in pairs: - if len(s1) == dimension+1 and len(s2) > 0: - l1, l2 = np.array(s1), np.array(s2) - i1 = [l1[v] for v in np.unravel_index(np.argmax(DX[l1,:][:,l1]),[len(l1), len(l1)])] - i2 = [l2[v] for v in np.unravel_index(np.argmax(DX[l2,:][:,l2]),[len(l2), len(l2)])] - indices.append(i1) - indices.append(i2) - pers.append(st.filtration(s2)-st.filtration(s1)) - - # Sort points with distance-to-diagonal - perm = np.argsort(pers) - indices = np.reshape(indices, [-1,4])[perm][::-1,:].flatten() - - return np.array(indices, dtype=np.int32) - -class RipsLayer(tf.keras.layers.Layer): - """ - TensorFlow layer for computing Rips persistence out of a point cloud - - Attributes: - maximum_edge_length (float): maximum edge length for the Rips complex - dimension (int): homology dimension - """ - def __init__(self, maximum_edge_length=12, dimension=1, **kwargs): - super().__init__(dynamic=True, **kwargs) - self.max_edge = maximum_edge_length - self.dimension = dimension - - def build(self): - super.build() - - def call(self, X): - """ - Compute Rips persistence diagram associated to a point cloud - - Parameters: - X (TensorFlow variable): point cloud of shape [number of points, number of dimensions] - """ - # Compute distance matrix - DX = tf.math.sqrt(tf.reduce_sum((tf.expand_dims(X, 1)-tf.expand_dims(X, 0))**2, 2)) - # Compute vertices associated to positive and negative simplices - # Don't compute gradient for this operation - indices = tf.stop_gradient(_Rips(DX.numpy(), self.max_edge, self.dimension)) - # Get persistence diagram by simply picking the corresponding entries in the distance matrix - if self.dimension > 0: - dgm = tf.reshape(tf.gather_nd(DX, tf.reshape(indices, [-1,2])), [-1,2]) - else: - indices = tf.reshape(indices, [-1,2])[1::2,:] - dgm = tf.concat([tf.zeros([indices.shape[0],1]), tf.reshape(tf.gather_nd(DX, indices), [-1,1])], axis=1) - return dgm - diff --git a/src/python/gudhi/tensorflow/__init__.py b/src/python/gudhi/tensorflow/__init__.py index 47335a25..1599cf52 100644 --- a/src/python/gudhi/tensorflow/__init__.py +++ b/src/python/gudhi/tensorflow/__init__.py @@ -1,5 +1,5 @@ -from .CubicalLayer import CubicalLayer -from .LowerStarSimplexTreeLayer import LowerStarSimplexTreeLayer -from .RipsLayer import RipsLayer +from .cubical_layer import CubicalLayer +from .lower_star_simplex_tree_layer import LowerStarSimplexTreeLayer +from .rips_layer import RipsLayer __all__ = ["LowerStarSimplexTreeLayer", "RipsLayer", "CubicalLayer"] diff --git a/src/python/gudhi/tensorflow/cubical_layer.py b/src/python/gudhi/tensorflow/cubical_layer.py new file mode 100644 index 00000000..e36adec5 --- /dev/null +++ b/src/python/gudhi/tensorflow/cubical_layer.py @@ -0,0 +1,66 @@ +import numpy as np +import tensorflow as tf +from ..cubical_complex import CubicalComplex + +###################### +# Cubical filtration # +###################### + +# The parameters of the model are the pixel values. + +def _Cubical(X, dimension): + # Parameters: X (image), + # dimension (homology dimension) + + # Compute the persistence pairs with Gudhi + cc = CubicalComplex(dimensions=X.shape, top_dimensional_cells=X.flatten()) + cc.persistence() + try: + cof = cc.cofaces_of_persistence_pairs()[0][dimension] + except IndexError: + cof = np.array([]) + + if len(cof) > 0: + # Sort points with distance-to-diagonal + Xs = X.shape + pers = [X[np.unravel_index(cof[idx,1], Xs)] - X[np.unravel_index(cof[idx,0], Xs)] for idx in range(len(cof))] + perm = np.argsort(pers) + cof = cof[perm[::-1]] + + # Retrieve and ouput image indices/pixels corresponding to positive and negative simplices + D = len(Xs) if len(cof) > 0 else 1 + ocof = np.array([0 for _ in range(D*2*cof.shape[0])]) + count = 0 + for idx in range(0,2*cof.shape[0],2): + ocof[D*idx:D*(idx+1)] = np.unravel_index(cof[count,0], Xs) + ocof[D*(idx+1):D*(idx+2)] = np.unravel_index(cof[count,1], Xs) + count += 1 + return np.array(ocof, dtype=np.int32) + +class CubicalLayer(tf.keras.layers.Layer): + """ + TensorFlow layer for computing cubical persistence out of a cubical complex + + Attributes: + dimension (int): homology dimension + """ + def __init__(self, dimension=1, **kwargs): + super().__init__(dynamic=True, **kwargs) + self.dimension = dimension + + def build(self): + super.build() + + def call(self, X): + """ + Compute persistence diagram associated to a cubical complex filtered by some pixel values + + Parameters: + X (TensorFlow variable): pixel values of the cubical complex + """ + # Compute pixels associated to positive and negative simplices + # Don't compute gradient for this operation + indices = tf.stop_gradient(_Cubical(X.numpy(), self.dimension)) + # Get persistence diagram by simply picking the corresponding entries in the image + dgm = tf.reshape(tf.gather_nd(X, tf.reshape(indices, [-1,len(X.shape)])), [-1,2]) + return dgm diff --git a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py new file mode 100644 index 00000000..fc963d2f --- /dev/null +++ b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py @@ -0,0 +1,77 @@ +import numpy as np +import tensorflow as tf + +######################################### +# Lower star filtration on simplex tree # +######################################### + +# The parameters of the model are the vertex function values of the simplex tree. + +def _LowerStarSimplexTree(simplextree, filtration, dimension): + # Parameters: simplextree (simplex tree on which to compute persistence) + # filtration (function values on the vertices of st), + # dimension (homology dimension), + + for s,_ in simplextree.get_filtration(): + simplextree.assign_filtration(s, -1e10) + + # Assign new filtration values + for i in range(simplextree.num_vertices()): + simplextree.assign_filtration([i], filtration[i]) + simplextree.make_filtration_non_decreasing() + + # Compute persistence diagram + dgm = simplextree.persistence() + + # Get vertex pairs for optimization. First, get all simplex pairs + pairs = simplextree.persistence_pairs() + + # Then, loop over all simplex pairs + indices, pers = [], [] + for s1, s2 in pairs: + # Select pairs with good homological dimension and finite lifetime + if len(s1) == dimension+1 and len(s2) > 0: + # Get IDs of the vertices corresponding to the filtration values of the simplices + l1, l2 = np.array(s1), np.array(s2) + i1 = l1[np.argmax(filtration[l1])] + i2 = l2[np.argmax(filtration[l2])] + indices.append(i1) + indices.append(i2) + # Compute lifetime + pers.append(simplextree.filtration(s2)-simplextree.filtration(s1)) + + # Sort vertex pairs wrt lifetime + perm = np.argsort(pers) + indices = np.reshape(indices, [-1,2])[perm][::-1,:].flatten() + + return np.array(indices, dtype=np.int32) + +class LowerStarSimplexTreeLayer(tf.keras.layers.Layer): + """ + TensorFlow layer for computing lower-star persistence out of a simplex tree + + Attributes: + simplextree (gudhi.SimplexTree()): underlying simplex tree + dimension (int): homology dimension + """ + def __init__(self, simplextree, dimension=0, **kwargs): + super().__init__(dynamic=True, **kwargs) + self.dimension = dimension + self.simplextree = simplextree + + def build(self): + super.build() + + def call(self, filtration): + """ + Compute lower-star persistence diagram associated to a function defined on the vertices of the simplex tree + + Parameters: + F (TensorFlow variable): filter function values over the vertices of the simplex tree + """ + # Don't try to compute gradients for the vertex pairs + indices = tf.stop_gradient(_LowerStarSimplexTree(self.simplextree, filtration.numpy(), self.dimension)) + # Get persistence diagram + self.dgm = tf.reshape(tf.gather(filtration, indices), [-1,2]) + return self.dgm + diff --git a/src/python/gudhi/tensorflow/rips_layer.py b/src/python/gudhi/tensorflow/rips_layer.py new file mode 100644 index 00000000..373e021e --- /dev/null +++ b/src/python/gudhi/tensorflow/rips_layer.py @@ -0,0 +1,75 @@ +import numpy as np +import tensorflow as tf +from ..rips_complex import RipsComplex + +############################ +# Vietoris-Rips filtration # +############################ + +# The parameters of the model are the point coordinates. + +def _Rips(DX, max_edge, dimension): + # Parameters: DX (distance matrix), + # max_edge (maximum edge length for Rips filtration), + # dimension (homology dimension) + + # Compute the persistence pairs with Gudhi + rc = RipsComplex(distance_matrix=DX, max_edge_length=max_edge) + st = rc.create_simplex_tree(max_dimension=dimension+1) + dgm = st.persistence() + pairs = st.persistence_pairs() + + # Retrieve vertices v_a and v_b by picking the ones achieving the maximal + # distance among all pairwise distances between the simplex vertices + indices, pers = [], [] + for s1, s2 in pairs: + if len(s1) == dimension+1 and len(s2) > 0: + l1, l2 = np.array(s1), np.array(s2) + i1 = [l1[v] for v in np.unravel_index(np.argmax(DX[l1,:][:,l1]),[len(l1), len(l1)])] + i2 = [l2[v] for v in np.unravel_index(np.argmax(DX[l2,:][:,l2]),[len(l2), len(l2)])] + indices.append(i1) + indices.append(i2) + pers.append(st.filtration(s2)-st.filtration(s1)) + + # Sort points with distance-to-diagonal + perm = np.argsort(pers) + indices = np.reshape(indices, [-1,4])[perm][::-1,:].flatten() + + return np.array(indices, dtype=np.int32) + +class RipsLayer(tf.keras.layers.Layer): + """ + TensorFlow layer for computing Rips persistence out of a point cloud + + Attributes: + maximum_edge_length (float): maximum edge length for the Rips complex + dimension (int): homology dimension + """ + def __init__(self, maximum_edge_length=12, dimension=1, **kwargs): + super().__init__(dynamic=True, **kwargs) + self.max_edge = maximum_edge_length + self.dimension = dimension + + def build(self): + super.build() + + def call(self, X): + """ + Compute Rips persistence diagram associated to a point cloud + + Parameters: + X (TensorFlow variable): point cloud of shape [number of points, number of dimensions] + """ + # Compute distance matrix + DX = tf.math.sqrt(tf.reduce_sum((tf.expand_dims(X, 1)-tf.expand_dims(X, 0))**2, 2)) + # Compute vertices associated to positive and negative simplices + # Don't compute gradient for this operation + indices = tf.stop_gradient(_Rips(DX.numpy(), self.max_edge, self.dimension)) + # Get persistence diagram by simply picking the corresponding entries in the distance matrix + if self.dimension > 0: + dgm = tf.reshape(tf.gather_nd(DX, tf.reshape(indices, [-1,2])), [-1,2]) + else: + indices = tf.reshape(indices, [-1,2])[1::2,:] + dgm = tf.concat([tf.zeros([indices.shape[0],1]), tf.reshape(tf.gather_nd(DX, indices), [-1,1])], axis=1) + return dgm + -- cgit v1.2.3 From 7d3d9e57c7c72b0762db910c2638b08e596199df Mon Sep 17 00:00:00 2001 From: Hind-M Date: Tue, 19 Oct 2021 16:16:21 +0200 Subject: Make Cech benchmark work --- .../benchmark/cech_complex_benchmark.cpp | 24 ++++++++++++++-------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/benchmark/cech_complex_benchmark.cpp b/src/Cech_complex/benchmark/cech_complex_benchmark.cpp index cfeb0725..06d90757 100644 --- a/src/Cech_complex/benchmark/cech_complex_benchmark.cpp +++ b/src/Cech_complex/benchmark/cech_complex_benchmark.cpp @@ -9,7 +9,7 @@ */ #include -#include +#include #include #include #include @@ -33,7 +33,10 @@ using Points_off_reader = Gudhi::Points_off_reader; using Proximity_graph = Gudhi::Proximity_graph; using Rips_complex = Gudhi::rips_complex::Rips_complex; using Kernel = CGAL::Epeck_d; -using Cech_complex = Gudhi::cech_complex::Cech_complex; +using Point_cgal = typename Kernel::Point_d; +using Point_cloud_cgal = std::vector; +using Points_off_reader_cgal = Gudhi::Points_off_reader; +using Cech_complex = Gudhi::cech_complex::Cech_complex; class Minimal_enclosing_ball_radius { public: @@ -65,6 +68,7 @@ int main(int argc, char* argv[]) { // Extract the points from the file filepoints Points_off_reader off_reader(off_file_points); + Points_off_reader_cgal off_reader_cgal(off_file_points); Gudhi::Clock euclidean_clock("Gudhi::Euclidean_distance"); // Compute the proximity graph of the points @@ -79,16 +83,16 @@ int main(int argc, char* argv[]) { off_reader.get_point_cloud(), threshold, Minimal_enclosing_ball_radius()); std::clog << miniball_clock << std::endl; - Gudhi::Clock common_miniball_clock("Gudhi::Minimal_enclosing_ball_radius()"); + Gudhi::Clock cgal_miniball_clock("Gudhi::Minimal_enclosing_ball_radius_cgal()"); // Compute the proximity graph of the points - Proximity_graph common_miniball_prox_graph = Gudhi::compute_proximity_graph( - off_reader.get_point_cloud(), threshold, Gudhi::Minimal_enclosing_ball_radius()); - std::clog << common_miniball_clock << std::endl; + Proximity_graph cgal_miniball_prox_graph = Gudhi::compute_proximity_graph( + off_reader_cgal.get_point_cloud(), threshold, Gudhi::Minimal_enclosing_ball_radius()); + std::clog << cgal_miniball_clock << std::endl; boost::filesystem::path full_path(boost::filesystem::current_path()); std::clog << "Current path is : " << full_path << std::endl; - std::clog << "File name;Radius;Rips time;Cech time; Ratio Rips/Cech time;Rips nb simplices;Cech nb simplices;" + std::clog << "File name; Radius; Rips time; Cech time; Ratio Rips/Cech time; Rips nb simplices; Cech nb simplices;" << std::endl; boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end for (boost::filesystem::directory_iterator itr(boost::filesystem::current_path()); itr != end_itr; ++itr) { @@ -96,13 +100,15 @@ int main(int argc, char* argv[]) { if (itr->path().extension() == ".off") // see below { Points_off_reader off_reader(itr->path().string()); + Points_off_reader_cgal off_reader_cgal(itr->path().string()); + Point p0 = off_reader.get_point_cloud()[0]; for (Filtration_value radius = 0.1; radius < 0.4; radius += 0.1) { std::clog << itr->path().stem() << ";"; std::clog << radius << ";"; Gudhi::Clock rips_clock("Rips computation"); - Rips_complex rips_complex_from_points(off_reader.get_point_cloud(), radius, + Rips_complex rips_complex_from_points(off_reader_cgal.get_point_cloud(), radius, Gudhi::Minimal_enclosing_ball_radius()); Simplex_tree rips_stree; rips_complex_from_points.create_complex(rips_stree, p0.size() - 1); @@ -113,7 +119,7 @@ int main(int argc, char* argv[]) { std::clog << rips_sec << ";"; Gudhi::Clock cech_clock("Cech computation"); - Cech_complex cech_complex_from_points(off_reader.get_point_cloud(), radius); + Cech_complex cech_complex_from_points(off_reader_cgal.get_point_cloud(), radius); Simplex_tree cech_stree; cech_complex_from_points.create_complex(cech_stree, p0.size() - 1); // ------------------------------------------ -- cgit v1.2.3 From c4269eef025d4e6c7a763cd99b5dada647693c1d Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Mon, 1 Nov 2021 14:36:11 +0100 Subject: fix doc --- src/python/doc/cubical_complex_tflow_itf_ref.rst | 9 ++++++++- src/python/doc/ls_simplex_tree_tflow_itf_ref.rst | 11 +++++++++-- src/python/doc/rips_complex_tflow_itf_ref.rst | 8 +++++++- 3 files changed, 24 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/python/doc/cubical_complex_tflow_itf_ref.rst b/src/python/doc/cubical_complex_tflow_itf_ref.rst index e85cfdc6..a907dfce 100644 --- a/src/python/doc/cubical_complex_tflow_itf_ref.rst +++ b/src/python/doc/cubical_complex_tflow_itf_ref.rst @@ -10,7 +10,7 @@ TensorFlow layer for cubical persistence Example of gradient computed from cubical persistence ----------------------------------------------------- -.. code-block:: python +.. testcode:: from gudhi.tensorflow import * import numpy as np @@ -23,9 +23,16 @@ Example of gradient computed from cubical persistence with tf.GradientTape() as tape: dgm = cl.call(X) loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) + grads = tape.gradient(loss, [X]) print(grads[0].numpy()) +.. testoutput:: + + [[ 0. 0. 0. ] + [ 0. 0.5 0. ] + [ 0. 0. -0.5]] + Documentation for CubicalLayer ------------------------------ diff --git a/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst b/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst index 7baf611c..26cf1ff2 100644 --- a/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst +++ b/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst @@ -10,7 +10,7 @@ TensorFlow layer for lower-star persistence on simplex trees Example of gradient computed from lower-star filtration of a simplex tree ------------------------------------------------------------------------- -.. code-block:: python +.. testcode:: from gudhi.tensorflow import * import numpy as np @@ -47,8 +47,15 @@ Example of gradient computed from lower-star filtration of a simplex tree with tf.GradientTape() as tape: dgm = sl.call(F) loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) + grads = tape.gradient(loss, [F]) - print(grads[0].numpy()) + print(grads[0].indices.numpy()) + print(grads[0].values.numpy()) + +.. testoutput:: + + [2 4] + [-1. 1.] Documentation for LowerStarSimplexTreeLayer ------------------------------------------- diff --git a/src/python/doc/rips_complex_tflow_itf_ref.rst b/src/python/doc/rips_complex_tflow_itf_ref.rst index 15ba4c8e..7300eba0 100644 --- a/src/python/doc/rips_complex_tflow_itf_ref.rst +++ b/src/python/doc/rips_complex_tflow_itf_ref.rst @@ -10,7 +10,7 @@ TensorFlow layer for Vietoris-Rips persistence Example of gradient computed from Vietoris-Rips persistence ----------------------------------------------------------- -.. code-block:: python +.. testcode:: from gudhi.tensorflow import * import numpy as np @@ -23,9 +23,15 @@ Example of gradient computed from Vietoris-Rips persistence with tf.GradientTape() as tape: dgm = rl.call(X) loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) + grads = tape.gradient(loss, [X]) print(grads[0].numpy()) +.. testoutput:: + + [[-0.5 -0.5] + [ 0.5 0.5]] + Documentation for RipsLayer --------------------------- -- cgit v1.2.3 From 6b16678c71daa2b9b56cc8fa79a18cde080298cc Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Mon, 1 Nov 2021 15:38:41 +0100 Subject: fix installation doc --- src/python/doc/installation.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/python/doc/installation.rst b/src/python/doc/installation.rst index 35c344e3..25eb7a90 100644 --- a/src/python/doc/installation.rst +++ b/src/python/doc/installation.rst @@ -393,7 +393,11 @@ mathematics, science, and engineering. TensorFlow ---------- -`TensorFlow `_ is currently only used in some automatic differentiation tests. +The :doc:`cubical complex `, :doc:`simplex tree ` +and :doc:`Rips complex ` modules require `TensorFlow `_ +for incorporating them in neural nets. + +`TensorFlow `_ is also used in some automatic differentiation tests. Bug reports and contributions ***************************** -- cgit v1.2.3 From 734622d5a8816cfdaaed2aaa4b9b3212fb6a259c Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Fri, 5 Nov 2021 00:35:51 +0100 Subject: update doc and pieces of code --- src/python/doc/cubical_complex_tflow_itf_ref.rst | 4 +-- src/python/doc/ls_simplex_tree_tflow_itf_ref.rst | 4 +-- src/python/doc/rips_complex_tflow_itf_ref.rst | 4 +-- src/python/gudhi/tensorflow/cubical_layer.py | 19 +++++------ .../tensorflow/lower_star_simplex_tree_layer.py | 25 +++++++------- src/python/gudhi/tensorflow/rips_layer.py | 39 ++++++++++------------ 6 files changed, 42 insertions(+), 53 deletions(-) (limited to 'src') diff --git a/src/python/doc/cubical_complex_tflow_itf_ref.rst b/src/python/doc/cubical_complex_tflow_itf_ref.rst index a907dfce..582e0551 100644 --- a/src/python/doc/cubical_complex_tflow_itf_ref.rst +++ b/src/python/doc/cubical_complex_tflow_itf_ref.rst @@ -13,11 +13,9 @@ Example of gradient computed from cubical persistence .. testcode:: from gudhi.tensorflow import * - import numpy as np import tensorflow as tf - Xinit = np.array([[0.,2.,2.],[2.,2.,2.],[2.,2.,1.]], dtype=np.float32) - X = tf.Variable(initial_value=Xinit, trainable=True) + X = tf.Variable([[0.,2.,2.],[2.,2.,2.],[2.,2.,1.]], dtype=tf.float32, trainable=True) cl = CubicalLayer(dimension=0) with tf.GradientTape() as tape: diff --git a/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst b/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst index 26cf1ff2..6c8b5a08 100644 --- a/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst +++ b/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst @@ -13,7 +13,6 @@ Example of gradient computed from lower-star filtration of a simplex tree .. testcode:: from gudhi.tensorflow import * - import numpy as np import tensorflow as tf import gudhi as gd @@ -40,8 +39,7 @@ Example of gradient computed from lower-star filtration of a simplex tree st.insert([8, 9]) st.insert([9, 10]) - Finit = np.array([6.,4.,3.,4.,5.,4.,3.,2.,3.,4.,5.], dtype=np.float32) - F = tf.Variable(initial_value=Finit, trainable=True) + F = tf.Variable([6.,4.,3.,4.,5.,4.,3.,2.,3.,4.,5.], dtype=tf.float32, trainable=True) sl = LowerStarSimplexTreeLayer(simplextree=st, dimension=0) with tf.GradientTape() as tape: diff --git a/src/python/doc/rips_complex_tflow_itf_ref.rst b/src/python/doc/rips_complex_tflow_itf_ref.rst index 7300eba0..bd9c5da5 100644 --- a/src/python/doc/rips_complex_tflow_itf_ref.rst +++ b/src/python/doc/rips_complex_tflow_itf_ref.rst @@ -13,11 +13,9 @@ Example of gradient computed from Vietoris-Rips persistence .. testcode:: from gudhi.tensorflow import * - import numpy as np import tensorflow as tf - Xinit = np.array([[1.,1.],[2.,2.]], dtype=np.float32) - X = tf.Variable(initial_value=Xinit, trainable=True) + X = tf.Variable([[1.,1.],[2.,2.]], dtype=tf.float32, trainable=True) rl = RipsLayer(maximum_edge_length=2., dimension=0) with tf.GradientTape() as tape: diff --git a/src/python/gudhi/tensorflow/cubical_layer.py b/src/python/gudhi/tensorflow/cubical_layer.py index e36adec5..b4ff2598 100644 --- a/src/python/gudhi/tensorflow/cubical_layer.py +++ b/src/python/gudhi/tensorflow/cubical_layer.py @@ -20,13 +20,6 @@ def _Cubical(X, dimension): except IndexError: cof = np.array([]) - if len(cof) > 0: - # Sort points with distance-to-diagonal - Xs = X.shape - pers = [X[np.unravel_index(cof[idx,1], Xs)] - X[np.unravel_index(cof[idx,0], Xs)] for idx in range(len(cof))] - perm = np.argsort(pers) - cof = cof[perm[::-1]] - # Retrieve and ouput image indices/pixels corresponding to positive and negative simplices D = len(Xs) if len(cof) > 0 else 1 ocof = np.array([0 for _ in range(D*2*cof.shape[0])]) @@ -40,11 +33,14 @@ def _Cubical(X, dimension): class CubicalLayer(tf.keras.layers.Layer): """ TensorFlow layer for computing cubical persistence out of a cubical complex - - Attributes: - dimension (int): homology dimension """ def __init__(self, dimension=1, **kwargs): + """ + Constructor for the CubicalLayer class + + Parameters: + dimension (int): homology dimension + """ super().__init__(dynamic=True, **kwargs) self.dimension = dimension @@ -57,6 +53,9 @@ class CubicalLayer(tf.keras.layers.Layer): Parameters: X (TensorFlow variable): pixel values of the cubical complex + + Returns: + dgm (TensorFlow variable): cubical persistence diagram with shape [num_points, 2] """ # Compute pixels associated to positive and negative simplices # Don't compute gradient for this operation diff --git a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py index fc963d2f..4f515386 100644 --- a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py +++ b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py @@ -21,7 +21,7 @@ def _LowerStarSimplexTree(simplextree, filtration, dimension): simplextree.make_filtration_non_decreasing() # Compute persistence diagram - dgm = simplextree.persistence() + dgm = simplextree.compute_persistence() # Get vertex pairs for optimization. First, get all simplex pairs pairs = simplextree.persistence_pairs() @@ -37,24 +37,22 @@ def _LowerStarSimplexTree(simplextree, filtration, dimension): i2 = l2[np.argmax(filtration[l2])] indices.append(i1) indices.append(i2) - # Compute lifetime - pers.append(simplextree.filtration(s2)-simplextree.filtration(s1)) - - # Sort vertex pairs wrt lifetime - perm = np.argsort(pers) - indices = np.reshape(indices, [-1,2])[perm][::-1,:].flatten() + indices = np.reshape(indices, [-1,2]).flatten() return np.array(indices, dtype=np.int32) class LowerStarSimplexTreeLayer(tf.keras.layers.Layer): """ TensorFlow layer for computing lower-star persistence out of a simplex tree - - Attributes: - simplextree (gudhi.SimplexTree()): underlying simplex tree - dimension (int): homology dimension """ def __init__(self, simplextree, dimension=0, **kwargs): + """ + Constructor for the LowerStarSimplexTreeLayer class + + Parameters: + simplextree (gudhi.SimplexTree): underlying simplex tree. Its vertices MUST be named with integers from 0 to n = number of vertices + dimension (int): homology dimension + """ super().__init__(dynamic=True, **kwargs) self.dimension = dimension self.simplextree = simplextree @@ -67,7 +65,10 @@ class LowerStarSimplexTreeLayer(tf.keras.layers.Layer): Compute lower-star persistence diagram associated to a function defined on the vertices of the simplex tree Parameters: - F (TensorFlow variable): filter function values over the vertices of the simplex tree + F (TensorFlow variable): filter function values over the vertices of the simplex tree. The ith entry of F corresponds to vertex i in self.simplextree + + Returns: + dgm (TensorFlow variable): lower-star persistence diagram with shape [num_points, 2] """ # Don't try to compute gradients for the vertex pairs indices = tf.stop_gradient(_LowerStarSimplexTree(self.simplextree, filtration.numpy(), self.dimension)) diff --git a/src/python/gudhi/tensorflow/rips_layer.py b/src/python/gudhi/tensorflow/rips_layer.py index 373e021e..6d54871c 100644 --- a/src/python/gudhi/tensorflow/rips_layer.py +++ b/src/python/gudhi/tensorflow/rips_layer.py @@ -17,35 +17,26 @@ def _Rips(DX, max_edge, dimension): rc = RipsComplex(distance_matrix=DX, max_edge_length=max_edge) st = rc.create_simplex_tree(max_dimension=dimension+1) dgm = st.persistence() - pairs = st.persistence_pairs() - - # Retrieve vertices v_a and v_b by picking the ones achieving the maximal - # distance among all pairwise distances between the simplex vertices - indices, pers = [], [] - for s1, s2 in pairs: - if len(s1) == dimension+1 and len(s2) > 0: - l1, l2 = np.array(s1), np.array(s2) - i1 = [l1[v] for v in np.unravel_index(np.argmax(DX[l1,:][:,l1]),[len(l1), len(l1)])] - i2 = [l2[v] for v in np.unravel_index(np.argmax(DX[l2,:][:,l2]),[len(l2), len(l2)])] - indices.append(i1) - indices.append(i2) - pers.append(st.filtration(s2)-st.filtration(s1)) - - # Sort points with distance-to-diagonal - perm = np.argsort(pers) - indices = np.reshape(indices, [-1,4])[perm][::-1,:].flatten() + if dimension == 0: + pairs = st.flag_persistence_generators()[0] + else: + pairs = st.flag_persistence_generators()[1][dimension-1] + indices = pairs.flatten() return np.array(indices, dtype=np.int32) class RipsLayer(tf.keras.layers.Layer): """ TensorFlow layer for computing Rips persistence out of a point cloud - - Attributes: - maximum_edge_length (float): maximum edge length for the Rips complex - dimension (int): homology dimension """ def __init__(self, maximum_edge_length=12, dimension=1, **kwargs): + """ + Constructor for the RipsLayer class + + Parameters: + maximum_edge_length (float): maximum edge length for the Rips complex + dimension (int): homology dimension + """ super().__init__(dynamic=True, **kwargs) self.max_edge = maximum_edge_length self.dimension = dimension @@ -59,6 +50,9 @@ class RipsLayer(tf.keras.layers.Layer): Parameters: X (TensorFlow variable): point cloud of shape [number of points, number of dimensions] + + Returns: + dgm (TensorFlow variable): Rips persistence diagram with shape [num_points, 2] with points sorted by """ # Compute distance matrix DX = tf.math.sqrt(tf.reduce_sum((tf.expand_dims(X, 1)-tf.expand_dims(X, 0))**2, 2)) @@ -69,7 +63,8 @@ class RipsLayer(tf.keras.layers.Layer): if self.dimension > 0: dgm = tf.reshape(tf.gather_nd(DX, tf.reshape(indices, [-1,2])), [-1,2]) else: - indices = tf.reshape(indices, [-1,2])[1::2,:] + #indices = tf.reshape(indices, [-1,2])[1::2,:] + indices = indices[:,1:] dgm = tf.concat([tf.zeros([indices.shape[0],1]), tf.reshape(tf.gather_nd(DX, indices), [-1,1])], axis=1) return dgm -- cgit v1.2.3 From bd7134d71628958e4e281817f746b0ad7ad83d00 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Fri, 5 Nov 2021 19:21:54 +0100 Subject: modified API for multiple dimensions and finite + essential --- src/python/gudhi/tensorflow/cubical_layer.py | 53 ++++++++++-------- .../tensorflow/lower_star_simplex_tree_layer.py | 52 +++++++++--------- src/python/gudhi/tensorflow/rips_layer.py | 62 ++++++++++++++-------- src/python/test/test_diff.py | 12 ++--- 4 files changed, 102 insertions(+), 77 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/tensorflow/cubical_layer.py b/src/python/gudhi/tensorflow/cubical_layer.py index b4ff2598..d8177864 100644 --- a/src/python/gudhi/tensorflow/cubical_layer.py +++ b/src/python/gudhi/tensorflow/cubical_layer.py @@ -8,41 +8,48 @@ from ..cubical_complex import CubicalComplex # The parameters of the model are the pixel values. -def _Cubical(X, dimension): +def _Cubical(X, dimensions): # Parameters: X (image), - # dimension (homology dimension) + # dimensions (homology dimensions) # Compute the persistence pairs with Gudhi - cc = CubicalComplex(dimensions=X.shape, top_dimensional_cells=X.flatten()) + Xs = X.shape + cc = CubicalComplex(dimensions=Xs, top_dimensional_cells=X.flatten()) cc.persistence() - try: - cof = cc.cofaces_of_persistence_pairs()[0][dimension] - except IndexError: - cof = np.array([]) - # Retrieve and ouput image indices/pixels corresponding to positive and negative simplices - D = len(Xs) if len(cof) > 0 else 1 - ocof = np.array([0 for _ in range(D*2*cof.shape[0])]) - count = 0 - for idx in range(0,2*cof.shape[0],2): - ocof[D*idx:D*(idx+1)] = np.unravel_index(cof[count,0], Xs) - ocof[D*(idx+1):D*(idx+2)] = np.unravel_index(cof[count,1], Xs) - count += 1 - return np.array(ocof, dtype=np.int32) + L_cofs = [] + for dim in dimensions: + + try: + cof = cc.cofaces_of_persistence_pairs()[0][dim] + except IndexError: + cof = np.array([]) + + # Retrieve and ouput image indices/pixels corresponding to positive and negative simplices + D = len(Xs) if len(cof) > 0 else 1 + ocof = np.array([0 for _ in range(D*2*cof.shape[0])]) + count = 0 + for idx in range(0,2*cof.shape[0],2): + ocof[D*idx:D*(idx+1)] = np.unravel_index(cof[count,0], Xs) + ocof[D*(idx+1):D*(idx+2)] = np.unravel_index(cof[count,1], Xs) + count += 1 + L_cofs.append(np.array(ocof, dtype=np.int32)) + + return L_cofs class CubicalLayer(tf.keras.layers.Layer): """ TensorFlow layer for computing cubical persistence out of a cubical complex """ - def __init__(self, dimension=1, **kwargs): + def __init__(self, dimensions=[1], **kwargs): """ Constructor for the CubicalLayer class Parameters: - dimension (int): homology dimension + dimensions (list of int): homology dimensions """ super().__init__(dynamic=True, **kwargs) - self.dimension = dimension + self.dimensions = dimensions def build(self): super.build() @@ -55,11 +62,11 @@ class CubicalLayer(tf.keras.layers.Layer): X (TensorFlow variable): pixel values of the cubical complex Returns: - dgm (TensorFlow variable): cubical persistence diagram with shape [num_points, 2] + dgms (list of TensorFlow variables): list of cubical persistence diagrams of length self.dimensions, where each element contains a finite persistence diagram of shape [num_finite_points, 2] """ # Compute pixels associated to positive and negative simplices # Don't compute gradient for this operation - indices = tf.stop_gradient(_Cubical(X.numpy(), self.dimension)) + indices = _Cubical(X.numpy(), self.dimensions) # Get persistence diagram by simply picking the corresponding entries in the image - dgm = tf.reshape(tf.gather_nd(X, tf.reshape(indices, [-1,len(X.shape)])), [-1,2]) - return dgm + self.dgms = [tf.reshape(tf.gather_nd(X, tf.reshape(indice, [-1,len(X.shape)])), [-1,2]) for indice in indices] + return self.dgms diff --git a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py index 4f515386..c509c456 100644 --- a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py +++ b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py @@ -7,10 +7,10 @@ import tensorflow as tf # The parameters of the model are the vertex function values of the simplex tree. -def _LowerStarSimplexTree(simplextree, filtration, dimension): +def _LowerStarSimplexTree(simplextree, filtration, dimensions): # Parameters: simplextree (simplex tree on which to compute persistence) # filtration (function values on the vertices of st), - # dimension (homology dimension), + # dimensions (homology dimensions), for s,_ in simplextree.get_filtration(): simplextree.assign_filtration(s, -1e10) @@ -21,40 +21,38 @@ def _LowerStarSimplexTree(simplextree, filtration, dimension): simplextree.make_filtration_non_decreasing() # Compute persistence diagram - dgm = simplextree.compute_persistence() + simplextree.compute_persistence() # Get vertex pairs for optimization. First, get all simplex pairs - pairs = simplextree.persistence_pairs() + pairs = simplextree.lower_star_persistence_generators() - # Then, loop over all simplex pairs - indices, pers = [], [] - for s1, s2 in pairs: - # Select pairs with good homological dimension and finite lifetime - if len(s1) == dimension+1 and len(s2) > 0: - # Get IDs of the vertices corresponding to the filtration values of the simplices - l1, l2 = np.array(s1), np.array(s2) - i1 = l1[np.argmax(filtration[l1])] - i2 = l2[np.argmax(filtration[l2])] - indices.append(i1) - indices.append(i2) + L_indices = [] + for dimension in dimensions: - indices = np.reshape(indices, [-1,2]).flatten() - return np.array(indices, dtype=np.int32) + finite_pairs = pairs[0][dimension] if len(pairs[0]) >= dimension+1 else np.empty(shape=[0,2]) + essential_pairs = pairs[1][dimension] if len(pairs[1]) >= dimension+1 else np.empty(shape=[0,1]) + + finite_indices = np.array(finite_pairs.flatten(), dtype=np.int32) + essential_indices = np.array(essential_pairs.flatten(), dtype=np.int32) + + L_indices.append((finite_indices, essential_indices)) + + return L_indices class LowerStarSimplexTreeLayer(tf.keras.layers.Layer): """ TensorFlow layer for computing lower-star persistence out of a simplex tree """ - def __init__(self, simplextree, dimension=0, **kwargs): + def __init__(self, simplextree, dimensions=[0], **kwargs): """ Constructor for the LowerStarSimplexTreeLayer class Parameters: simplextree (gudhi.SimplexTree): underlying simplex tree. Its vertices MUST be named with integers from 0 to n = number of vertices - dimension (int): homology dimension + dimensions (int): homology dimensions """ super().__init__(dynamic=True, **kwargs) - self.dimension = dimension + self.dimensions = dimensions self.simplextree = simplextree def build(self): @@ -68,11 +66,15 @@ class LowerStarSimplexTreeLayer(tf.keras.layers.Layer): F (TensorFlow variable): filter function values over the vertices of the simplex tree. The ith entry of F corresponds to vertex i in self.simplextree Returns: - dgm (TensorFlow variable): lower-star persistence diagram with shape [num_points, 2] + dgms (list of tuple of TensorFlow variables): list of lower-star persistence diagrams of length self.dimensions, where each element of the list is a tuple that contains the finite and essential persistence diagrams of shapes [num_finite_points, 2] and [num_essential_points, 1] respectively """ # Don't try to compute gradients for the vertex pairs - indices = tf.stop_gradient(_LowerStarSimplexTree(self.simplextree, filtration.numpy(), self.dimension)) - # Get persistence diagram - self.dgm = tf.reshape(tf.gather(filtration, indices), [-1,2]) - return self.dgm + indices = _LowerStarSimplexTree(self.simplextree, filtration.numpy(), self.dimensions) + # Get persistence diagrams + self.dgms = [] + for idx_dim, dimension in enumerate(self.dimensions): + finite_dgm = tf.reshape(tf.gather(filtration, indices[idx_dim][0]), [-1,2]) + essential_dgm = tf.reshape(tf.gather(filtration, indices[idx_dim][1]), [-1,1]) + self.dgms.append((finite_dgm, essential_dgm)) + return self.dgms diff --git a/src/python/gudhi/tensorflow/rips_layer.py b/src/python/gudhi/tensorflow/rips_layer.py index 6d54871c..83387d21 100644 --- a/src/python/gudhi/tensorflow/rips_layer.py +++ b/src/python/gudhi/tensorflow/rips_layer.py @@ -8,38 +8,49 @@ from ..rips_complex import RipsComplex # The parameters of the model are the point coordinates. -def _Rips(DX, max_edge, dimension): +def _Rips(DX, max_edge, dimensions): # Parameters: DX (distance matrix), # max_edge (maximum edge length for Rips filtration), - # dimension (homology dimension) + # dimensions (homology dimensions) # Compute the persistence pairs with Gudhi rc = RipsComplex(distance_matrix=DX, max_edge_length=max_edge) - st = rc.create_simplex_tree(max_dimension=dimension+1) - dgm = st.persistence() - if dimension == 0: - pairs = st.flag_persistence_generators()[0] - else: - pairs = st.flag_persistence_generators()[1][dimension-1] + st = rc.create_simplex_tree(max_dimension=max(dimensions)+1) + st.persistence() + pairs = st.flag_persistence_generators() - indices = pairs.flatten() - return np.array(indices, dtype=np.int32) + L_indices = [] + for dimension in dimensions: + + if dimension == 0: + finite_pairs = pairs[0] + essential_pairs = pairs[2] + else: + finite_pairs = pairs[1][dimension-1] if len(pairs[1]) >= dimension else np.empty(shape=[0,4]) + essential_pairs = pairs[3][dimension-1] if len(pairs[3]) >= dimension else np.empty(shape=[0,2]) + + finite_indices = np.array(finite_pairs.flatten(), dtype=np.int32) + essential_indices = np.array(essential_pairs.flatten(), dtype=np.int32) + + L_indices.append((finite_indices, essential_indices)) + + return L_indices class RipsLayer(tf.keras.layers.Layer): """ TensorFlow layer for computing Rips persistence out of a point cloud """ - def __init__(self, maximum_edge_length=12, dimension=1, **kwargs): + def __init__(self, maximum_edge_length=12, dimensions=[1], **kwargs): """ Constructor for the RipsLayer class Parameters: maximum_edge_length (float): maximum edge length for the Rips complex - dimension (int): homology dimension + dimensions (int): homology dimensions """ super().__init__(dynamic=True, **kwargs) self.max_edge = maximum_edge_length - self.dimension = dimension + self.dimensions = dimensions def build(self): super.build() @@ -52,19 +63,24 @@ class RipsLayer(tf.keras.layers.Layer): X (TensorFlow variable): point cloud of shape [number of points, number of dimensions] Returns: - dgm (TensorFlow variable): Rips persistence diagram with shape [num_points, 2] with points sorted by + dgms (list of tuple of TensorFlow variables): list of Rips persistence diagrams of length self.dimensions, where each element of the list is a tuple that contains the finite and essential persistence diagrams of shapes [num_finite_points, 2] and [num_essential_points, 1] respectively """ # Compute distance matrix DX = tf.math.sqrt(tf.reduce_sum((tf.expand_dims(X, 1)-tf.expand_dims(X, 0))**2, 2)) # Compute vertices associated to positive and negative simplices # Don't compute gradient for this operation - indices = tf.stop_gradient(_Rips(DX.numpy(), self.max_edge, self.dimension)) - # Get persistence diagram by simply picking the corresponding entries in the distance matrix - if self.dimension > 0: - dgm = tf.reshape(tf.gather_nd(DX, tf.reshape(indices, [-1,2])), [-1,2]) - else: - #indices = tf.reshape(indices, [-1,2])[1::2,:] - indices = indices[:,1:] - dgm = tf.concat([tf.zeros([indices.shape[0],1]), tf.reshape(tf.gather_nd(DX, indices), [-1,1])], axis=1) - return dgm + indices = _Rips(DX.numpy(), self.max_edge, self.dimensions) + # Get persistence diagrams by simply picking the corresponding entries in the distance matrix + self.dgms = [] + for idx_dim, dimension in enumerate(self.dimensions): + cur_idx = indices[idx_dim] + if dimension > 0: + finite_dgm = tf.reshape(tf.gather_nd(DX, tf.reshape(cur_idx[0], [-1,2])), [-1,2]) + essential_dgm = tf.reshape(tf.gather_nd(DX, tf.reshape(cur_idx[1], [-1,2])), [-1,1]) + else: + reshaped_cur_idx = tf.reshape(cur_idx[0], [-1,3]) + finite_dgm = tf.concat([tf.zeros([reshaped_cur_idx.shape[0],1]), tf.reshape(tf.gather_nd(DX, reshaped_cur_idx[:,1:]), [-1,1])], axis=1) + essential_dgm = tf.zeros([cur_idx[1].shape[0],1]) + self.dgms.append((finite_dgm, essential_dgm)) + return self.dgms diff --git a/src/python/test/test_diff.py b/src/python/test/test_diff.py index 73a03697..f49eff7b 100644 --- a/src/python/test/test_diff.py +++ b/src/python/test/test_diff.py @@ -7,10 +7,10 @@ def test_rips_diff(): Xinit = np.array([[1.,1.],[2.,2.]], dtype=np.float32) X = tf.Variable(initial_value=Xinit, trainable=True) - rl = RipsLayer(maximum_edge_length=2., dimension=0) + rl = RipsLayer(maximum_edge_length=2., dimensions=[0]) with tf.GradientTape() as tape: - dgm = rl.call(X) + dgm = rl.call(X)[0][0] loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) grads = tape.gradient(loss, [X]) assert np.abs(grads[0].numpy()-np.array([[-.5,-.5],[.5,.5]])).sum() <= 1e-6 @@ -20,10 +20,10 @@ def test_cubical_diff(): Xinit = np.array([[0.,2.,2.],[2.,2.,2.],[2.,2.,1.]], dtype=np.float32) X = tf.Variable(initial_value=Xinit, trainable=True) - cl = CubicalLayer(dimension=0) + cl = CubicalLayer(dimensions=[0]) with tf.GradientTape() as tape: - dgm = cl.call(X) + dgm = cl.call(X)[0] loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) grads = tape.gradient(loss, [X]) assert np.abs(grads[0].numpy()-np.array([[0.,0.,0.],[0.,.5,0.],[0.,0.,-.5]])).sum() <= 1e-6 @@ -55,10 +55,10 @@ def test_st_diff(): Finit = np.array([6.,4.,3.,4.,5.,4.,3.,2.,3.,4.,5.], dtype=np.float32) F = tf.Variable(initial_value=Finit, trainable=True) - sl = LowerStarSimplexTreeLayer(simplextree=st, dimension=0) + sl = LowerStarSimplexTreeLayer(simplextree=st, dimensions=[0]) with tf.GradientTape() as tape: - dgm = sl.call(F) + dgm = sl.call(F)[0][0] loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) grads = tape.gradient(loss, [F]) -- cgit v1.2.3 From dacc47d8aa5e96700600cd93532363d5dfa6cd8a Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Sun, 7 Nov 2021 20:54:24 +0100 Subject: fix cubical code --- src/python/gudhi/tensorflow/cubical_layer.py | 16 +++++++++------- src/python/gudhi/tensorflow/rips_layer.py | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/tensorflow/cubical_layer.py b/src/python/gudhi/tensorflow/cubical_layer.py index d8177864..55bd2685 100644 --- a/src/python/gudhi/tensorflow/cubical_layer.py +++ b/src/python/gudhi/tensorflow/cubical_layer.py @@ -14,24 +14,26 @@ def _Cubical(X, dimensions): # Compute the persistence pairs with Gudhi Xs = X.shape - cc = CubicalComplex(dimensions=Xs, top_dimensional_cells=X.flatten()) - cc.persistence() + cc = CubicalComplex(top_dimensional_cells=X) + cc.compute_persistence() + cof_pp = cc.cofaces_of_persistence_pairs() + L_cofs = [] for dim in dimensions: try: - cof = cc.cofaces_of_persistence_pairs()[0][dim] + cof = cof_pp[0][dim] except IndexError: cof = np.array([]) # Retrieve and ouput image indices/pixels corresponding to positive and negative simplices D = len(Xs) if len(cof) > 0 else 1 - ocof = np.array([0 for _ in range(D*2*cof.shape[0])]) + ocof = np.zeros(D*2*cof.shape[0]) count = 0 for idx in range(0,2*cof.shape[0],2): - ocof[D*idx:D*(idx+1)] = np.unravel_index(cof[count,0], Xs) - ocof[D*(idx+1):D*(idx+2)] = np.unravel_index(cof[count,1], Xs) + ocof[D*idx:D*(idx+1)] = np.unravel_index(cof[count,0], Xs, order='F') + ocof[D*(idx+1):D*(idx+2)] = np.unravel_index(cof[count,1], Xs, order='F') count += 1 L_cofs.append(np.array(ocof, dtype=np.int32)) @@ -41,7 +43,7 @@ class CubicalLayer(tf.keras.layers.Layer): """ TensorFlow layer for computing cubical persistence out of a cubical complex """ - def __init__(self, dimensions=[1], **kwargs): + def __init__(self, dimensions=[0], **kwargs): """ Constructor for the CubicalLayer class diff --git a/src/python/gudhi/tensorflow/rips_layer.py b/src/python/gudhi/tensorflow/rips_layer.py index 83387d21..7735db67 100644 --- a/src/python/gudhi/tensorflow/rips_layer.py +++ b/src/python/gudhi/tensorflow/rips_layer.py @@ -40,7 +40,7 @@ class RipsLayer(tf.keras.layers.Layer): """ TensorFlow layer for computing Rips persistence out of a point cloud """ - def __init__(self, maximum_edge_length=12, dimensions=[1], **kwargs): + def __init__(self, maximum_edge_length=12, dimensions=[0], **kwargs): """ Constructor for the RipsLayer class -- cgit v1.2.3 From 6ae793a8cad4503d1795e227d40d85d43954d1dd Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Fri, 12 Nov 2021 09:46:22 +0100 Subject: removed unraveling in cubical --- src/python/gudhi/tensorflow/cubical_layer.py | 15 ++++----------- .../gudhi/tensorflow/lower_star_simplex_tree_layer.py | 2 +- src/python/gudhi/tensorflow/rips_layer.py | 4 ++-- src/python/test/test_diff.py | 13 ++++++++++++- 4 files changed, 19 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/tensorflow/cubical_layer.py b/src/python/gudhi/tensorflow/cubical_layer.py index 55bd2685..70528f98 100644 --- a/src/python/gudhi/tensorflow/cubical_layer.py +++ b/src/python/gudhi/tensorflow/cubical_layer.py @@ -17,6 +17,7 @@ def _Cubical(X, dimensions): cc = CubicalComplex(top_dimensional_cells=X) cc.compute_persistence() + # Retrieve and ouput image indices/pixels corresponding to positive and negative simplices cof_pp = cc.cofaces_of_persistence_pairs() L_cofs = [] @@ -27,15 +28,7 @@ def _Cubical(X, dimensions): except IndexError: cof = np.array([]) - # Retrieve and ouput image indices/pixels corresponding to positive and negative simplices - D = len(Xs) if len(cof) > 0 else 1 - ocof = np.zeros(D*2*cof.shape[0]) - count = 0 - for idx in range(0,2*cof.shape[0],2): - ocof[D*idx:D*(idx+1)] = np.unravel_index(cof[count,0], Xs, order='F') - ocof[D*(idx+1):D*(idx+2)] = np.unravel_index(cof[count,1], Xs, order='F') - count += 1 - L_cofs.append(np.array(ocof, dtype=np.int32)) + L_cofs.append(np.array(cof, dtype=np.int32)) return L_cofs @@ -43,7 +36,7 @@ class CubicalLayer(tf.keras.layers.Layer): """ TensorFlow layer for computing cubical persistence out of a cubical complex """ - def __init__(self, dimensions=[0], **kwargs): + def __init__(self, dimensions, **kwargs): """ Constructor for the CubicalLayer class @@ -70,5 +63,5 @@ class CubicalLayer(tf.keras.layers.Layer): # Don't compute gradient for this operation indices = _Cubical(X.numpy(), self.dimensions) # Get persistence diagram by simply picking the corresponding entries in the image - self.dgms = [tf.reshape(tf.gather_nd(X, tf.reshape(indice, [-1,len(X.shape)])), [-1,2]) for indice in indices] + self.dgms = [tf.reshape(tf.gather( tf.reshape(tf.transpose(X), [-1]), indice ), [-1,2]) for indice in indices] return self.dgms diff --git a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py index c509c456..fd1698ea 100644 --- a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py +++ b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py @@ -43,7 +43,7 @@ class LowerStarSimplexTreeLayer(tf.keras.layers.Layer): """ TensorFlow layer for computing lower-star persistence out of a simplex tree """ - def __init__(self, simplextree, dimensions=[0], **kwargs): + def __init__(self, simplextree, dimensions, **kwargs): """ Constructor for the LowerStarSimplexTreeLayer class diff --git a/src/python/gudhi/tensorflow/rips_layer.py b/src/python/gudhi/tensorflow/rips_layer.py index 7735db67..a314229b 100644 --- a/src/python/gudhi/tensorflow/rips_layer.py +++ b/src/python/gudhi/tensorflow/rips_layer.py @@ -16,7 +16,7 @@ def _Rips(DX, max_edge, dimensions): # Compute the persistence pairs with Gudhi rc = RipsComplex(distance_matrix=DX, max_edge_length=max_edge) st = rc.create_simplex_tree(max_dimension=max(dimensions)+1) - st.persistence() + st.compute_persistence() pairs = st.flag_persistence_generators() L_indices = [] @@ -40,7 +40,7 @@ class RipsLayer(tf.keras.layers.Layer): """ TensorFlow layer for computing Rips persistence out of a point cloud """ - def __init__(self, maximum_edge_length=12, dimensions=[0], **kwargs): + def __init__(self, maximum_edge_length=12, dimensions, **kwargs): """ Constructor for the RipsLayer class diff --git a/src/python/test/test_diff.py b/src/python/test/test_diff.py index f49eff7b..e0c99d07 100644 --- a/src/python/test/test_diff.py +++ b/src/python/test/test_diff.py @@ -15,7 +15,6 @@ def test_rips_diff(): grads = tape.gradient(loss, [X]) assert np.abs(grads[0].numpy()-np.array([[-.5,-.5],[.5,.5]])).sum() <= 1e-6 - def test_cubical_diff(): Xinit = np.array([[0.,2.,2.],[2.,2.,2.],[2.,2.,1.]], dtype=np.float32) @@ -28,6 +27,18 @@ def test_cubical_diff(): grads = tape.gradient(loss, [X]) assert np.abs(grads[0].numpy()-np.array([[0.,0.,0.],[0.,.5,0.],[0.,0.,-.5]])).sum() <= 1e-6 +def test_nonsquare_cubical_diff(): + + Xinit = np.array([[-1.,1.,0.],[1.,1.,1.]], dtype=np.float32) + X = tf.Variable(initial_value=Xinit, trainable=True) + cl = CubicalLayer(dimensions=[0]) + + with tf.GradientTape() as tape: + dgm = cl.call(X)[0] + loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) + grads = tape.gradient(loss, [X]) + assert np.abs(grads[0].numpy()-np.array([[0.,0.5,-0.5],[0.,0.,0.]])).sum() <= 1e-6 + def test_st_diff(): st = gd.SimplexTree() -- cgit v1.2.3 From 74dfd101312a48272f2f91c3ddc401d1148deaec Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Fri, 12 Nov 2021 12:13:49 +0100 Subject: fix non-default vs default --- src/python/gudhi/tensorflow/rips_layer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/python/gudhi/tensorflow/rips_layer.py b/src/python/gudhi/tensorflow/rips_layer.py index a314229b..da7087f6 100644 --- a/src/python/gudhi/tensorflow/rips_layer.py +++ b/src/python/gudhi/tensorflow/rips_layer.py @@ -40,7 +40,7 @@ class RipsLayer(tf.keras.layers.Layer): """ TensorFlow layer for computing Rips persistence out of a point cloud """ - def __init__(self, maximum_edge_length=12, dimensions, **kwargs): + def __init__(self, dimensions, maximum_edge_length=12, **kwargs): """ Constructor for the RipsLayer class -- cgit v1.2.3 From 1fd37bf29d665330f1eb242139bc0faf10a542c1 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Fri, 12 Nov 2021 12:34:43 +0100 Subject: avoid transpose --- src/python/gudhi/tensorflow/cubical_layer.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/tensorflow/cubical_layer.py b/src/python/gudhi/tensorflow/cubical_layer.py index 70528f98..0971a446 100644 --- a/src/python/gudhi/tensorflow/cubical_layer.py +++ b/src/python/gudhi/tensorflow/cubical_layer.py @@ -8,13 +8,14 @@ from ..cubical_complex import CubicalComplex # The parameters of the model are the pixel values. -def _Cubical(X, dimensions): - # Parameters: X (image), +def _Cubical(Xflat, Xdim, dimensions): + # Parameters: Xflat (flattened image), + # Xdim (shape of non-flattened image) # dimensions (homology dimensions) # Compute the persistence pairs with Gudhi - Xs = X.shape - cc = CubicalComplex(top_dimensional_cells=X) + # We reverse the dimensions because CubicalComplex uses Fortran ordering + cc = CubicalComplex(dimensions=Xdim[::-1], top_dimensional_cells=Xflat) cc.compute_persistence() # Retrieve and ouput image indices/pixels corresponding to positive and negative simplices @@ -61,7 +62,9 @@ class CubicalLayer(tf.keras.layers.Layer): """ # Compute pixels associated to positive and negative simplices # Don't compute gradient for this operation - indices = _Cubical(X.numpy(), self.dimensions) + Xflat = tf.reshape(X, [-1]) + Xdim = X.shape + indices = _Cubical(Xflat.numpy(), Xdim, self.dimensions) # Get persistence diagram by simply picking the corresponding entries in the image - self.dgms = [tf.reshape(tf.gather( tf.reshape(tf.transpose(X), [-1]), indice ), [-1,2]) for indice in indices] + self.dgms = [tf.reshape(tf.gather(Xflat, indice), [-1,2]) for indice in indices] return self.dgms -- cgit v1.2.3 From f09d7da77c8a7bc1a16abde3f11f611a4fd7b6f5 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Mon, 15 Nov 2021 18:44:34 +0100 Subject: update doc --- src/python/doc/cubical_complex_tflow_itf_ref.rst | 2 +- src/python/doc/ls_simplex_tree_tflow_itf_ref.rst | 2 +- src/python/doc/rips_complex_tflow_itf_ref.rst | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/python/doc/cubical_complex_tflow_itf_ref.rst b/src/python/doc/cubical_complex_tflow_itf_ref.rst index 582e0551..92727a75 100644 --- a/src/python/doc/cubical_complex_tflow_itf_ref.rst +++ b/src/python/doc/cubical_complex_tflow_itf_ref.rst @@ -12,7 +12,7 @@ Example of gradient computed from cubical persistence .. testcode:: - from gudhi.tensorflow import * + from gudhi.tensorflow import CubicalLayer import tensorflow as tf X = tf.Variable([[0.,2.,2.],[2.,2.,2.],[2.,2.,1.]], dtype=tf.float32, trainable=True) diff --git a/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst b/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst index 6c8b5a08..0a6764fa 100644 --- a/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst +++ b/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst @@ -12,7 +12,7 @@ Example of gradient computed from lower-star filtration of a simplex tree .. testcode:: - from gudhi.tensorflow import * + from gudhi.tensorflow import LowerStarSimplexTreeLayer import tensorflow as tf import gudhi as gd diff --git a/src/python/doc/rips_complex_tflow_itf_ref.rst b/src/python/doc/rips_complex_tflow_itf_ref.rst index bd9c5da5..7aa77da6 100644 --- a/src/python/doc/rips_complex_tflow_itf_ref.rst +++ b/src/python/doc/rips_complex_tflow_itf_ref.rst @@ -12,7 +12,7 @@ Example of gradient computed from Vietoris-Rips persistence .. testcode:: - from gudhi.tensorflow import * + from gudhi.tensorflow import RipsLayer import tensorflow as tf X = tf.Variable([[1.,1.],[2.,2.]], dtype=tf.float32, trainable=True) -- cgit v1.2.3 From 7b83812e37986c9adf9cccaeab360f1d4ffa846f Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Wed, 17 Nov 2021 00:18:33 +0100 Subject: fix doc --- src/python/doc/cubical_complex_tflow_itf_ref.rst | 2 +- src/python/doc/ls_simplex_tree_tflow_itf_ref.rst | 13 +------------ src/python/doc/rips_complex_tflow_itf_ref.rst | 2 +- src/python/gudhi/tensorflow/cubical_layer.py | 2 +- .../gudhi/tensorflow/lower_star_simplex_tree_layer.py | 2 +- src/python/gudhi/tensorflow/rips_layer.py | 2 +- 6 files changed, 6 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/python/doc/cubical_complex_tflow_itf_ref.rst b/src/python/doc/cubical_complex_tflow_itf_ref.rst index 92727a75..692191ba 100644 --- a/src/python/doc/cubical_complex_tflow_itf_ref.rst +++ b/src/python/doc/cubical_complex_tflow_itf_ref.rst @@ -16,7 +16,7 @@ Example of gradient computed from cubical persistence import tensorflow as tf X = tf.Variable([[0.,2.,2.],[2.,2.,2.],[2.,2.,1.]], dtype=tf.float32, trainable=True) - cl = CubicalLayer(dimension=0) + cl = CubicalLayer(dimensions=[0]) with tf.GradientTape() as tape: dgm = cl.call(X) diff --git a/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst b/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst index 0a6764fa..3200b8e5 100644 --- a/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst +++ b/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst @@ -17,17 +17,6 @@ Example of gradient computed from lower-star filtration of a simplex tree import gudhi as gd st = gd.SimplexTree() - st.insert([0]) - st.insert([1]) - st.insert([2]) - st.insert([3]) - st.insert([4]) - st.insert([5]) - st.insert([6]) - st.insert([7]) - st.insert([8]) - st.insert([9]) - st.insert([10]) st.insert([0, 1]) st.insert([1, 2]) st.insert([2, 3]) @@ -40,7 +29,7 @@ Example of gradient computed from lower-star filtration of a simplex tree st.insert([9, 10]) F = tf.Variable([6.,4.,3.,4.,5.,4.,3.,2.,3.,4.,5.], dtype=tf.float32, trainable=True) - sl = LowerStarSimplexTreeLayer(simplextree=st, dimension=0) + sl = LowerStarSimplexTreeLayer(simplextree=st, dimensions=[0]) with tf.GradientTape() as tape: dgm = sl.call(F) diff --git a/src/python/doc/rips_complex_tflow_itf_ref.rst b/src/python/doc/rips_complex_tflow_itf_ref.rst index 7aa77da6..fc42e5c9 100644 --- a/src/python/doc/rips_complex_tflow_itf_ref.rst +++ b/src/python/doc/rips_complex_tflow_itf_ref.rst @@ -16,7 +16,7 @@ Example of gradient computed from Vietoris-Rips persistence import tensorflow as tf X = tf.Variable([[1.,1.],[2.,2.]], dtype=tf.float32, trainable=True) - rl = RipsLayer(maximum_edge_length=2., dimension=0) + rl = RipsLayer(maximum_edge_length=2., dimensions=[0]) with tf.GradientTape() as tape: dgm = rl.call(X) diff --git a/src/python/gudhi/tensorflow/cubical_layer.py b/src/python/gudhi/tensorflow/cubical_layer.py index 0971a446..d07a4cd8 100644 --- a/src/python/gudhi/tensorflow/cubical_layer.py +++ b/src/python/gudhi/tensorflow/cubical_layer.py @@ -42,7 +42,7 @@ class CubicalLayer(tf.keras.layers.Layer): Constructor for the CubicalLayer class Parameters: - dimensions (list of int): homology dimensions + dimensions (List[int]): homology dimensions """ super().__init__(dynamic=True, **kwargs) self.dimensions = dimensions diff --git a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py index fd1698ea..aa55604a 100644 --- a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py +++ b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py @@ -49,7 +49,7 @@ class LowerStarSimplexTreeLayer(tf.keras.layers.Layer): Parameters: simplextree (gudhi.SimplexTree): underlying simplex tree. Its vertices MUST be named with integers from 0 to n = number of vertices - dimensions (int): homology dimensions + dimensions (List[int]): homology dimensions """ super().__init__(dynamic=True, **kwargs) self.dimensions = dimensions diff --git a/src/python/gudhi/tensorflow/rips_layer.py b/src/python/gudhi/tensorflow/rips_layer.py index da7087f6..472a418b 100644 --- a/src/python/gudhi/tensorflow/rips_layer.py +++ b/src/python/gudhi/tensorflow/rips_layer.py @@ -46,7 +46,7 @@ class RipsLayer(tf.keras.layers.Layer): Parameters: maximum_edge_length (float): maximum edge length for the Rips complex - dimensions (int): homology dimensions + dimensions (List[int]): homology dimensions """ super().__init__(dynamic=True, **kwargs) self.max_edge = maximum_edge_length -- cgit v1.2.3 From b966a15818fd7a397ed6edc2b17ee6e188df6df0 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Mon, 22 Nov 2021 23:58:49 +0100 Subject: small change on doc --- src/python/doc/differentiation_sum.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/python/doc/differentiation_sum.inc b/src/python/doc/differentiation_sum.inc index 3dd8e59c..3aec33df 100644 --- a/src/python/doc/differentiation_sum.inc +++ b/src/python/doc/differentiation_sum.inc @@ -8,4 +8,5 @@ We provide TensorFlow 2 models that can handle automatic differentiation for the computation of persistence diagrams from complexes available in the Gudhi library. This includes simplex trees, cubical complexes and Vietoris-Rips complexes. Detailed example on how to use these layers in practice are available -in the following `notebook `_. +in the following `notebook `_. Note that even if TensorFlow GPU is enabled, all +internal computations using Gudhi will be done on CPU. -- cgit v1.2.3 From 979d12e00b4ea71391d132589ee3304e378459b9 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Sat, 4 Dec 2021 12:41:59 +0100 Subject: added min persistence --- src/python/gudhi/tensorflow/cubical_layer.py | 12 +++++------- .../gudhi/tensorflow/lower_star_simplex_tree_layer.py | 14 ++++++-------- src/python/gudhi/tensorflow/rips_layer.py | 12 +++++------- 3 files changed, 16 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/tensorflow/cubical_layer.py b/src/python/gudhi/tensorflow/cubical_layer.py index d07a4cd8..8fe9cff0 100644 --- a/src/python/gudhi/tensorflow/cubical_layer.py +++ b/src/python/gudhi/tensorflow/cubical_layer.py @@ -8,7 +8,7 @@ from ..cubical_complex import CubicalComplex # The parameters of the model are the pixel values. -def _Cubical(Xflat, Xdim, dimensions): +def _Cubical(Xflat, Xdim, dimensions, min_persistence): # Parameters: Xflat (flattened image), # Xdim (shape of non-flattened image) # dimensions (homology dimensions) @@ -16,7 +16,7 @@ def _Cubical(Xflat, Xdim, dimensions): # Compute the persistence pairs with Gudhi # We reverse the dimensions because CubicalComplex uses Fortran ordering cc = CubicalComplex(dimensions=Xdim[::-1], top_dimensional_cells=Xflat) - cc.compute_persistence() + cc.compute_persistence(min_persistence=min_persistence) # Retrieve and ouput image indices/pixels corresponding to positive and negative simplices cof_pp = cc.cofaces_of_persistence_pairs() @@ -37,7 +37,7 @@ class CubicalLayer(tf.keras.layers.Layer): """ TensorFlow layer for computing cubical persistence out of a cubical complex """ - def __init__(self, dimensions, **kwargs): + def __init__(self, dimensions, min_persistence=0., **kwargs): """ Constructor for the CubicalLayer class @@ -46,9 +46,7 @@ class CubicalLayer(tf.keras.layers.Layer): """ super().__init__(dynamic=True, **kwargs) self.dimensions = dimensions - - def build(self): - super.build() + self.min_persistence = min_persistence def call(self, X): """ @@ -64,7 +62,7 @@ class CubicalLayer(tf.keras.layers.Layer): # Don't compute gradient for this operation Xflat = tf.reshape(X, [-1]) Xdim = X.shape - indices = _Cubical(Xflat.numpy(), Xdim, self.dimensions) + indices = _Cubical(Xflat.numpy(), Xdim, self.dimensions, self.min_persistence) # Get persistence diagram by simply picking the corresponding entries in the image self.dgms = [tf.reshape(tf.gather(Xflat, indice), [-1,2]) for indice in indices] return self.dgms diff --git a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py index aa55604a..5902e4a1 100644 --- a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py +++ b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py @@ -7,7 +7,7 @@ import tensorflow as tf # The parameters of the model are the vertex function values of the simplex tree. -def _LowerStarSimplexTree(simplextree, filtration, dimensions): +def _LowerStarSimplexTree(simplextree, filtration, dimensions, min_persistence): # Parameters: simplextree (simplex tree on which to compute persistence) # filtration (function values on the vertices of st), # dimensions (homology dimensions), @@ -21,7 +21,7 @@ def _LowerStarSimplexTree(simplextree, filtration, dimensions): simplextree.make_filtration_non_decreasing() # Compute persistence diagram - simplextree.compute_persistence() + simplextree.compute_persistence(min_persistence=min_persistence) # Get vertex pairs for optimization. First, get all simplex pairs pairs = simplextree.lower_star_persistence_generators() @@ -43,7 +43,7 @@ class LowerStarSimplexTreeLayer(tf.keras.layers.Layer): """ TensorFlow layer for computing lower-star persistence out of a simplex tree """ - def __init__(self, simplextree, dimensions, **kwargs): + def __init__(self, simplextree, dimensions, min_persistence=0., **kwargs): """ Constructor for the LowerStarSimplexTreeLayer class @@ -54,10 +54,8 @@ class LowerStarSimplexTreeLayer(tf.keras.layers.Layer): super().__init__(dynamic=True, **kwargs) self.dimensions = dimensions self.simplextree = simplextree - - def build(self): - super.build() - + self.min_persistence = min_persistence + def call(self, filtration): """ Compute lower-star persistence diagram associated to a function defined on the vertices of the simplex tree @@ -69,7 +67,7 @@ class LowerStarSimplexTreeLayer(tf.keras.layers.Layer): dgms (list of tuple of TensorFlow variables): list of lower-star persistence diagrams of length self.dimensions, where each element of the list is a tuple that contains the finite and essential persistence diagrams of shapes [num_finite_points, 2] and [num_essential_points, 1] respectively """ # Don't try to compute gradients for the vertex pairs - indices = _LowerStarSimplexTree(self.simplextree, filtration.numpy(), self.dimensions) + indices = _LowerStarSimplexTree(self.simplextree, filtration.numpy(), self.dimensions, self.min_persistence) # Get persistence diagrams self.dgms = [] for idx_dim, dimension in enumerate(self.dimensions): diff --git a/src/python/gudhi/tensorflow/rips_layer.py b/src/python/gudhi/tensorflow/rips_layer.py index 472a418b..97f28d74 100644 --- a/src/python/gudhi/tensorflow/rips_layer.py +++ b/src/python/gudhi/tensorflow/rips_layer.py @@ -8,7 +8,7 @@ from ..rips_complex import RipsComplex # The parameters of the model are the point coordinates. -def _Rips(DX, max_edge, dimensions): +def _Rips(DX, max_edge, dimensions, min_persistence): # Parameters: DX (distance matrix), # max_edge (maximum edge length for Rips filtration), # dimensions (homology dimensions) @@ -16,7 +16,7 @@ def _Rips(DX, max_edge, dimensions): # Compute the persistence pairs with Gudhi rc = RipsComplex(distance_matrix=DX, max_edge_length=max_edge) st = rc.create_simplex_tree(max_dimension=max(dimensions)+1) - st.compute_persistence() + st.compute_persistence(min_persistence=min_persistence) pairs = st.flag_persistence_generators() L_indices = [] @@ -40,7 +40,7 @@ class RipsLayer(tf.keras.layers.Layer): """ TensorFlow layer for computing Rips persistence out of a point cloud """ - def __init__(self, dimensions, maximum_edge_length=12, **kwargs): + def __init__(self, dimensions, maximum_edge_length=12, min_persistence=0., **kwargs): """ Constructor for the RipsLayer class @@ -51,9 +51,7 @@ class RipsLayer(tf.keras.layers.Layer): super().__init__(dynamic=True, **kwargs) self.max_edge = maximum_edge_length self.dimensions = dimensions - - def build(self): - super.build() + self.min_persistence = min_persistence def call(self, X): """ @@ -69,7 +67,7 @@ class RipsLayer(tf.keras.layers.Layer): DX = tf.math.sqrt(tf.reduce_sum((tf.expand_dims(X, 1)-tf.expand_dims(X, 0))**2, 2)) # Compute vertices associated to positive and negative simplices # Don't compute gradient for this operation - indices = _Rips(DX.numpy(), self.max_edge, self.dimensions) + indices = _Rips(DX.numpy(), self.max_edge, self.dimensions, self.min_persistence) # Get persistence diagrams by simply picking the corresponding entries in the distance matrix self.dgms = [] for idx_dim, dimension in enumerate(self.dimensions): -- cgit v1.2.3 From 96c7e5ce2f0146798f66c89421b0d23e98a2a390 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Sat, 4 Dec 2021 13:22:23 +0100 Subject: update code and doc --- src/python/gudhi/tensorflow/cubical_layer.py | 20 ++++++++++++++------ .../tensorflow/lower_star_simplex_tree_layer.py | 21 ++++++++++++++------- src/python/gudhi/tensorflow/rips_layer.py | 19 +++++++++++++------ 3 files changed, 41 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/tensorflow/cubical_layer.py b/src/python/gudhi/tensorflow/cubical_layer.py index 8fe9cff0..b16c512f 100644 --- a/src/python/gudhi/tensorflow/cubical_layer.py +++ b/src/python/gudhi/tensorflow/cubical_layer.py @@ -8,7 +8,7 @@ from ..cubical_complex import CubicalComplex # The parameters of the model are the pixel values. -def _Cubical(Xflat, Xdim, dimensions, min_persistence): +def _Cubical(Xflat, Xdim, dimensions): # Parameters: Xflat (flattened image), # Xdim (shape of non-flattened image) # dimensions (homology dimensions) @@ -16,7 +16,7 @@ def _Cubical(Xflat, Xdim, dimensions, min_persistence): # Compute the persistence pairs with Gudhi # We reverse the dimensions because CubicalComplex uses Fortran ordering cc = CubicalComplex(dimensions=Xdim[::-1], top_dimensional_cells=Xflat) - cc.compute_persistence(min_persistence=min_persistence) + cc.compute_persistence() # Retrieve and ouput image indices/pixels corresponding to positive and negative simplices cof_pp = cc.cofaces_of_persistence_pairs() @@ -37,17 +37,19 @@ class CubicalLayer(tf.keras.layers.Layer): """ TensorFlow layer for computing cubical persistence out of a cubical complex """ - def __init__(self, dimensions, min_persistence=0., **kwargs): + def __init__(self, dimensions, min_persistence=None, **kwargs): """ Constructor for the CubicalLayer class Parameters: dimensions (List[int]): homology dimensions + min_persistence (List[float]): minimum distance-to-diagonal of the points in the output persistence diagrams (default None, in which case 0. is used for all dimensions) """ super().__init__(dynamic=True, **kwargs) self.dimensions = dimensions - self.min_persistence = min_persistence - + self.min_persistence = min_persistence if min_persistence != None else [0. for _ in range(len(self.dimensions))] + assert len(self.min_persistence) == len(self.dimensions) + def call(self, X): """ Compute persistence diagram associated to a cubical complex filtered by some pixel values @@ -62,7 +64,13 @@ class CubicalLayer(tf.keras.layers.Layer): # Don't compute gradient for this operation Xflat = tf.reshape(X, [-1]) Xdim = X.shape - indices = _Cubical(Xflat.numpy(), Xdim, self.dimensions, self.min_persistence) + indices = _Cubical(Xflat.numpy(), Xdim, self.dimensions) # Get persistence diagram by simply picking the corresponding entries in the image self.dgms = [tf.reshape(tf.gather(Xflat, indice), [-1,2]) for indice in indices] + for idx_dim in range(len(self.min_persistence)): + min_pers = self.min_persistence[idx_dim] + if min_pers >= 0: + finite_dgm = self.dgms[idx_dim] + persistent_indices = np.argwhere(np.abs(finite_dgm[:,1]-finite_dgm[:,0]) > min_pers).ravel() + self.dgms[idx_dim] = tf.gather(finite_dgm, indices=persistent_indices) return self.dgms diff --git a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py index 5902e4a1..e1627944 100644 --- a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py +++ b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py @@ -7,7 +7,7 @@ import tensorflow as tf # The parameters of the model are the vertex function values of the simplex tree. -def _LowerStarSimplexTree(simplextree, filtration, dimensions, min_persistence): +def _LowerStarSimplexTree(simplextree, filtration, dimensions): # Parameters: simplextree (simplex tree on which to compute persistence) # filtration (function values on the vertices of st), # dimensions (homology dimensions), @@ -21,7 +21,7 @@ def _LowerStarSimplexTree(simplextree, filtration, dimensions, min_persistence): simplextree.make_filtration_non_decreasing() # Compute persistence diagram - simplextree.compute_persistence(min_persistence=min_persistence) + simplextree.compute_persistence() # Get vertex pairs for optimization. First, get all simplex pairs pairs = simplextree.lower_star_persistence_generators() @@ -43,19 +43,21 @@ class LowerStarSimplexTreeLayer(tf.keras.layers.Layer): """ TensorFlow layer for computing lower-star persistence out of a simplex tree """ - def __init__(self, simplextree, dimensions, min_persistence=0., **kwargs): + def __init__(self, simplextree, dimensions, min_persistence=None, **kwargs): """ Constructor for the LowerStarSimplexTreeLayer class Parameters: simplextree (gudhi.SimplexTree): underlying simplex tree. Its vertices MUST be named with integers from 0 to n = number of vertices dimensions (List[int]): homology dimensions + min_persistence (List[float]): minimum distance-to-diagonal of the points in the output persistence diagrams (default None, in which case 0. is used for all dimensions) """ super().__init__(dynamic=True, **kwargs) self.dimensions = dimensions self.simplextree = simplextree - self.min_persistence = min_persistence - + self.min_persistence = min_persistence if min_persistence != None else [0. for _ in range(len(self.dimensions))] + assert len(self.min_persistence) == len(self.dimensions) + def call(self, filtration): """ Compute lower-star persistence diagram associated to a function defined on the vertices of the simplex tree @@ -67,12 +69,17 @@ class LowerStarSimplexTreeLayer(tf.keras.layers.Layer): dgms (list of tuple of TensorFlow variables): list of lower-star persistence diagrams of length self.dimensions, where each element of the list is a tuple that contains the finite and essential persistence diagrams of shapes [num_finite_points, 2] and [num_essential_points, 1] respectively """ # Don't try to compute gradients for the vertex pairs - indices = _LowerStarSimplexTree(self.simplextree, filtration.numpy(), self.dimensions, self.min_persistence) + indices = _LowerStarSimplexTree(self.simplextree, filtration.numpy(), self.dimensions) # Get persistence diagrams self.dgms = [] for idx_dim, dimension in enumerate(self.dimensions): finite_dgm = tf.reshape(tf.gather(filtration, indices[idx_dim][0]), [-1,2]) essential_dgm = tf.reshape(tf.gather(filtration, indices[idx_dim][1]), [-1,1]) - self.dgms.append((finite_dgm, essential_dgm)) + min_pers = self.min_persistence[idx_dim] + if min_pers >= 0: + persistent_indices = np.argwhere(np.abs(finite_dgm[:,1]-finite_dgm[:,0]) > min_pers).ravel() + self.dgms.append((tf.gather(finite_dgm, indices=persistent_indices), essential_dgm)) + else: + self.dgms.append((finite_dgm, essential_dgm)) return self.dgms diff --git a/src/python/gudhi/tensorflow/rips_layer.py b/src/python/gudhi/tensorflow/rips_layer.py index 97f28d74..a5f212e3 100644 --- a/src/python/gudhi/tensorflow/rips_layer.py +++ b/src/python/gudhi/tensorflow/rips_layer.py @@ -8,7 +8,7 @@ from ..rips_complex import RipsComplex # The parameters of the model are the point coordinates. -def _Rips(DX, max_edge, dimensions, min_persistence): +def _Rips(DX, max_edge, dimensions): # Parameters: DX (distance matrix), # max_edge (maximum edge length for Rips filtration), # dimensions (homology dimensions) @@ -16,7 +16,7 @@ def _Rips(DX, max_edge, dimensions, min_persistence): # Compute the persistence pairs with Gudhi rc = RipsComplex(distance_matrix=DX, max_edge_length=max_edge) st = rc.create_simplex_tree(max_dimension=max(dimensions)+1) - st.compute_persistence(min_persistence=min_persistence) + st.compute_persistence() pairs = st.flag_persistence_generators() L_indices = [] @@ -40,18 +40,20 @@ class RipsLayer(tf.keras.layers.Layer): """ TensorFlow layer for computing Rips persistence out of a point cloud """ - def __init__(self, dimensions, maximum_edge_length=12, min_persistence=0., **kwargs): + def __init__(self, dimensions, maximum_edge_length=12, min_persistence=None, **kwargs): """ Constructor for the RipsLayer class Parameters: maximum_edge_length (float): maximum edge length for the Rips complex dimensions (List[int]): homology dimensions + min_persistence (List[float]): minimum distance-to-diagonal of the points in the output persistence diagrams (default None, in which case 0. is used for all dimensions) """ super().__init__(dynamic=True, **kwargs) self.max_edge = maximum_edge_length self.dimensions = dimensions - self.min_persistence = min_persistence + self.min_persistence = min_persistence if min_persistence != None else [0. for _ in range(len(self.dimensions))] + assert len(self.min_persistence) == len(self.dimensions) def call(self, X): """ @@ -67,7 +69,7 @@ class RipsLayer(tf.keras.layers.Layer): DX = tf.math.sqrt(tf.reduce_sum((tf.expand_dims(X, 1)-tf.expand_dims(X, 0))**2, 2)) # Compute vertices associated to positive and negative simplices # Don't compute gradient for this operation - indices = _Rips(DX.numpy(), self.max_edge, self.dimensions, self.min_persistence) + indices = _Rips(DX.numpy(), self.max_edge, self.dimensions) # Get persistence diagrams by simply picking the corresponding entries in the distance matrix self.dgms = [] for idx_dim, dimension in enumerate(self.dimensions): @@ -79,6 +81,11 @@ class RipsLayer(tf.keras.layers.Layer): reshaped_cur_idx = tf.reshape(cur_idx[0], [-1,3]) finite_dgm = tf.concat([tf.zeros([reshaped_cur_idx.shape[0],1]), tf.reshape(tf.gather_nd(DX, reshaped_cur_idx[:,1:]), [-1,1])], axis=1) essential_dgm = tf.zeros([cur_idx[1].shape[0],1]) - self.dgms.append((finite_dgm, essential_dgm)) + min_pers = self.min_persistence[idx_dim] + if min_pers >= 0: + persistent_indices = np.argwhere(np.abs(finite_dgm[:,1]-finite_dgm[:,0]) > min_pers).ravel() + self.dgms.append((tf.gather(finite_dgm, indices=persistent_indices), essential_dgm)) + else: + self.dgms.append((finite_dgm, essential_dgm)) return self.dgms -- cgit v1.2.3 From 9db268b5ecf056b87ee2f66c6d3f83de93a8681f Mon Sep 17 00:00:00 2001 From: Hind-M Date: Wed, 29 Dec 2021 15:38:59 +0100 Subject: Get min sphere without using a set Move face_points declaration Remove face_sh redundant variable --- .../include/gudhi/Cech_complex_blocker.h | 30 ++++++++-------------- 1 file changed, 11 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h index 5edd005d..f7f86534 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h +++ b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h @@ -63,16 +63,6 @@ class Cech_blocker { return std::make_pair(std::move(c), std::move(r)); } - template - class CompareSpheresRadii - { - public: - CGAL::NT_converter cast_to_double; - bool operator()(const Sphere& firstSphere, const Sphere& secondSphere) - { - return cast_to_double(firstSphere.second) < cast_to_double(secondSphere.second); - } - }; /** \internal \brief Čech complex blocker operator() - the oracle - assigns the filtration value from the simplex * radius and returns if the simplex expansion must be blocked. @@ -84,7 +74,10 @@ class Cech_blocker { Filtration_value radius = 0.; // for each face of simplex sh, test outsider point is indeed inside enclosing ball, if yes, take it and exit loop, otherwise, new sphere is circumsphere of all vertices - std::set > enclosing_ball_spheres; + Sphere min_enclos_ball; + CGAL::NT_converter cast_to_FT; + min_enclos_ball.second = cast_to_FT(std::numeric_limits::max()); + Point_cloud face_points; for (auto face : sc_ptr_->boundary_simplex_range(sh)) { // Find which vertex of sh is missing in face. We rely on the fact that simplex_vertex_range is sorted. auto longlist = sc_ptr_->simplex_vertex_range(sh); @@ -96,7 +89,6 @@ class Cech_blocker { while(shortiter != enditer && *longiter == *shortiter) { ++longiter; ++shortiter; } auto extra = *longiter; // Vertex_handle - Point_cloud face_points; for (auto vertex : sc_ptr_->simplex_vertex_range(face)) { face_points.push_back(cc_ptr_->get_point(vertex)); #ifdef DEBUG_TRACES @@ -104,30 +96,30 @@ class Cech_blocker { #endif // DEBUG_TRACES } Sphere sph; - auto face_sh = sc_ptr_->find(sc_ptr_->simplex_vertex_range(face)); - auto k = sc_ptr_->key(face_sh); + auto k = sc_ptr_->key(face); if(k != sc_ptr_->null_key()) { sph = cc_ptr_->get_cache().at(k); } else { sph = get_sphere(face_points.cbegin(), face_points.cend()); } + face_points.clear(); if (kernel_.squared_distance_d_object()(sph.first, cc_ptr_->get_point(extra)) <= sph.second) { radius = std::sqrt(cast_to_double(sph.second)); #ifdef DEBUG_TRACES std::clog << "circumcenter: " << sph.first << ", radius: " << radius << std::endl; #endif // DEBUG_TRACES - enclosing_ball_spheres.insert(sph); + if (cast_to_double(sph.second) < cast_to_double(min_enclos_ball.second)) + min_enclos_ball = sph; } } // Get the minimal radius of all faces enclosing balls if exists - if (!enclosing_ball_spheres.empty()) { - Sphere sph_min = *enclosing_ball_spheres.begin(); - radius = std::sqrt(cast_to_double(sph_min.second)); + if(cast_to_double(min_enclos_ball.second) != std::numeric_limits::max()) { + radius = std::sqrt(cast_to_double(min_enclos_ball.second)); sc_ptr_->assign_key(sh, cc_ptr_->get_cache().size()); - cc_ptr_->get_cache().push_back(sph_min); + cc_ptr_->get_cache().push_back(min_enclos_ball); } if (radius == 0.) { // Spheres of each face don't contain the whole simplex -- cgit v1.2.3 From b1f40dd2c4397c1975533c54a54538160c727d55 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Thu, 6 Jan 2022 11:39:05 +0100 Subject: Make kernel a parameter of Minimal_enclosing_ball_radius class Use Epick in cech benchmark instead of Epeck --- src/Cech_complex/benchmark/cech_complex_benchmark.cpp | 8 ++++---- src/Cech_complex/include/gudhi/Cech_complex.h | 3 +-- .../include/gudhi/Cech_complex/Cech_kernel.h | 16 ++++++++-------- src/Cech_complex/test/test_cech_complex.cpp | 4 ++-- 4 files changed, 15 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/benchmark/cech_complex_benchmark.cpp b/src/Cech_complex/benchmark/cech_complex_benchmark.cpp index 06d90757..e715b513 100644 --- a/src/Cech_complex/benchmark/cech_complex_benchmark.cpp +++ b/src/Cech_complex/benchmark/cech_complex_benchmark.cpp @@ -17,7 +17,7 @@ #include #include -#include // For EXACT or SAFE version +#include #include "boost/filesystem.hpp" // includes all needed Boost.Filesystem declarations @@ -32,7 +32,7 @@ using Point_cloud = std::vector; using Points_off_reader = Gudhi::Points_off_reader; using Proximity_graph = Gudhi::Proximity_graph; using Rips_complex = Gudhi::rips_complex::Rips_complex; -using Kernel = CGAL::Epeck_d; +using Kernel = CGAL::Epick_d>; using Point_cgal = typename Kernel::Point_d; using Point_cloud_cgal = std::vector; using Points_off_reader_cgal = Gudhi::Points_off_reader; @@ -86,7 +86,7 @@ int main(int argc, char* argv[]) { Gudhi::Clock cgal_miniball_clock("Gudhi::Minimal_enclosing_ball_radius_cgal()"); // Compute the proximity graph of the points Proximity_graph cgal_miniball_prox_graph = Gudhi::compute_proximity_graph( - off_reader_cgal.get_point_cloud(), threshold, Gudhi::Minimal_enclosing_ball_radius()); + off_reader_cgal.get_point_cloud(), threshold, Gudhi::Minimal_enclosing_ball_radius()); std::clog << cgal_miniball_clock << std::endl; boost::filesystem::path full_path(boost::filesystem::current_path()); @@ -109,7 +109,7 @@ int main(int argc, char* argv[]) { std::clog << radius << ";"; Gudhi::Clock rips_clock("Rips computation"); Rips_complex rips_complex_from_points(off_reader_cgal.get_point_cloud(), radius, - Gudhi::Minimal_enclosing_ball_radius()); + Gudhi::Minimal_enclosing_ball_radius()); Simplex_tree rips_stree; rips_complex_from_points.create_complex(rips_stree, p0.size() - 1); // ------------------------------------------ diff --git a/src/Cech_complex/include/gudhi/Cech_complex.h b/src/Cech_complex/include/gudhi/Cech_complex.h index 7bbf97d1..0031d861 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex.h +++ b/src/Cech_complex/include/gudhi/Cech_complex.h @@ -18,7 +18,6 @@ #include #include // for exception management -#include namespace Gudhi { @@ -78,7 +77,7 @@ class Cech_complex { point_cloud_.assign(points.begin(), points.end()); cech_skeleton_graph_ = Gudhi::compute_proximity_graph( - point_cloud_, max_radius_, Gudhi::Minimal_enclosing_ball_radius()); + point_cloud_, max_radius_, Gudhi::Minimal_enclosing_ball_radius()); } /** \brief Initializes the simplicial complex from the proximity graph and expands it until a given maximal diff --git a/src/Cech_complex/include/gudhi/Cech_complex/Cech_kernel.h b/src/Cech_complex/include/gudhi/Cech_complex/Cech_kernel.h index 348bb57d..89012206 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex/Cech_kernel.h +++ b/src/Cech_complex/include/gudhi/Cech_complex/Cech_kernel.h @@ -11,9 +11,10 @@ #ifndef CECH_KERNEL_H_ #define CECH_KERNEL_H_ -#include +#include // for #include #include // for std::sqrt +#include namespace Gudhi { @@ -21,8 +22,14 @@ namespace Gudhi { /** @brief Compute the radius of the minimal enclosing ball between Points given by a range of coordinates. * The points are assumed to have the same dimension. */ +template class Minimal_enclosing_ball_radius { + private: + Kernel kernel_; public: + using Point = typename Kernel::Point_d; + using Point_cloud = typename std::vector; + /** \brief Enclosing ball radius from two points using CGAL. * * @param[in] point_1 @@ -31,10 +38,7 @@ class Minimal_enclosing_ball_radius { * \tparam Point must be a Kernel::Point_d from CGAL. * */ - template< typename Kernel = CGAL::Epeck_d, - typename Point= typename Kernel::Point_d> double operator()(const Point& point_1, const Point& point_2) const { - Kernel kernel_; return std::sqrt(CGAL::to_double(kernel_.squared_distance_d_object()(point_1, point_2))) / 2.; } @@ -46,11 +50,7 @@ class Minimal_enclosing_ball_radius { * \tparam Point_cloud must be a range of Kernel::Point_d points from CGAL. * */ - template< typename Kernel = CGAL::Epeck_d, - typename Point= typename Kernel::Point_d, - typename Point_cloud = std::vector> double operator()(const Point_cloud& point_cloud) const { - Kernel kernel_; return std::sqrt(CGAL::to_double(kernel_.compute_squared_radius_d_object()(point_cloud.begin(), point_cloud.end()))); } diff --git a/src/Cech_complex/test/test_cech_complex.cpp b/src/Cech_complex/test/test_cech_complex.cpp index 7d8c3c22..ca7a9778 100644 --- a/src/Cech_complex/test/test_cech_complex.cpp +++ b/src/Cech_complex/test/test_cech_complex.cpp @@ -108,11 +108,11 @@ BOOST_AUTO_TEST_CASE(Cech_complex_for_documentation) { std::clog << vertex << ","; vp.push_back(points.at(vertex)); } - std::clog << ") - distance =" << Gudhi::Minimal_enclosing_ball_radius()(vp.at(0), vp.at(1)) + std::clog << ") - distance =" << Gudhi::Minimal_enclosing_ball_radius()(vp.at(0), vp.at(1)) << " - filtration =" << st.filtration(f_simplex) << std::endl; BOOST_CHECK(vp.size() == 2); GUDHI_TEST_FLOAT_EQUALITY_CHECK(st.filtration(f_simplex), - Gudhi::Minimal_enclosing_ball_radius()(vp.at(0), vp.at(1))); + Gudhi::Minimal_enclosing_ball_radius()(vp.at(0), vp.at(1))); } } -- cgit v1.2.3 From beb431316a5181caf0eec5c0940601457340cc58 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Thu, 6 Jan 2022 13:40:33 +0100 Subject: Add left out kernel to Minimal_enclosing_ball_radius class in cech example --- src/Cech_complex/example/cech_complex_step_by_step.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/example/cech_complex_step_by_step.cpp b/src/Cech_complex/example/cech_complex_step_by_step.cpp index 2d8321b1..c8dd1585 100644 --- a/src/Cech_complex/example/cech_complex_step_by_step.cpp +++ b/src/Cech_complex/example/cech_complex_step_by_step.cpp @@ -52,7 +52,7 @@ class Cech_blocker { std::clog << "#(" << vertex << ")#"; #endif // DEBUG_TRACES } - Filtration_value radius = Gudhi::Minimal_enclosing_ball_radius()(points); + Filtration_value radius = Gudhi::Minimal_enclosing_ball_radius()(points); #ifdef DEBUG_TRACES std::clog << "radius = " << radius << " - " << (radius > max_radius_) << std::endl; #endif // DEBUG_TRACES @@ -83,7 +83,7 @@ int main(int argc, char* argv[]) { // Compute the proximity graph of the points Proximity_graph prox_graph = Gudhi::compute_proximity_graph(off_reader.get_point_cloud(), max_radius, - Gudhi::Minimal_enclosing_ball_radius()); + Gudhi::Minimal_enclosing_ball_radius()); // Construct the Cech complex in a Simplex Tree Simplex_tree st; -- cgit v1.2.3 From 5c00d2dfcf4b0e2835441533f12f195d83652e99 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Wed, 2 Feb 2022 22:11:04 +0100 Subject: fixed bugs from the new API --- src/python/CMakeLists.txt | 3 ++- src/python/doc/cubical_complex_tflow_itf_ref.rst | 2 +- src/python/doc/ls_simplex_tree_tflow_itf_ref.rst | 2 +- src/python/doc/rips_complex_tflow_itf_ref.rst | 2 +- src/python/gudhi/tensorflow/cubical_layer.py | 8 ++++---- src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py | 3 +-- src/python/gudhi/tensorflow/rips_layer.py | 4 ++-- src/python/test/test_diff.py | 4 ++-- 8 files changed, 14 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index bed2b541..e4ac1b48 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -556,6 +556,7 @@ if(PYTHONINTERP_FOUND) # Differentiation if(TENSORFLOW_FOUND) add_gudhi_py_test(test_diff) + endif() # Betti curves if(SKLEARN_FOUND AND SCIPY_FOUND) @@ -596,4 +597,4 @@ if(PYTHONINTERP_FOUND) else(PYTHONINTERP_FOUND) message("++ Python module will not be compiled because no Python interpreter was found") set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python" CACHE INTERNAL "GUDHI_MISSING_MODULES") -endif(PYTHONINTERP_FOUND) \ No newline at end of file +endif(PYTHONINTERP_FOUND) diff --git a/src/python/doc/cubical_complex_tflow_itf_ref.rst b/src/python/doc/cubical_complex_tflow_itf_ref.rst index 692191ba..18b97adf 100644 --- a/src/python/doc/cubical_complex_tflow_itf_ref.rst +++ b/src/python/doc/cubical_complex_tflow_itf_ref.rst @@ -19,7 +19,7 @@ Example of gradient computed from cubical persistence cl = CubicalLayer(dimensions=[0]) with tf.GradientTape() as tape: - dgm = cl.call(X) + dgm = cl.call(X)[0][0] loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) grads = tape.gradient(loss, [X]) diff --git a/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst b/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst index 3200b8e5..56bb4492 100644 --- a/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst +++ b/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst @@ -32,7 +32,7 @@ Example of gradient computed from lower-star filtration of a simplex tree sl = LowerStarSimplexTreeLayer(simplextree=st, dimensions=[0]) with tf.GradientTape() as tape: - dgm = sl.call(F) + dgm = sl.call(F)[0][0] loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) grads = tape.gradient(loss, [F]) diff --git a/src/python/doc/rips_complex_tflow_itf_ref.rst b/src/python/doc/rips_complex_tflow_itf_ref.rst index fc42e5c9..104b0971 100644 --- a/src/python/doc/rips_complex_tflow_itf_ref.rst +++ b/src/python/doc/rips_complex_tflow_itf_ref.rst @@ -19,7 +19,7 @@ Example of gradient computed from Vietoris-Rips persistence rl = RipsLayer(maximum_edge_length=2., dimensions=[0]) with tf.GradientTape() as tape: - dgm = rl.call(X) + dgm = rl.call(X)[0][0] loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) grads = tape.gradient(loss, [X]) diff --git a/src/python/gudhi/tensorflow/cubical_layer.py b/src/python/gudhi/tensorflow/cubical_layer.py index b16c512f..99d02d66 100644 --- a/src/python/gudhi/tensorflow/cubical_layer.py +++ b/src/python/gudhi/tensorflow/cubical_layer.py @@ -47,7 +47,7 @@ class CubicalLayer(tf.keras.layers.Layer): """ super().__init__(dynamic=True, **kwargs) self.dimensions = dimensions - self.min_persistence = min_persistence if min_persistence != None else [0. for _ in range(len(self.dimensions))] + self.min_persistence = min_persistence if min_persistence != None else [0.] * len(self.dimensions) assert len(self.min_persistence) == len(self.dimensions) def call(self, X): @@ -64,13 +64,13 @@ class CubicalLayer(tf.keras.layers.Layer): # Don't compute gradient for this operation Xflat = tf.reshape(X, [-1]) Xdim = X.shape - indices = _Cubical(Xflat.numpy(), Xdim, self.dimensions) + indices_list = _Cubical(Xflat.numpy(), Xdim, self.dimensions) # Get persistence diagram by simply picking the corresponding entries in the image - self.dgms = [tf.reshape(tf.gather(Xflat, indice), [-1,2]) for indice in indices] + self.dgms = [tf.reshape(tf.gather(Xflat, indices), [-1,2]) for indices in indices_list] for idx_dim in range(len(self.min_persistence)): min_pers = self.min_persistence[idx_dim] if min_pers >= 0: finite_dgm = self.dgms[idx_dim] - persistent_indices = np.argwhere(np.abs(finite_dgm[:,1]-finite_dgm[:,0]) > min_pers).ravel() + persistent_indices = tf.where(tf.math.abs(finite_dgm[:,1]-finite_dgm[:,0]) > min_pers) self.dgms[idx_dim] = tf.gather(finite_dgm, indices=persistent_indices) return self.dgms diff --git a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py index e1627944..8da1f7fe 100644 --- a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py +++ b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py @@ -12,8 +12,7 @@ def _LowerStarSimplexTree(simplextree, filtration, dimensions): # filtration (function values on the vertices of st), # dimensions (homology dimensions), - for s,_ in simplextree.get_filtration(): - simplextree.assign_filtration(s, -1e10) + simplextree.reset_filtration(-np.inf, 0) # Assign new filtration values for i in range(simplextree.num_vertices()): diff --git a/src/python/gudhi/tensorflow/rips_layer.py b/src/python/gudhi/tensorflow/rips_layer.py index a5f212e3..88d501c1 100644 --- a/src/python/gudhi/tensorflow/rips_layer.py +++ b/src/python/gudhi/tensorflow/rips_layer.py @@ -40,7 +40,7 @@ class RipsLayer(tf.keras.layers.Layer): """ TensorFlow layer for computing Rips persistence out of a point cloud """ - def __init__(self, dimensions, maximum_edge_length=12, min_persistence=None, **kwargs): + def __init__(self, dimensions, maximum_edge_length=np.inf, min_persistence=None, **kwargs): """ Constructor for the RipsLayer class @@ -66,7 +66,7 @@ class RipsLayer(tf.keras.layers.Layer): dgms (list of tuple of TensorFlow variables): list of Rips persistence diagrams of length self.dimensions, where each element of the list is a tuple that contains the finite and essential persistence diagrams of shapes [num_finite_points, 2] and [num_essential_points, 1] respectively """ # Compute distance matrix - DX = tf.math.sqrt(tf.reduce_sum((tf.expand_dims(X, 1)-tf.expand_dims(X, 0))**2, 2)) + DX = tf.norm(tf.expand_dims(X, 1)-tf.expand_dims(X, 0), axis=2) # Compute vertices associated to positive and negative simplices # Don't compute gradient for this operation indices = _Rips(DX.numpy(), self.max_edge, self.dimensions) diff --git a/src/python/test/test_diff.py b/src/python/test/test_diff.py index e0c99d07..bab0d10c 100644 --- a/src/python/test/test_diff.py +++ b/src/python/test/test_diff.py @@ -22,7 +22,7 @@ def test_cubical_diff(): cl = CubicalLayer(dimensions=[0]) with tf.GradientTape() as tape: - dgm = cl.call(X)[0] + dgm = cl.call(X)[0][0] loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) grads = tape.gradient(loss, [X]) assert np.abs(grads[0].numpy()-np.array([[0.,0.,0.],[0.,.5,0.],[0.,0.,-.5]])).sum() <= 1e-6 @@ -34,7 +34,7 @@ def test_nonsquare_cubical_diff(): cl = CubicalLayer(dimensions=[0]) with tf.GradientTape() as tape: - dgm = cl.call(X)[0] + dgm = cl.call(X)[0][0] loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) grads = tape.gradient(loss, [X]) assert np.abs(grads[0].numpy()-np.array([[0.,0.5,-0.5],[0.,0.,0.]])).sum() <= 1e-6 -- cgit v1.2.3 From 2d1fb6b63f0ca0c7e027cc298fc16198a6283df1 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Fri, 11 Feb 2022 15:00:27 +0100 Subject: Do some code clean up/renaming --- .../benchmark/cech_complex_benchmark.cpp | 13 ++- .../example/cech_complex_example_from_points.cpp | 2 +- .../example/cech_complex_step_by_step.cpp | 6 +- src/Cech_complex/include/gudhi/Cech_complex.h | 40 +++----- .../include/gudhi/Cech_complex/Cech_kernel.h | 107 --------------------- .../include/gudhi/Cech_complex_blocker.h | 22 ++--- .../include/gudhi/sphere_circumradius.h | 62 ++++++++++++ src/Cech_complex/test/test_cech_complex.cpp | 10 +- src/Cech_complex/utilities/cech_persistence.cpp | 5 +- 9 files changed, 106 insertions(+), 161 deletions(-) delete mode 100644 src/Cech_complex/include/gudhi/Cech_complex/Cech_kernel.h create mode 100644 src/Cech_complex/include/gudhi/sphere_circumradius.h (limited to 'src') diff --git a/src/Cech_complex/benchmark/cech_complex_benchmark.cpp b/src/Cech_complex/benchmark/cech_complex_benchmark.cpp index 94c5fa4f..b283e1a8 100644 --- a/src/Cech_complex/benchmark/cech_complex_benchmark.cpp +++ b/src/Cech_complex/benchmark/cech_complex_benchmark.cpp @@ -34,9 +34,8 @@ using Proximity_graph = Gudhi::Proximity_graph; using Rips_complex = Gudhi::rips_complex::Rips_complex; using Kernel = CGAL::Epick_d>; using Point_cgal = typename Kernel::Point_d; -using Point_cloud_cgal = std::vector; using Points_off_reader_cgal = Gudhi::Points_off_reader; -using Cech_complex = Gudhi::cech_complex::Cech_complex; +using Cech_complex = Gudhi::cech_complex::Cech_complex; class Minimal_enclosing_ball_radius { public: @@ -83,11 +82,11 @@ int main(int argc, char* argv[]) { off_reader.get_point_cloud(), threshold, Minimal_enclosing_ball_radius()); std::clog << miniball_clock << std::endl; - Gudhi::Clock cgal_miniball_clock("Gudhi::Minimal_enclosing_ball_radius_cgal()"); + Gudhi::Clock cgal_circumsphere_clock("Gudhi::cech_complex::Sphere_circumradius_cgal()"); // Compute the proximity graph of the points - Proximity_graph cgal_miniball_prox_graph = Gudhi::compute_proximity_graph( - off_reader_cgal.get_point_cloud(), threshold, Gudhi::Minimal_enclosing_ball_radius()); - std::clog << cgal_miniball_clock << std::endl; + Proximity_graph cgal_circumsphere_prox_graph = Gudhi::compute_proximity_graph( + off_reader_cgal.get_point_cloud(), threshold, Gudhi::cech_complex::Sphere_circumradius()); + std::clog << cgal_circumsphere_clock << std::endl; boost::filesystem::path full_path(boost::filesystem::current_path()); std::clog << "Current path is : " << full_path << std::endl; @@ -109,7 +108,7 @@ int main(int argc, char* argv[]) { std::clog << radius << ";"; Gudhi::Clock rips_clock("Rips computation"); Rips_complex rips_complex_from_points(off_reader_cgal.get_point_cloud(), radius, - Gudhi::Minimal_enclosing_ball_radius()); + Gudhi::cech_complex::Sphere_circumradius()); Simplex_tree rips_stree; rips_complex_from_points.create_complex(rips_stree, p0.size() - 1); // ------------------------------------------ diff --git a/src/Cech_complex/example/cech_complex_example_from_points.cpp b/src/Cech_complex/example/cech_complex_example_from_points.cpp index 78861951..38021e4a 100644 --- a/src/Cech_complex/example/cech_complex_example_from_points.cpp +++ b/src/Cech_complex/example/cech_complex_example_from_points.cpp @@ -16,7 +16,7 @@ int main() { using FT = typename Kernel::FT; using Point = typename Kernel::Point_d; using Point_cloud = std::vector; - using Cech_complex = Gudhi::cech_complex::Cech_complex; + using Cech_complex = Gudhi::cech_complex::Cech_complex; Point_cloud points; diff --git a/src/Cech_complex/example/cech_complex_step_by_step.cpp b/src/Cech_complex/example/cech_complex_step_by_step.cpp index c8dd1585..4401f6af 100644 --- a/src/Cech_complex/example/cech_complex_step_by_step.cpp +++ b/src/Cech_complex/example/cech_complex_step_by_step.cpp @@ -9,7 +9,7 @@ */ #include -#include +#include #include #include @@ -52,7 +52,7 @@ class Cech_blocker { std::clog << "#(" << vertex << ")#"; #endif // DEBUG_TRACES } - Filtration_value radius = Gudhi::Minimal_enclosing_ball_radius()(points); + Filtration_value radius = Gudhi::cech_complex::Sphere_circumradius()(points); #ifdef DEBUG_TRACES std::clog << "radius = " << radius << " - " << (radius > max_radius_) << std::endl; #endif // DEBUG_TRACES @@ -83,7 +83,7 @@ int main(int argc, char* argv[]) { // Compute the proximity graph of the points Proximity_graph prox_graph = Gudhi::compute_proximity_graph(off_reader.get_point_cloud(), max_radius, - Gudhi::Minimal_enclosing_ball_radius()); + Gudhi::cech_complex::Sphere_circumradius()); // Construct the Cech complex in a Simplex Tree Simplex_tree st; diff --git a/src/Cech_complex/include/gudhi/Cech_complex.h b/src/Cech_complex/include/gudhi/Cech_complex.h index 0031d861..375be1d2 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex.h +++ b/src/Cech_complex/include/gudhi/Cech_complex.h @@ -11,7 +11,7 @@ #ifndef CECH_COMPLEX_H_ #define CECH_COMPLEX_H_ -#include // for Gudhi::Minimal_enclosing_ball_radius +#include // for Gudhi::cech_complex::Sphere_circumradius #include // for Gudhi::Proximity_graph #include // for GUDHI_CHECK #include // for Gudhi::cech_complex::Cech_blocker @@ -31,23 +31,21 @@ namespace cech_complex { * * \details * The data structure is a proximity graph, containing edges when the edge length is less or equal - * to a given max_radius. Edge length is computed from `Gudhi::Minimal_enclosing_ball_radius` distance function. + * to a given max_radius. Edge length is computed from `Gudhi::cech_complex::Sphere_circumradius` distance function. * - * \tparam SimplicialComplexForProximityGraph furnishes `Vertex_handle` and `Filtration_value` type definition required - * by `Gudhi::Proximity_graph`. + * \tparam Kernel CGAL kernel. + * + * \tparam SimplicialComplexForCechComplex furnishes `Vertex_handle` and `Filtration_value` type definition required + * by `Gudhi::Proximity_graph` and Cech blocker. * - * \tparam ForwardPointRange must be a range for which `std::begin()` and `std::end()` methods return input - * iterators on a point. `std::begin()` and `std::end()` methods are also required for a point. */ -template +template class Cech_complex { private: // Required by compute_proximity_graph - using Vertex_handle = typename SimplicialComplexForProximityGraph::Vertex_handle; - using Filtration_value = typename SimplicialComplexForProximityGraph::Filtration_value; - using Proximity_graph = Gudhi::Proximity_graph; - - public: + using Vertex_handle = typename SimplicialComplexForCechComplex::Vertex_handle; + using Filtration_value = typename SimplicialComplexForCechComplex::Filtration_value; + using Proximity_graph = Gudhi::Proximity_graph; using cech_blocker = Cech_blocker; @@ -57,27 +55,21 @@ class Cech_complex { // Numeric type of coordinates in the kernel using FT = typename cech_blocker::FT; // Sphere is a pair of point and squared radius. - using Sphere = typename std::pair; + using Sphere = typename cech_blocker::Sphere; - public: + public: /** \brief Cech_complex constructor from a list of points. * - * @param[in] points Range of points. + * @param[in] points Vector of points where each point is defined as `kernel::Point_d`. * @param[in] max_radius Maximal radius value. * - * \tparam ForwardPointRange must be a range of Point. Point must be a range of copyable Cartesian coordinates. - * */ - Cech_complex(const ForwardPointRange& points, Filtration_value max_radius) : max_radius_(max_radius) { - // Point cloud deep copy - -// point_cloud_.reserve(boost::size(points)); -// for (auto&& point : points) point_cloud_.emplace_back(point.cartesian_begin(), point.cartesian_end()); + Cech_complex(const Point_cloud & points, Filtration_value max_radius) : max_radius_(max_radius) { point_cloud_.assign(points.begin(), points.end()); - cech_skeleton_graph_ = Gudhi::compute_proximity_graph( - point_cloud_, max_radius_, Gudhi::Minimal_enclosing_ball_radius()); + cech_skeleton_graph_ = Gudhi::compute_proximity_graph( + point_cloud_, max_radius_, Sphere_circumradius()); } /** \brief Initializes the simplicial complex from the proximity graph and expands it until a given maximal diff --git a/src/Cech_complex/include/gudhi/Cech_complex/Cech_kernel.h b/src/Cech_complex/include/gudhi/Cech_complex/Cech_kernel.h deleted file mode 100644 index 89012206..00000000 --- a/src/Cech_complex/include/gudhi/Cech_complex/Cech_kernel.h +++ /dev/null @@ -1,107 +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): Hind Montassif - * - * Copyright (C) 2021 Inria - * - * Modification(s): - * - YYYY/MM Author: Description of the modification - */ - -#ifndef CECH_KERNEL_H_ -#define CECH_KERNEL_H_ - -#include // for #include - -#include // for std::sqrt -#include - -namespace Gudhi { - -// namespace cech_complex { - -/** @brief Compute the radius of the minimal enclosing ball between Points given by a range of coordinates. - * The points are assumed to have the same dimension. */ -template -class Minimal_enclosing_ball_radius { - private: - Kernel kernel_; - public: - using Point = typename Kernel::Point_d; - using Point_cloud = typename std::vector; - - /** \brief Enclosing ball radius from two points using CGAL. - * - * @param[in] point_1 - * @param[in] point_2 - * @return Enclosing ball radius for the two points. - * \tparam Point must be a Kernel::Point_d from CGAL. - * - */ - double operator()(const Point& point_1, const Point& point_2) const { - return std::sqrt(CGAL::to_double(kernel_.squared_distance_d_object()(point_1, point_2))) / 2.; - } - - - /** \brief Enclosing ball radius from a point cloud using CGAL. - * - * @param[in] point_cloud The points. - * @return Enclosing ball radius for the points. - * \tparam Point_cloud must be a range of Kernel::Point_d points from CGAL. - * - */ - double operator()(const Point_cloud& point_cloud) const { - return std::sqrt(CGAL::to_double(kernel_.compute_squared_radius_d_object()(point_cloud.begin(), point_cloud.end()))); - } - -}; - -/** - * \class Cech_kernel - * \brief Cech complex kernel container. - * - * \details - * The Cech complex kernel container stores CGAL Kernel and dispatch basic computations. - */ - -// template < typename Kernel > -// class Cech_kernel { -// private: -// // Kernel for functions access. -// Kernel kernel_; -// public: -// using Point_d = typename Kernel::Point_d; -// // Numeric type of coordinates in the kernel -// using FT = typename Kernel::FT; -// // Sphere is a pair of point and squared radius. -// using Sphere = typename std::pair; -// -// int get_dimension(const Point_d& p0) const { -// return kernel_.point_dimension_d_object()(p0); -// } -// -// template -// Sphere get_sphere(PointIterator begin, PointIterator end) const { -// Point_d c = kernel_.construct_circumcenter_d_object()(begin, end); -// FT r = kernel_.squared_distance_d_object()(c, *begin); -// return std::make_pair(std::move(c), std::move(r)); -// } -// -// template -// FT get_squared_radius(PointIterator begin, PointIterator end) const { -// return kernel_.compute_squared_radius_d_object()(begin, end); -// } -// -// FT get_squared_radius(const Sphere& sph) const { -// return sph.second; -// } -// }; - - -//} // namespace cech_complex - -// namespace cechcomplex = cech_complex; - -} // namespace Gudhi - -#endif // CECH_KERNEL_H_ diff --git a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h index f7f86534..1a696422 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h +++ b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h @@ -31,17 +31,15 @@ namespace cech_complex { * \details * Čech blocker is an oracle constructed from a Cech_complex and a simplicial complex. * - * \tparam SimplicialComplexForProximityGraph furnishes `Simplex_handle` and `Filtration_value` type definition, + * \tparam SimplicialComplexForCech furnishes `Simplex_handle` and `Filtration_value` type definition, * `simplex_vertex_range(Simplex_handle sh)`and `assign_filtration(Simplex_handle sh, Filtration_value filt)` methods. * - * \tparam Chech_complex is required by the blocker. + * \tparam Cech_complex is required by the blocker. + * + * \tparam Kernel CGAL kernel. */ template class Cech_blocker { - private: - - using Simplex_handle = typename SimplicialComplexForCech::Simplex_handle; - using Filtration_value = typename SimplicialComplexForCech::Filtration_value; public: @@ -51,10 +49,10 @@ class Cech_blocker { // Sphere is a pair of point and squared radius. using Sphere = typename std::pair; - template - FT get_squared_radius(PointIterator begin, PointIterator end) const { - return kernel_.compute_squared_radius_d_object()(begin, end); - } + private: + + using Simplex_handle = typename SimplicialComplexForCech::Simplex_handle; + using Filtration_value = typename SimplicialComplexForCech::Filtration_value; template Sphere get_sphere(PointIterator begin, PointIterator end) const { @@ -63,6 +61,7 @@ class Cech_blocker { return std::make_pair(std::move(c), std::move(r)); } + public: /** \internal \brief Čech complex blocker operator() - the oracle - assigns the filtration value from the simplex * radius and returns if the simplex expansion must be blocked. @@ -108,10 +107,11 @@ class Cech_blocker { if (kernel_.squared_distance_d_object()(sph.first, cc_ptr_->get_point(extra)) <= sph.second) { radius = std::sqrt(cast_to_double(sph.second)); #ifdef DEBUG_TRACES - std::clog << "circumcenter: " << sph.first << ", radius: " << radius << std::endl; + std::clog << "center: " << sph.first << ", radius: " << radius << std::endl; #endif // DEBUG_TRACES if (cast_to_double(sph.second) < cast_to_double(min_enclos_ball.second)) min_enclos_ball = sph; + break; } } // Get the minimal radius of all faces enclosing balls if exists diff --git a/src/Cech_complex/include/gudhi/sphere_circumradius.h b/src/Cech_complex/include/gudhi/sphere_circumradius.h new file mode 100644 index 00000000..a6dec3dc --- /dev/null +++ b/src/Cech_complex/include/gudhi/sphere_circumradius.h @@ -0,0 +1,62 @@ +/* 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): Hind Montassif + * + * Copyright (C) 2021 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#ifndef SPHERE_CIRCUMRADIUS_H_ +#define SPHERE_CIRCUMRADIUS_H_ + +#include // for #include + +#include // for std::sqrt +#include + +namespace Gudhi { + +namespace cech_complex { + +/** @brief Compute the circumradius of the sphere passing through points given by a range of coordinates. + * The points are assumed to have the same dimension. */ +template +class Sphere_circumradius { + private: + Kernel kernel_; + public: + using Point = typename Kernel::Point_d; + using Point_cloud = typename std::vector; + + /** \brief Circumradius of sphere passing through two points using CGAL. + * + * @param[in] point_1 + * @param[in] point_2 + * @return Sphere circumradius passing through two points. + * \tparam Point must be a Kernel::Point_d from CGAL. + * + */ + double operator()(const Point& point_1, const Point& point_2) const { + return std::sqrt(CGAL::to_double(kernel_.squared_distance_d_object()(point_1, point_2))) / 2.; + } + + /** \brief Circumradius of sphere passing through point cloud using CGAL. + * + * @param[in] point_cloud The points. + * @return Sphere circumradius passing through the points. + * \tparam Point_cloud must be a range of Kernel::Point_d points from CGAL. + * + */ + double operator()(const Point_cloud& point_cloud) const { + return std::sqrt(CGAL::to_double(kernel_.compute_squared_radius_d_object()(point_cloud.begin(), point_cloud.end()))); + } + +}; + +} // namespace cech_complex + +} // namespace Gudhi + +#endif // SPHERE_CIRCUMRADIUS_H_ diff --git a/src/Cech_complex/test/test_cech_complex.cpp b/src/Cech_complex/test/test_cech_complex.cpp index ca7a9778..4cf8b68f 100644 --- a/src/Cech_complex/test/test_cech_complex.cpp +++ b/src/Cech_complex/test/test_cech_complex.cpp @@ -22,7 +22,7 @@ // to construct Cech_complex from a OFF file of points #include #include -#include +#include #include #include // For EXACT or SAFE version @@ -36,7 +36,7 @@ using Point = typename Kernel::Point_d; using Point_cloud = std::vector; using Points_off_reader = Gudhi::Points_off_reader; -using Cech_complex = Gudhi::cech_complex::Cech_complex; +using Cech_complex = Gudhi::cech_complex::Cech_complex; BOOST_AUTO_TEST_CASE(Cech_complex_for_documentation) { // ---------------------------------------------------------------------------- @@ -108,11 +108,11 @@ BOOST_AUTO_TEST_CASE(Cech_complex_for_documentation) { std::clog << vertex << ","; vp.push_back(points.at(vertex)); } - std::clog << ") - distance =" << Gudhi::Minimal_enclosing_ball_radius()(vp.at(0), vp.at(1)) + std::clog << ") - distance =" << Gudhi::cech_complex::Sphere_circumradius()(vp.at(0), vp.at(1)) << " - filtration =" << st.filtration(f_simplex) << std::endl; BOOST_CHECK(vp.size() == 2); GUDHI_TEST_FLOAT_EQUALITY_CHECK(st.filtration(f_simplex), - Gudhi::Minimal_enclosing_ball_radius()(vp.at(0), vp.at(1))); + Gudhi::cech_complex::Sphere_circumradius()(vp.at(0), vp.at(1))); } } @@ -153,7 +153,7 @@ BOOST_AUTO_TEST_CASE(Cech_complex_for_documentation) { Simplex_tree::Filtration_value f1410 = st2.filtration(st2.find({1, 4, 10})); std::clog << "f1410= " << f1410 << std::endl; - // In this case, the computed sphere using CGAL kernel does not match the minimal enclosing ball; the filtration value check is therefore done against a hardcoded value + // In this case, the computed circumsphere using CGAL kernel does not match the minimal enclosing ball; the filtration value check is therefore done against a hardcoded value GUDHI_TEST_FLOAT_EQUALITY_CHECK(f1410, 1.); Point_cloud points469; diff --git a/src/Cech_complex/utilities/cech_persistence.cpp b/src/Cech_complex/utilities/cech_persistence.cpp index ccf63e3e..82992f2d 100644 --- a/src/Cech_complex/utilities/cech_persistence.cpp +++ b/src/Cech_complex/utilities/cech_persistence.cpp @@ -9,7 +9,7 @@ */ #include -#include +#include #include #include #include @@ -28,9 +28,8 @@ using Filtration_value = Simplex_tree::Filtration_value; using Kernel = CGAL::Epeck_d; using Point = typename Kernel::Point_d; -using Point_cloud = std::vector; using Points_off_reader = Gudhi::Points_off_reader; -using Cech_complex = Gudhi::cech_complex::Cech_complex; +using Cech_complex = Gudhi::cech_complex::Cech_complex; using Field_Zp = Gudhi::persistent_cohomology::Field_Zp; using Persistent_cohomology = Gudhi::persistent_cohomology::Persistent_cohomology; -- cgit v1.2.3 From fcc9fd9f01d3f9680afccac0c3aff894e6ea4ef3 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Fri, 11 Feb 2022 15:01:56 +0100 Subject: Update cech doc intro regarding code modification --- src/Cech_complex/doc/Intro_cech_complex.h | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/doc/Intro_cech_complex.h b/src/Cech_complex/doc/Intro_cech_complex.h index 698f9749..644fd6cc 100644 --- a/src/Cech_complex/doc/Intro_cech_complex.h +++ b/src/Cech_complex/doc/Intro_cech_complex.h @@ -28,7 +28,7 @@ namespace cech_complex { * simplicial complex constructed * from a proximity graph. The set of all simplices is filtered by the radius of their minimal enclosing ball. * - * The input shall be a point cloud in an Euclidean space. + * The input shall be a range of points where a point is defined as CGAL kernel Point_d. * * \remark For people only interested in the topology of the \ref cech_complex (for instance persistence), * \ref alpha_complex is equivalent to the \ref cech_complex and much smaller if you do not bound the radii. @@ -37,8 +37,7 @@ namespace cech_complex { * \subsection cechalgorithm Algorithm * * Cech_complex first builds a proximity graph from a point cloud. - * The filtration value of each edge of the `Gudhi::Proximity_graph` is computed from - * `Gudhi::Minimal_enclosing_ball_radius` function. + * The filtration value of each edge of the `Gudhi::Proximity_graph` is computed using CGAL kernel functions. * * All edges that have a filtration value strictly greater than a user given maximal radius value, \f$max\_radius\f$, * are not inserted into the complex. @@ -60,13 +59,6 @@ namespace cech_complex { * * \image html "cech_complex_representation.png" "Čech complex expansion" * - * The minimal ball radius computation is insured by - * - * the miniball software (V3.0) - Smallest Enclosing Balls of Points - and distributed with GUDHI. - * Please refer to - * - * the miniball software design description for more information about this computation. - * * This radius computation is the reason why the Cech_complex is taking much more time to be computed than the * \ref rips_complex but it offers more topological guarantees. * -- cgit v1.2.3 From f55f93ba971768441de005fde59802229a1e008f Mon Sep 17 00:00:00 2001 From: Hind-M Date: Fri, 11 Feb 2022 16:11:16 +0100 Subject: Set back original simple cech example from points --- src/Cech_complex/example/cech_complex_example_from_points.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/example/cech_complex_example_from_points.cpp b/src/Cech_complex/example/cech_complex_example_from_points.cpp index 38021e4a..034077eb 100644 --- a/src/Cech_complex/example/cech_complex_example_from_points.cpp +++ b/src/Cech_complex/example/cech_complex_example_from_points.cpp @@ -46,11 +46,11 @@ int main() { // ---------------------------------------------------------------------------- // Init of a Cech complex from points // ---------------------------------------------------------------------------- - Filtration_value max_radius = 100.; //100.; + Filtration_value max_radius = 1.; Cech_complex cech_complex_from_points(points, max_radius); Simplex_tree stree; - cech_complex_from_points.create_complex(stree, 6); //6 + cech_complex_from_points.create_complex(stree, 2); // ---------------------------------------------------------------------------- // Display information about the one skeleton Cech complex // ---------------------------------------------------------------------------- -- cgit v1.2.3 From cbba4bf2005ce129691d358a2d7475c5132e39e0 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Mon, 28 Feb 2022 08:41:38 +0100 Subject: changed doc + added tensorflow indexing --- src/python/doc/ls_simplex_tree_tflow_itf_ref.rst | 2 +- src/python/gudhi/tensorflow/cubical_layer.py | 2 +- src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py | 6 +++--- src/python/gudhi/tensorflow/rips_layer.py | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst b/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst index 56bb4492..b8518cdb 100644 --- a/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst +++ b/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst @@ -42,7 +42,7 @@ Example of gradient computed from lower-star filtration of a simplex tree .. testoutput:: [2 4] - [-1. 1.] + [-1. 1.] Documentation for LowerStarSimplexTreeLayer ------------------------------------------- diff --git a/src/python/gudhi/tensorflow/cubical_layer.py b/src/python/gudhi/tensorflow/cubical_layer.py index 99d02d66..369b0e54 100644 --- a/src/python/gudhi/tensorflow/cubical_layer.py +++ b/src/python/gudhi/tensorflow/cubical_layer.py @@ -35,7 +35,7 @@ def _Cubical(Xflat, Xdim, dimensions): class CubicalLayer(tf.keras.layers.Layer): """ - TensorFlow layer for computing cubical persistence out of a cubical complex + TensorFlow layer for computing the persistent homology of a cubical complex """ def __init__(self, dimensions, min_persistence=None, **kwargs): """ diff --git a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py index 8da1f7fe..cf7df6de 100644 --- a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py +++ b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py @@ -47,7 +47,7 @@ class LowerStarSimplexTreeLayer(tf.keras.layers.Layer): Constructor for the LowerStarSimplexTreeLayer class Parameters: - simplextree (gudhi.SimplexTree): underlying simplex tree. Its vertices MUST be named with integers from 0 to n = number of vertices + simplextree (gudhi.SimplexTree): underlying simplex tree. Its vertices MUST be named with integers from 0 to n = number of vertices. Note that its filtration values are modified in each call of the class. dimensions (List[int]): homology dimensions min_persistence (List[float]): minimum distance-to-diagonal of the points in the output persistence diagrams (default None, in which case 0. is used for all dimensions) """ @@ -76,8 +76,8 @@ class LowerStarSimplexTreeLayer(tf.keras.layers.Layer): essential_dgm = tf.reshape(tf.gather(filtration, indices[idx_dim][1]), [-1,1]) min_pers = self.min_persistence[idx_dim] if min_pers >= 0: - persistent_indices = np.argwhere(np.abs(finite_dgm[:,1]-finite_dgm[:,0]) > min_pers).ravel() - self.dgms.append((tf.gather(finite_dgm, indices=persistent_indices), essential_dgm)) + persistent_indices = tf.where(tf.math.abs(finite_dgm[:,1]-finite_dgm[:,0]) > min_pers) + self.dgms.append((tf.reshape(tf.gather(finite_dgm, indices=persistent_indices),[-1,2]), essential_dgm)) else: self.dgms.append((finite_dgm, essential_dgm)) return self.dgms diff --git a/src/python/gudhi/tensorflow/rips_layer.py b/src/python/gudhi/tensorflow/rips_layer.py index 88d501c1..7b5edfa3 100644 --- a/src/python/gudhi/tensorflow/rips_layer.py +++ b/src/python/gudhi/tensorflow/rips_layer.py @@ -83,8 +83,8 @@ class RipsLayer(tf.keras.layers.Layer): essential_dgm = tf.zeros([cur_idx[1].shape[0],1]) min_pers = self.min_persistence[idx_dim] if min_pers >= 0: - persistent_indices = np.argwhere(np.abs(finite_dgm[:,1]-finite_dgm[:,0]) > min_pers).ravel() - self.dgms.append((tf.gather(finite_dgm, indices=persistent_indices), essential_dgm)) + persistent_indices = tf.where(tf.math.abs(finite_dgm[:,1]-finite_dgm[:,0]) > min_pers) + self.dgms.append((tf.reshape(tf.gather(finite_dgm, indices=persistent_indices),[-1,2]), essential_dgm)) else: self.dgms.append((finite_dgm, essential_dgm)) return self.dgms -- cgit v1.2.3 From 2eabdd9afcd35a345a95bfde76b7a0f1ef545788 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Mon, 28 Feb 2022 11:04:50 +0100 Subject: fix rips test code --- src/python/doc/rips_complex_tflow_itf_ref.rst | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/python/doc/rips_complex_tflow_itf_ref.rst b/src/python/doc/rips_complex_tflow_itf_ref.rst index 104b0971..83421b2a 100644 --- a/src/python/doc/rips_complex_tflow_itf_ref.rst +++ b/src/python/doc/rips_complex_tflow_itf_ref.rst @@ -10,6 +10,11 @@ TensorFlow layer for Vietoris-Rips persistence Example of gradient computed from Vietoris-Rips persistence ----------------------------------------------------------- +.. testsetup:: + :hide: + import numpy + numpy.set_printoptions(precision=4) + .. testcode:: from gudhi.tensorflow import RipsLayer -- cgit v1.2.3 From af1bdcad5e8638ba9aa8b381aaabd5fc9cc5ae4e Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Wed, 2 Mar 2022 15:31:36 +0100 Subject: fix sphinx test for rips --- src/python/doc/rips_complex_tflow_itf_ref.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/python/doc/rips_complex_tflow_itf_ref.rst b/src/python/doc/rips_complex_tflow_itf_ref.rst index 83421b2a..6974f92d 100644 --- a/src/python/doc/rips_complex_tflow_itf_ref.rst +++ b/src/python/doc/rips_complex_tflow_itf_ref.rst @@ -11,7 +11,6 @@ Example of gradient computed from Vietoris-Rips persistence ----------------------------------------------------------- .. testsetup:: - :hide: import numpy numpy.set_printoptions(precision=4) @@ -30,6 +29,9 @@ Example of gradient computed from Vietoris-Rips persistence grads = tape.gradient(loss, [X]) print(grads[0].numpy()) +.. testcleanup:: + numpy.set_printoptions(precision=8) + .. testoutput:: [[-0.5 -0.5] -- cgit v1.2.3 From 159f869e76ef609858c6208a2b9d4cf069d9a163 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Wed, 2 Mar 2022 16:30:31 +0100 Subject: Missing empty lines in sphinx doc --- src/python/doc/rips_complex_tflow_itf_ref.rst | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/python/doc/rips_complex_tflow_itf_ref.rst b/src/python/doc/rips_complex_tflow_itf_ref.rst index 6974f92d..6c65c562 100644 --- a/src/python/doc/rips_complex_tflow_itf_ref.rst +++ b/src/python/doc/rips_complex_tflow_itf_ref.rst @@ -11,6 +11,7 @@ Example of gradient computed from Vietoris-Rips persistence ----------------------------------------------------------- .. testsetup:: + import numpy numpy.set_printoptions(precision=4) @@ -30,6 +31,7 @@ Example of gradient computed from Vietoris-Rips persistence print(grads[0].numpy()) .. testcleanup:: + numpy.set_printoptions(precision=8) .. testoutput:: -- cgit v1.2.3 From af74316148165cb01c9f28b8b05e1b9764e4579a Mon Sep 17 00:00:00 2001 From: Hind-M Date: Fri, 4 Mar 2022 14:05:15 +0100 Subject: Use Euclidean_distance instead of CGAL dependant Sphere_circumradius for Rips in Cech benchmark --- src/Cech_complex/benchmark/cech_complex_benchmark.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/benchmark/cech_complex_benchmark.cpp b/src/Cech_complex/benchmark/cech_complex_benchmark.cpp index b283e1a8..bf013a81 100644 --- a/src/Cech_complex/benchmark/cech_complex_benchmark.cpp +++ b/src/Cech_complex/benchmark/cech_complex_benchmark.cpp @@ -107,8 +107,7 @@ int main(int argc, char* argv[]) { std::clog << itr->path().stem() << ";"; std::clog << radius << ";"; Gudhi::Clock rips_clock("Rips computation"); - Rips_complex rips_complex_from_points(off_reader_cgal.get_point_cloud(), radius, - Gudhi::cech_complex::Sphere_circumradius()); + Rips_complex rips_complex_from_points(off_reader.get_point_cloud(), radius, Gudhi::Euclidean_distance()); Simplex_tree rips_stree; rips_complex_from_points.create_complex(rips_stree, p0.size() - 1); // ------------------------------------------ -- cgit v1.2.3 From d5e1760353e7d6ba66975a90f9a2768a48f0abf8 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Tue, 8 Mar 2022 15:27:19 +0100 Subject: Modify cech benchmark to include both Epick and Epeck cases --- .../benchmark/cech_complex_benchmark.cpp | 177 +++++++++++++-------- 1 file changed, 109 insertions(+), 68 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/benchmark/cech_complex_benchmark.cpp b/src/Cech_complex/benchmark/cech_complex_benchmark.cpp index bf013a81..9cf24542 100644 --- a/src/Cech_complex/benchmark/cech_complex_benchmark.cpp +++ b/src/Cech_complex/benchmark/cech_complex_benchmark.cpp @@ -18,6 +18,7 @@ #include #include +#include #include "boost/filesystem.hpp" // includes all needed Boost.Filesystem declarations @@ -32,10 +33,6 @@ using Point_cloud = std::vector; using Points_off_reader = Gudhi::Points_off_reader; using Proximity_graph = Gudhi::Proximity_graph; using Rips_complex = Gudhi::rips_complex::Rips_complex; -using Kernel = CGAL::Epick_d>; -using Point_cgal = typename Kernel::Point_d; -using Points_off_reader_cgal = Gudhi::Points_off_reader; -using Cech_complex = Gudhi::cech_complex::Cech_complex; class Minimal_enclosing_ball_radius { public: @@ -61,79 +58,123 @@ class Minimal_enclosing_ball_radius { } }; -int main(int argc, char* argv[]) { - std::string off_file_points = "tore3D_1307.off"; - Filtration_value threshold = 1e20; +enum distance_type { Euclidean_dist, Minimal_enclosing_ball_dist, CGAL_dist }; - // Extract the points from the file filepoints - Points_off_reader off_reader(off_file_points); - Points_off_reader_cgal off_reader_cgal(off_file_points); +template> +void benchmark_prox_graph(const std::string& off_file_points, const Filtration_value& threshold, const std::string& msg, distance_type dist = CGAL_dist) { + if (dist != CGAL_dist) { + std::cerr << "Error: when CGAL is used, the distance should be CGAL_dist" << std::endl; + exit(-1); + } + if (!use_cgal) { + std::cerr << "Warning: if kernel is given, CGAL will be used" << std::endl; + } + using Point_cgal = typename Kernel::Point_d; + using Points_off_reader_cgal = Gudhi::Points_off_reader; - Gudhi::Clock euclidean_clock("Gudhi::Euclidean_distance"); - // Compute the proximity graph of the points - Proximity_graph euclidean_prox_graph = Gudhi::compute_proximity_graph( - off_reader.get_point_cloud(), threshold, Gudhi::Euclidean_distance()); + // Extract the points from the file filepoints + Points_off_reader_cgal off_reader_cgal(off_file_points); - std::clog << euclidean_clock << std::endl; + Gudhi::Clock cgal_circumsphere_clock("Gudhi::cech_complex::Sphere_circumradius_cgal()"); + // Compute the proximity graph of the points + Proximity_graph cgal_circumsphere_prox_graph = Gudhi::compute_proximity_graph(off_reader_cgal.get_point_cloud(), threshold, + Gudhi::cech_complex::Sphere_circumradius()); + std::clog << msg << " - " << cgal_circumsphere_clock << std::endl; +} - Gudhi::Clock miniball_clock("Minimal_enclosing_ball_radius"); - // Compute the proximity graph of the points - Proximity_graph miniball_prox_graph = Gudhi::compute_proximity_graph( - off_reader.get_point_cloud(), threshold, Minimal_enclosing_ball_radius()); - std::clog << miniball_clock << std::endl; +template +void benchmark_prox_graph(const std::string& off_file_points, const Filtration_value& threshold, const std::string& msg, distance_type dist) { + // Extract the points from the file filepoints + Points_off_reader off_reader(off_file_points); + + if (dist == Euclidean_dist) { + Gudhi::Clock euclidean_clock("Gudhi::Euclidean_distance"); + // Compute the proximity graph of the points + Proximity_graph euclidean_prox_graph = Gudhi::compute_proximity_graph(off_reader.get_point_cloud(), threshold, + Gudhi::Euclidean_distance()); + std::clog << msg << " - " << euclidean_clock << std::endl; + } + else if (dist == Minimal_enclosing_ball_dist) { + Gudhi::Clock miniball_clock("Minimal_enclosing_ball_radius"); + // Compute the proximity graph of the points + Proximity_graph miniball_prox_graph = Gudhi::compute_proximity_graph(off_reader.get_point_cloud(), threshold, + Minimal_enclosing_ball_radius()); + std::clog << msg << " - " << miniball_clock << std::endl; + } + else { + std::cerr << "Error: when CGAL is not used, the distance should be either Euclidean_dist or Minimal_enclosing_ball_dist" << std::endl; + exit(-1); + } +} + +template +void benchmark_cech(const std::string& off_file_points, const Filtration_value& radius, const int& dim_max) { + using Point_cgal = typename Kernel::Point_d; + using Points_off_reader_cgal = Gudhi::Points_off_reader; + using Cech_complex = Gudhi::cech_complex::Cech_complex; + + // Extract the points from the file filepoints + Points_off_reader_cgal off_reader_cgal(off_file_points); + + Gudhi::Clock cech_clock("Cech computation"); + Cech_complex cech_complex_from_points(off_reader_cgal.get_point_cloud(), radius); + Simplex_tree cech_stree; + cech_complex_from_points.create_complex(cech_stree, dim_max); + + // ------------------------------------------ + // Display information about the Cech complex + // ------------------------------------------ + double cech_sec = cech_clock.num_seconds(); + std::clog << cech_sec << " ; "; + std::clog << cech_stree.num_simplices() << " ; "; +} - Gudhi::Clock cgal_circumsphere_clock("Gudhi::cech_complex::Sphere_circumradius_cgal()"); - // Compute the proximity graph of the points - Proximity_graph cgal_circumsphere_prox_graph = Gudhi::compute_proximity_graph( - off_reader_cgal.get_point_cloud(), threshold, Gudhi::cech_complex::Sphere_circumradius()); - std::clog << cgal_circumsphere_clock << std::endl; +int main(int argc, char* argv[]) { + std::string off_file_points = "tore3D_1307.off"; + Filtration_value threshold = 1e20; + + benchmark_prox_graph(off_file_points, threshold, "Euclidean distance", Euclidean_dist); + benchmark_prox_graph(off_file_points, threshold, "Minimal_enclosing_ball", Minimal_enclosing_ball_dist); + benchmark_prox_graph>>(off_file_points, threshold, "Epick"); + benchmark_prox_graph>>(off_file_points, threshold, "Epeck"); - boost::filesystem::path full_path(boost::filesystem::current_path()); - std::clog << "Current path is : " << full_path << std::endl; + boost::filesystem::path full_path(boost::filesystem::current_path()); + std::clog << "Current path is : " << full_path << std::endl; - std::clog << "File name; Radius; Rips time; Cech time; Ratio Rips/Cech time; Rips nb simplices; Cech nb simplices;" + std::clog << "File name ; Radius ; Rips time ; Epick Cech time ; Epick Cech nb simplices ; Epeck Cech time ; Epeck Cech nb simplices ; Rips nb simplices;" << std::endl; - boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end - for (boost::filesystem::directory_iterator itr(boost::filesystem::current_path()); itr != end_itr; ++itr) { - if (!boost::filesystem::is_directory(itr->status())) { - if (itr->path().extension() == ".off") // see below - { - Points_off_reader off_reader(itr->path().string()); - Points_off_reader_cgal off_reader_cgal(itr->path().string()); - - Point p0 = off_reader.get_point_cloud()[0]; - - for (Filtration_value radius = 0.1; radius < 0.4; radius += 0.1) { - std::clog << itr->path().stem() << ";"; - std::clog << radius << ";"; - Gudhi::Clock rips_clock("Rips computation"); - Rips_complex rips_complex_from_points(off_reader.get_point_cloud(), radius, Gudhi::Euclidean_distance()); - Simplex_tree rips_stree; - rips_complex_from_points.create_complex(rips_stree, p0.size() - 1); - // ------------------------------------------ - // Display information about the Rips complex - // ------------------------------------------ - double rips_sec = rips_clock.num_seconds(); - std::clog << rips_sec << ";"; - - Gudhi::Clock cech_clock("Cech computation"); - Cech_complex cech_complex_from_points(off_reader_cgal.get_point_cloud(), radius); - Simplex_tree cech_stree; - cech_complex_from_points.create_complex(cech_stree, p0.size() - 1); - // ------------------------------------------ - // Display information about the Cech complex - // ------------------------------------------ - double cech_sec = cech_clock.num_seconds(); - std::clog << cech_sec << ";"; - std::clog << cech_sec / rips_sec << ";"; - - assert(rips_stree.num_simplices() >= cech_stree.num_simplices()); - std::clog << rips_stree.num_simplices() << ";"; - std::clog << cech_stree.num_simplices() << ";" << std::endl; + boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end + for (boost::filesystem::directory_iterator itr(boost::filesystem::current_path()); itr != end_itr; ++itr) { + if (!boost::filesystem::is_directory(itr->status())) { + if (itr->path().extension() == ".off") { + Points_off_reader off_reader(itr->path().string()); + Point p0 = off_reader.get_point_cloud()[0]; + + for (Filtration_value radius = 0.1; radius < 0.4; radius += 0.1) { + std::clog << itr->path().stem() << " ; "; + std::clog << radius << " ; "; + + Gudhi::Clock rips_clock("Rips computation"); + Rips_complex rips_complex_from_points(off_reader.get_point_cloud(), radius, Gudhi::Euclidean_distance()); + Simplex_tree rips_stree; + rips_complex_from_points.create_complex(rips_stree, p0.size() - 1); + // ------------------------------------------ + // Display information about the Rips complex + // ------------------------------------------ + double rips_sec = rips_clock.num_seconds(); + std::clog << rips_sec << " ; "; + + // -------------- + // Cech complex + // -------------- + benchmark_cech>>(itr->path().string(), radius, p0.size() - 1); + benchmark_cech>>(itr->path().string(), radius, p0.size() - 1); + + std::clog << rips_stree.num_simplices() << ";" << std::endl; + } + } } - } } - } - return 0; + return 0; } -- cgit v1.2.3 From 27f8df308e3ed935e4ef9f62d23717efebdf36ae Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Tue, 12 Apr 2022 15:21:02 +0200 Subject: fix doc + reshape in cubical --- ext/gudhi-deploy | 2 +- src/python/doc/cubical_complex_tflow_itf_ref.rst | 2 +- src/python/gudhi/tensorflow/cubical_layer.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/ext/gudhi-deploy b/ext/gudhi-deploy index 290ade10..975d1bff 160000 --- a/ext/gudhi-deploy +++ b/ext/gudhi-deploy @@ -1 +1 @@ -Subproject commit 290ade1086bedbc96a35df886cadecabbf4072e6 +Subproject commit 975d1bffb317f3b84bf1a3d576cdfdbf7b45861c diff --git a/src/python/doc/cubical_complex_tflow_itf_ref.rst b/src/python/doc/cubical_complex_tflow_itf_ref.rst index 18b97adf..881a2950 100644 --- a/src/python/doc/cubical_complex_tflow_itf_ref.rst +++ b/src/python/doc/cubical_complex_tflow_itf_ref.rst @@ -19,7 +19,7 @@ Example of gradient computed from cubical persistence cl = CubicalLayer(dimensions=[0]) with tf.GradientTape() as tape: - dgm = cl.call(X)[0][0] + dgm = cl.call(X)[0] loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) grads = tape.gradient(loss, [X]) diff --git a/src/python/gudhi/tensorflow/cubical_layer.py b/src/python/gudhi/tensorflow/cubical_layer.py index 369b0e54..31c44205 100644 --- a/src/python/gudhi/tensorflow/cubical_layer.py +++ b/src/python/gudhi/tensorflow/cubical_layer.py @@ -72,5 +72,5 @@ class CubicalLayer(tf.keras.layers.Layer): if min_pers >= 0: finite_dgm = self.dgms[idx_dim] persistent_indices = tf.where(tf.math.abs(finite_dgm[:,1]-finite_dgm[:,0]) > min_pers) - self.dgms[idx_dim] = tf.gather(finite_dgm, indices=persistent_indices) + self.dgms[idx_dim] = tf.reshape(tf.gather(finite_dgm, indices=persistent_indices), [-1,2]) return self.dgms -- cgit v1.2.3 From cc723a7a3735a44491bd1085b6bb6c47272b73ed Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Sat, 16 Apr 2022 11:21:09 +0200 Subject: fix test --- src/python/test/test_diff.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/python/test/test_diff.py b/src/python/test/test_diff.py index bab0d10c..e0c99d07 100644 --- a/src/python/test/test_diff.py +++ b/src/python/test/test_diff.py @@ -22,7 +22,7 @@ def test_cubical_diff(): cl = CubicalLayer(dimensions=[0]) with tf.GradientTape() as tape: - dgm = cl.call(X)[0][0] + dgm = cl.call(X)[0] loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) grads = tape.gradient(loss, [X]) assert np.abs(grads[0].numpy()-np.array([[0.,0.,0.],[0.,.5,0.],[0.,0.,-.5]])).sum() <= 1e-6 @@ -34,7 +34,7 @@ def test_nonsquare_cubical_diff(): cl = CubicalLayer(dimensions=[0]) with tf.GradientTape() as tape: - dgm = cl.call(X)[0][0] + dgm = cl.call(X)[0] loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) grads = tape.gradient(loss, [X]) assert np.abs(grads[0].numpy()-np.array([[0.,0.5,-0.5],[0.,0.,0.]])).sum() <= 1e-6 -- cgit v1.2.3 From dbb65c3f3eb82d080e47b40b52deb03814d8da31 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Mon, 25 Apr 2022 13:18:11 +0200 Subject: Remove proximity_graph computation benchmark Add Dynamic_dimension_tag case --- .../benchmark/cech_complex_benchmark.cpp | 97 ++-------------------- 1 file changed, 8 insertions(+), 89 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/benchmark/cech_complex_benchmark.cpp b/src/Cech_complex/benchmark/cech_complex_benchmark.cpp index 9cf24542..d2a71879 100644 --- a/src/Cech_complex/benchmark/cech_complex_benchmark.cpp +++ b/src/Cech_complex/benchmark/cech_complex_benchmark.cpp @@ -10,12 +10,10 @@ #include #include -#include #include #include #include #include -#include #include #include @@ -29,86 +27,11 @@ using Simplex_tree = Gudhi::Simplex_tree<>; using Filtration_value = Simplex_tree::Filtration_value; using Point = std::vector; -using Point_cloud = std::vector; using Points_off_reader = Gudhi::Points_off_reader; -using Proximity_graph = Gudhi::Proximity_graph; using Rips_complex = Gudhi::rips_complex::Rips_complex; -class Minimal_enclosing_ball_radius { - public: - // boost::range_value is not SFINAE-friendly so we cannot use it in the return type - template - typename std::iterator_traits::type>::value_type operator()( - const Point& p1, const Point& p2) const { - // Type def - using Point_cloud = std::vector; - using Point_iterator = typename Point_cloud::const_iterator; - using Coordinate_iterator = typename Point::const_iterator; - using Min_sphere = - typename Gudhi::Miniball::Miniball>; - - Point_cloud point_cloud; - point_cloud.push_back(p1); - point_cloud.push_back(p2); - - GUDHI_CHECK((p1.end() - p1.begin()) == (p2.end() - p2.begin()), "inconsistent point dimensions"); - Min_sphere min_sphere(p1.end() - p1.begin(), point_cloud.begin(), point_cloud.end()); - - return std::sqrt(min_sphere.squared_radius()); - } -}; - -enum distance_type { Euclidean_dist, Minimal_enclosing_ball_dist, CGAL_dist }; - -template> -void benchmark_prox_graph(const std::string& off_file_points, const Filtration_value& threshold, const std::string& msg, distance_type dist = CGAL_dist) { - if (dist != CGAL_dist) { - std::cerr << "Error: when CGAL is used, the distance should be CGAL_dist" << std::endl; - exit(-1); - } - if (!use_cgal) { - std::cerr << "Warning: if kernel is given, CGAL will be used" << std::endl; - } - using Point_cgal = typename Kernel::Point_d; - using Points_off_reader_cgal = Gudhi::Points_off_reader; - - // Extract the points from the file filepoints - Points_off_reader_cgal off_reader_cgal(off_file_points); - - Gudhi::Clock cgal_circumsphere_clock("Gudhi::cech_complex::Sphere_circumradius_cgal()"); - // Compute the proximity graph of the points - Proximity_graph cgal_circumsphere_prox_graph = Gudhi::compute_proximity_graph(off_reader_cgal.get_point_cloud(), threshold, - Gudhi::cech_complex::Sphere_circumradius()); - std::clog << msg << " - " << cgal_circumsphere_clock << std::endl; -} - -template -void benchmark_prox_graph(const std::string& off_file_points, const Filtration_value& threshold, const std::string& msg, distance_type dist) { - // Extract the points from the file filepoints - Points_off_reader off_reader(off_file_points); - - if (dist == Euclidean_dist) { - Gudhi::Clock euclidean_clock("Gudhi::Euclidean_distance"); - // Compute the proximity graph of the points - Proximity_graph euclidean_prox_graph = Gudhi::compute_proximity_graph(off_reader.get_point_cloud(), threshold, - Gudhi::Euclidean_distance()); - std::clog << msg << " - " << euclidean_clock << std::endl; - } - else if (dist == Minimal_enclosing_ball_dist) { - Gudhi::Clock miniball_clock("Minimal_enclosing_ball_radius"); - // Compute the proximity graph of the points - Proximity_graph miniball_prox_graph = Gudhi::compute_proximity_graph(off_reader.get_point_cloud(), threshold, - Minimal_enclosing_ball_radius()); - std::clog << msg << " - " << miniball_clock << std::endl; - } - else { - std::cerr << "Error: when CGAL is not used, the distance should be either Euclidean_dist or Minimal_enclosing_ball_dist" << std::endl; - exit(-1); - } -} - template -void benchmark_cech(const std::string& off_file_points, const Filtration_value& radius, const int& dim_max) { +Simplex_tree benchmark_cech(const std::string& off_file_points, const Filtration_value& radius, const int& dim_max) { using Point_cgal = typename Kernel::Point_d; using Points_off_reader_cgal = Gudhi::Points_off_reader; using Cech_complex = Gudhi::cech_complex::Cech_complex; @@ -126,23 +49,16 @@ void benchmark_cech(const std::string& off_file_points, const Filtration_value& // ------------------------------------------ double cech_sec = cech_clock.num_seconds(); std::clog << cech_sec << " ; "; - std::clog << cech_stree.num_simplices() << " ; "; + return cech_stree; } int main(int argc, char* argv[]) { - std::string off_file_points = "tore3D_1307.off"; - Filtration_value threshold = 1e20; - - benchmark_prox_graph(off_file_points, threshold, "Euclidean distance", Euclidean_dist); - benchmark_prox_graph(off_file_points, threshold, "Minimal_enclosing_ball", Minimal_enclosing_ball_dist); - benchmark_prox_graph>>(off_file_points, threshold, "Epick"); - benchmark_prox_graph>>(off_file_points, threshold, "Epeck"); - boost::filesystem::path full_path(boost::filesystem::current_path()); std::clog << "Current path is : " << full_path << std::endl; - std::clog << "File name ; Radius ; Rips time ; Epick Cech time ; Epick Cech nb simplices ; Epeck Cech time ; Epeck Cech nb simplices ; Rips nb simplices;" - << std::endl; + std::clog << "File name ; Radius ; Rips time ; Dim-3 Epick Cech time ; Dynamic_dim Epick Cech time ; " + "Dim-3 Epeck Cech time ; Dynamic_dim Epeck Cech time ; Cech nb simplices ; Rips nb simplices;" + << std::endl; boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end for (boost::filesystem::directory_iterator itr(boost::filesystem::current_path()); itr != end_itr; ++itr) { if (!boost::filesystem::is_directory(itr->status())) { @@ -168,8 +84,11 @@ int main(int argc, char* argv[]) { // Cech complex // -------------- benchmark_cech>>(itr->path().string(), radius, p0.size() - 1); + benchmark_cech>(itr->path().string(), radius, p0.size() - 1); benchmark_cech>>(itr->path().string(), radius, p0.size() - 1); + auto cech_stree = benchmark_cech>(itr->path().string(), radius, p0.size() - 1); + std::clog << cech_stree.num_simplices() << " ; "; std::clog << rips_stree.num_simplices() << ";" << std::endl; } } -- cgit v1.2.3 From 8730db2e8d1a8663358168ff6a20881c97773002 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Mon, 25 Apr 2022 14:57:26 +0200 Subject: Remove cech_complex_step_by_step example and Miniball --- src/Cech_complex/doc/Intro_cech_complex.h | 4 - src/Cech_complex/example/CMakeLists.txt | 10 - .../example/cech_complex_step_by_step.cpp | 150 ------ src/Cech_complex/include/gudhi/Miniball.COPYRIGHT | 4 - src/Cech_complex/include/gudhi/Miniball.README | 26 - src/Cech_complex/include/gudhi/Miniball.hpp | 523 --------------------- src/common/doc/examples.h | 1 - src/common/doc/main_page.md | 2 +- 8 files changed, 1 insertion(+), 719 deletions(-) delete mode 100644 src/Cech_complex/example/cech_complex_step_by_step.cpp delete mode 100644 src/Cech_complex/include/gudhi/Miniball.COPYRIGHT delete mode 100644 src/Cech_complex/include/gudhi/Miniball.README delete mode 100644 src/Cech_complex/include/gudhi/Miniball.hpp (limited to 'src') diff --git a/src/Cech_complex/doc/Intro_cech_complex.h b/src/Cech_complex/doc/Intro_cech_complex.h index 644fd6cc..595fb64b 100644 --- a/src/Cech_complex/doc/Intro_cech_complex.h +++ b/src/Cech_complex/doc/Intro_cech_complex.h @@ -62,10 +62,6 @@ namespace cech_complex { * This radius computation is the reason why the Cech_complex is taking much more time to be computed than the * \ref rips_complex but it offers more topological guarantees. * - * If the Cech_complex interfaces are not detailed enough for your need, please refer to - * - * cech_complex_step_by_step.cpp example, where the graph construction over the Simplex_tree is more detailed. - * * \subsection cechpointscloudexample Example from a point cloud * * This example builds the proximity graph from the given points, and maximal radius values. diff --git a/src/Cech_complex/example/CMakeLists.txt b/src/Cech_complex/example/CMakeLists.txt index 4d11ace2..7d52ed5e 100644 --- a/src/Cech_complex/example/CMakeLists.txt +++ b/src/Cech_complex/example/CMakeLists.txt @@ -1,16 +1,6 @@ project(Cech_complex_examples) if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 5.0.1) - if (TARGET Boost::program_options) - add_executable ( Cech_complex_example_step_by_step cech_complex_step_by_step.cpp ) - target_link_libraries(Cech_complex_example_step_by_step Boost::program_options) - if (TBB_FOUND) - target_link_libraries(Cech_complex_example_step_by_step ${TBB_LIBRARIES}) - endif() - add_test(NAME Cech_complex_utility_from_rips_on_tore_3D COMMAND $ - "${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off" "-r" "0.25" "-d" "3") - endif() - add_executable ( Cech_complex_example_from_points cech_complex_example_from_points.cpp) if (TBB_FOUND) target_link_libraries(Cech_complex_example_from_points ${TBB_LIBRARIES}) diff --git a/src/Cech_complex/example/cech_complex_step_by_step.cpp b/src/Cech_complex/example/cech_complex_step_by_step.cpp deleted file mode 100644 index 4401f6af..00000000 --- a/src/Cech_complex/example/cech_complex_step_by_step.cpp +++ /dev/null @@ -1,150 +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): Vincent Rouvreau - * - * Copyright (C) 2018 Inria - * - * Modification(s): - * - YYYY/MM Author: Description of the modification - */ - -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include // infinity -#include // for pair -#include - -// ---------------------------------------------------------------------------- -// cech_complex_step_by_step is an example of each step that is required to -// build a Cech over a Simplex_tree. Please refer to cech_complex_example_from_points to see -// how to do the same thing with the Cech complex wrapper for less detailed -// steps. -// ---------------------------------------------------------------------------- - -// Types definition -using Simplex_tree = Gudhi::Simplex_tree<>; -using Simplex_handle = Simplex_tree::Simplex_handle; -using Filtration_value = Simplex_tree::Filtration_value; -using Kernel = CGAL::Epeck_d; -using Point = typename Kernel::Point_d; -using Points_off_reader = Gudhi::Points_off_reader; -using Proximity_graph = Gudhi::Proximity_graph; - -class Cech_blocker { - private: - using Point_cloud = std::vector; - - public: - bool operator()(Simplex_handle sh) { - std::vector points; - for (auto vertex : simplex_tree_.simplex_vertex_range(sh)) { - points.push_back(point_cloud_[vertex]); -#ifdef DEBUG_TRACES - std::clog << "#(" << vertex << ")#"; -#endif // DEBUG_TRACES - } - Filtration_value radius = Gudhi::cech_complex::Sphere_circumradius()(points); -#ifdef DEBUG_TRACES - std::clog << "radius = " << radius << " - " << (radius > max_radius_) << std::endl; -#endif // DEBUG_TRACES - simplex_tree_.assign_filtration(sh, radius); - return (radius > max_radius_); - } - Cech_blocker(Simplex_tree& simplex_tree, Filtration_value max_radius, const std::vector& point_cloud) - : simplex_tree_(simplex_tree), max_radius_(max_radius), point_cloud_(point_cloud) { - } - - private: - Simplex_tree simplex_tree_; - Filtration_value max_radius_; - std::vector point_cloud_; -}; - -void program_options(int argc, char* argv[], std::string& off_file_points, Filtration_value& max_radius, int& dim_max); - -int main(int argc, char* argv[]) { - std::string off_file_points; - Filtration_value max_radius; - int dim_max; - - program_options(argc, argv, off_file_points, max_radius, dim_max); - - // Extract the points from the file filepoints - Points_off_reader off_reader(off_file_points); - - // Compute the proximity graph of the points - Proximity_graph prox_graph = Gudhi::compute_proximity_graph(off_reader.get_point_cloud(), max_radius, - Gudhi::cech_complex::Sphere_circumradius()); - - // Construct the Cech complex in a Simplex Tree - Simplex_tree st; - // insert the proximity graph in the simplex tree - st.insert_graph(prox_graph); - // expand the graph until dimension dim_max - st.expansion_with_blockers(dim_max, Cech_blocker(st, max_radius, off_reader.get_point_cloud())); - - std::clog << "The complex contains " << st.num_simplices() << " simplices \n"; - std::clog << " and has dimension " << st.dimension() << " \n"; - - // Sort the simplices in the order of the filtration - st.initialize_filtration(); - -#if DEBUG_TRACES - std::clog << "********************************************************************\n"; - std::clog << "* The complex contains " << st.num_simplices() << " simplices - dimension=" << st.dimension() << "\n"; - std::clog << "* Iterator on Simplices in the filtration, with [filtration value]:\n"; - for (auto f_simplex : st.filtration_simplex_range()) { - std::clog << " " - << "[" << st.filtration(f_simplex) << "] "; - for (auto vertex : st.simplex_vertex_range(f_simplex)) { - std::clog << static_cast(vertex) << " "; - } - std::clog << std::endl; - } -#endif // DEBUG_TRACES - - return 0; -} - -void program_options(int argc, char* argv[], std::string& off_file_points, Filtration_value& max_radius, int& dim_max) { - namespace po = boost::program_options; - po::options_description hidden("Hidden options"); - hidden.add_options()("input-file", po::value(&off_file_points), - "Name of an OFF file containing a point set.\n"); - - po::options_description visible("Allowed options", 100); - visible.add_options()("help,h", "produce help message")( - "max-radius,r", - po::value(&max_radius)->default_value(std::numeric_limits::infinity()), - "Maximal length of an edge for the Cech complex construction.")( - "cpx-dimension,d", po::value(&dim_max)->default_value(1), - "Maximal dimension of the Cech complex we want to compute."); - - po::positional_options_description pos; - pos.add("input-file", 1); - - po::options_description all; - all.add(visible).add(hidden); - - po::variables_map vm; - po::store(po::command_line_parser(argc, argv).options(all).positional(pos).run(), vm); - po::notify(vm); - - if (vm.count("help") || !vm.count("input-file")) { - std::clog << std::endl; - std::clog << "Construct a Cech complex defined on a set of input points.\n \n"; - - std::clog << "Usage: " << argv[0] << " [options] input-file" << std::endl << std::endl; - std::clog << visible << std::endl; - exit(-1); - } -} diff --git a/src/Cech_complex/include/gudhi/Miniball.COPYRIGHT b/src/Cech_complex/include/gudhi/Miniball.COPYRIGHT deleted file mode 100644 index dbe4c553..00000000 --- a/src/Cech_complex/include/gudhi/Miniball.COPYRIGHT +++ /dev/null @@ -1,4 +0,0 @@ -The miniball software is available under the GNU General Public License (GPLv3 - https://www.gnu.org/copyleft/gpl.html). -If your intended use is not compliant with this license, please buy a commercial license (EUR 500 - https://people.inf.ethz.ch/gaertner/subdir/software/miniball/license.html). -You need a license if the software that you develop using Miniball V3.0 is not open source. - diff --git a/src/Cech_complex/include/gudhi/Miniball.README b/src/Cech_complex/include/gudhi/Miniball.README deleted file mode 100644 index 033d8953..00000000 --- a/src/Cech_complex/include/gudhi/Miniball.README +++ /dev/null @@ -1,26 +0,0 @@ -https://people.inf.ethz.ch/gaertner/subdir/software/miniball.html - -Smallest Enclosing Balls of Points - Fast and Robust in C++. -(high-quality software for smallest enclosing balls of balls is available in the computational geometry algorithms library CGAL) - - -This is the miniball software (V3.0) for computing smallest enclosing balls of points in arbitrary dimensions. It consists of a C++ header file Miniball.hpp (around 500 lines of code) and two example programs miniball_example.cpp and miniball_example_containers.cpp that demonstrate the usage. The first example stores the coordinates of the input points in a two-dimensional array, the second example uses a list of vectors to show how generic containers can be used. - -Credits: Aditya Gupta and Alexandros Konstantinakis-Karmis have significantly contributed to this version of the software. - -Changes - https://people.inf.ethz.ch/gaertner/subdir/software/miniball/changes.txt - from previous versions. - -The theory - https://people.inf.ethz.ch/gaertner/subdir/texts/own_work/esa99_final.pdf - behind the miniball software (Proc. 7th Annual European Symposium on Algorithms (ESA), Lecture Notes in Computer Science 1643, Springer-Verlag, pp.325-338, 1999). - -Main Features: - - Very fast in low dimensions. 1 million points in 5-space are processed within 0.05 seconds on any recent machine. - - High numerical stability. Almost all input degeneracies (cospherical points, multiple points, points very close together) are routinely handled. - - Easily integrates into your code. You can freely choose the coordinate type of your points and the container to store the points. If you still need to adapt the code, the header is small and readable and contains documentation for all major methods. - - -Changes done for the GUDHI version of MiniBall: - - Add include guard - - Move Miniball namespace inside a new Gudhi namespace diff --git a/src/Cech_complex/include/gudhi/Miniball.hpp b/src/Cech_complex/include/gudhi/Miniball.hpp deleted file mode 100644 index ce6cbb5b..00000000 --- a/src/Cech_complex/include/gudhi/Miniball.hpp +++ /dev/null @@ -1,523 +0,0 @@ -// Copright (C) 1999-2013, Bernd Gaertner -// $Rev: 3581 $ -// -// 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 -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// Contact: -// -------- -// Bernd Gaertner -// Institute of Theoretical Computer Science -// ETH Zuerich -// CAB G31.1 -// CH-8092 Zuerich, Switzerland -// http://www.inf.ethz.ch/personal/gaertner - -#ifndef MINIBALL_HPP_ -#define MINIBALL_HPP_ - -#include -#include -#include -#include -#include - -namespace Gudhi { - -namespace Miniball { - - // Global Functions - // ================ - template - inline NT mb_sqr (NT r) {return r*r;} - - // Functors - // ======== - - // functor to map a point iterator to the corresponding coordinate iterator; - // generic version for points whose coordinate containers have begin() - template < typename Pit_, typename Cit_ > - struct CoordAccessor { - typedef Pit_ Pit; - typedef Cit_ Cit; - inline Cit operator() (Pit it) const { return (*it).begin(); } - }; - - // partial specialization for points whose coordinate containers are arrays - template < typename Pit_, typename Cit_ > - struct CoordAccessor { - typedef Pit_ Pit; - typedef Cit_* Cit; - inline Cit operator() (Pit it) const { return *it; } - }; - - // Class Declaration - // ================= - - template - class Miniball { - private: - // types - // The iterator type to go through the input points - typedef typename CoordAccessor::Pit Pit; - // The iterator type to go through the coordinates of a single point. - typedef typename CoordAccessor::Cit Cit; - // The coordinate type - typedef typename std::iterator_traits::value_type NT; - // The iterator to go through the support points - typedef typename std::list::iterator Sit; - - // data members... - const int d; // dimension - Pit points_begin; - Pit points_end; - CoordAccessor coord_accessor; - double time; - const NT nt0; // NT(0) - - //...for the algorithms - std::list L; - Sit support_end; - int fsize; // number of forced points - int ssize; // number of support points - - // ...for the ball updates - NT* current_c; - NT current_sqr_r; - NT** c; - NT* sqr_r; - - // helper arrays - NT* q0; - NT* z; - NT* f; - NT** v; - NT** a; - - public: - // The iterator type to go through the support points - typedef typename std::list::const_iterator SupportPointIterator; - - // PRE: [begin, end) is a nonempty range - // POST: computes the smallest enclosing ball of the points in the range - // [begin, end); the functor a maps a point iterator to an iterator - // through the d coordinates of the point - Miniball (int d_, Pit begin, Pit end, CoordAccessor ca = CoordAccessor()); - - // POST: returns a pointer to the first element of an array that holds - // the d coordinates of the center of the computed ball - const NT* center () const; - - // POST: returns the squared radius of the computed ball - NT squared_radius () const; - - // POST: returns the number of support points of the computed ball; - // the support points form a minimal set with the same smallest - // enclosing ball as the input set; in particular, the support - // points are on the boundary of the computed ball, and their - // number is at most d+1 - int nr_support_points () const; - - // POST: returns an iterator to the first support point - SupportPointIterator support_points_begin () const; - - // POST: returns a past-the-end iterator for the range of support points - SupportPointIterator support_points_end () const; - - // POST: returns the maximum excess of any input point w.r.t. the computed - // ball, divided by the squared radius of the computed ball. The - // excess of a point is the difference between its squared distance - // from the center and the squared radius; Ideally, the return value - // is 0. subopt is set to the absolute value of the most negative - // coefficient in the affine combination of the support points that - // yields the center. Ideally, this is a convex combination, and there - // is no negative coefficient in which case subopt is set to 0. - NT relative_error (NT& subopt) const; - - // POST: return true if the relative error is at most tol, and the - // suboptimality is 0; the default tolerance is 10 times the - // coordinate type's machine epsilon - bool is_valid (NT tol = NT(10) * std::numeric_limits::epsilon()) const; - - // POST: returns the time in seconds taken by the constructor call for - // computing the smallest enclosing ball - double get_time() const; - - // POST: deletes dynamically allocated arrays - ~Miniball(); - - private: - void mtf_mb (Sit n); - void mtf_move_to_front (Sit j); - void pivot_mb (Pit n); - void pivot_move_to_front (Pit j); - NT excess (Pit pit) const; - void pop (); - bool push (Pit pit); - NT suboptimality () const; - void create_arrays(); - void delete_arrays(); - }; - - // Class Definition - // ================ - template - Miniball::Miniball (int d_, Pit begin, Pit end, - CoordAccessor ca) - : d (d_), - points_begin (begin), - points_end (end), - coord_accessor (ca), - time (clock()), - nt0 (NT(0)), - L(), - support_end (L.begin()), - fsize(0), - ssize(0), - current_c (NULL), - current_sqr_r (NT(-1)), - c (NULL), - sqr_r (NULL), - q0 (NULL), - z (NULL), - f (NULL), - v (NULL), - a (NULL) - { - assert (points_begin != points_end); - create_arrays(); - - // set initial center - for (int j=0; j - Miniball::~Miniball() - { - delete_arrays(); - } - - template - void Miniball::create_arrays() - { - c = new NT*[d+1]; - v = new NT*[d+1]; - a = new NT*[d+1]; - for (int i=0; i - void Miniball::delete_arrays() - { - delete[] f; - delete[] z; - delete[] q0; - delete[] sqr_r; - for (int i=0; i - const typename Miniball::NT* - Miniball::center () const - { - return current_c; - } - - template - typename Miniball::NT - Miniball::squared_radius () const - { - return current_sqr_r; - } - - template - int Miniball::nr_support_points () const - { - assert (ssize < d+2); - return ssize; - } - - template - typename Miniball::SupportPointIterator - Miniball::support_points_begin () const - { - return L.begin(); - } - - template - typename Miniball::SupportPointIterator - Miniball::support_points_end () const - { - return support_end; - } - - template - typename Miniball::NT - Miniball::relative_error (NT& subopt) const - { - NT e, max_e = nt0; - // compute maximum absolute excess of support points - for (SupportPointIterator it = support_points_begin(); - it != support_points_end(); ++it) { - e = excess (*it); - if (e < nt0) e = -e; - if (e > max_e) { - max_e = e; - } - } - // compute maximum excess of any point - for (Pit i = points_begin; i != points_end; ++i) - if ((e = excess (i)) > max_e) - max_e = e; - - subopt = suboptimality(); - assert (current_sqr_r > nt0 || max_e == nt0); - return (current_sqr_r == nt0 ? nt0 : max_e / current_sqr_r); - } - - template - bool Miniball::is_valid (NT tol) const - { - NT suboptimality; - return ( (relative_error (suboptimality) <= tol) && (suboptimality == 0) ); - } - - template - double Miniball::get_time() const - { - return time; - } - - template - void Miniball::mtf_mb (Sit n) - { - // Algorithm 1: mtf_mb (L_{n-1}, B), where L_{n-1} = [L.begin, n) - // B: the set of forced points, defining the current ball - // S: the superset of support points computed by the algorithm - // -------------------------------------------------------------- - // from B. Gaertner, Fast and Robust Smallest Enclosing Balls, ESA 1999, - // http://www.inf.ethz.ch/personal/gaertner/texts/own_work/esa99_final.pdf - - // PRE: B = S - assert (fsize == ssize); - - support_end = L.begin(); - if ((fsize) == d+1) return; - - // incremental construction - for (Sit i = L.begin(); i != n;) - { - // INV: (support_end - L.begin() == |S|-|B|) - assert (std::distance (L.begin(), support_end) == ssize - fsize); - - Sit j = i++; - if (excess(*j) > nt0) - if (push(*j)) { // B := B + p_i - mtf_mb (j); // mtf_mb (L_{i-1}, B + p_i) - pop(); // B := B - p_i - mtf_move_to_front(j); - } - } - // POST: the range [L.begin(), support_end) stores the set S\B - } - - template - void Miniball::mtf_move_to_front (Sit j) - { - if (support_end == j) - support_end++; - L.splice (L.begin(), L, j); - } - - template - void Miniball::pivot_mb (Pit n) - { - // Algorithm 2: pivot_mb (L_{n-1}), where L_{n-1} = [L.begin, n) - // -------------------------------------------------------------- - // from B. Gaertner, Fast and Robust Smallest Enclosing Balls, ESA 1999, - // http://www.inf.ethz.ch/personal/gaertner/texts/own_work/esa99_final.pdf - NT old_sqr_r; - const NT* c; - Pit pivot, k; - NT e, max_e, sqr_r; - Cit p; - do { - old_sqr_r = current_sqr_r; - sqr_r = current_sqr_r; - - pivot = points_begin; - max_e = nt0; - for (k = points_begin; k != n; ++k) { - p = coord_accessor(k); - e = -sqr_r; - c = current_c; - for (int j=0; j(*p++-*c++); - if (e > max_e) { - max_e = e; - pivot = k; - } - } - - if (max_e > nt0) { - // check if the pivot is already contained in the support set - if (std::find(L.begin(), support_end, pivot) == support_end) { - assert (fsize == 0); - if (push (pivot)) { - mtf_mb(support_end); - pop(); - pivot_move_to_front(pivot); - } - } - } - } while (old_sqr_r < current_sqr_r); - } - - template - void Miniball::pivot_move_to_front (Pit j) - { - L.push_front(j); - if (std::distance(L.begin(), support_end) == d+2) - support_end--; - } - - template - inline typename Miniball::NT - Miniball::excess (Pit pit) const - { - Cit p = coord_accessor(pit); - NT e = -current_sqr_r; - NT* c = current_c; - for (int k=0; k(*p++-*c++); - } - return e; - } - - template - void Miniball::pop () - { - --fsize; - } - - template - bool Miniball::push (Pit pit) - { - int i, j; - NT eps = mb_sqr(std::numeric_limits::epsilon()); - - Cit cit = coord_accessor(pit); - Cit p = cit; - - if (fsize==0) { - for (i=0; i(v[fsize][j]); - z[fsize]*=2; - - // reject push if z_fsize too small - if (z[fsize](*p++-c[fsize-1][i]); - f[fsize]=e/z[fsize]; - - for (i=0; i - typename Miniball::NT - Miniball::suboptimality () const - { - NT* l = new NT[d+1]; - NT min_l = nt0; - l[0] = NT(1); - for (int i=ssize-1; i>0; --i) { - l[i] = f[i]; - for (int k=ssize-1; k>i; --k) - l[i]-=a[k][i]*l[k]; - if (l[i] < min_l) min_l = l[i]; - l[0] -= l[i]; - } - if (l[0] < min_l) min_l = l[0]; - delete[] l; - if (min_l < nt0) - return -min_l; - return nt0; - } -} // namespace Miniball - -} // namespace Gudhi - -#endif // MINIBALL_HPP_ diff --git a/src/common/doc/examples.h b/src/common/doc/examples.h index 879fb96a..0c4320f6 100644 --- a/src/common/doc/examples.h +++ b/src/common/doc/examples.h @@ -40,7 +40,6 @@ * @example edge_collapse_basic_example.cpp * \section Cech_complex_example_section Cech_complex * @example cech_persistence.cpp - * @example cech_complex_step_by_step.cpp * @example cech_complex_example_from_points.cpp * \section Bitmap_cubical_complex_example_section Bitmap_cubical_complex * @example periodic_cubical_complex_persistence.cpp diff --git a/src/common/doc/main_page.md b/src/common/doc/main_page.md index 17354179..6f995fee 100644 --- a/src/common/doc/main_page.md +++ b/src/common/doc/main_page.md @@ -181,7 +181,7 @@ Author: Vincent Rouvreau
Introduced in: GUDHI 2.2.0
Copyright: MIT [(GPL v3)](../../licensing/)
- Includes: [Miniball](https://people.inf.ethz.ch/gaertner/subdir/software/miniball.html)
+ Requires: \ref cgal -- cgit v1.2.3 From a3d8a052e260c501d2feee2e63d3699b71baf549 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Mon, 25 Apr 2022 16:10:39 +0200 Subject: Use Dimension_tag<2> instead of Dynamic_dimension_tag in cech_complex_example_from_points.cpp --- .../example/cech_complex_example_from_points.cpp | 38 +++++++--------------- 1 file changed, 12 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/example/cech_complex_example_from_points.cpp b/src/Cech_complex/example/cech_complex_example_from_points.cpp index 034077eb..ef9071ec 100644 --- a/src/Cech_complex/example/cech_complex_example_from_points.cpp +++ b/src/Cech_complex/example/cech_complex_example_from_points.cpp @@ -6,42 +6,28 @@ #include #include #include -#include int main() { // Type definitions using Simplex_tree = Gudhi::Simplex_tree; using Filtration_value = Simplex_tree::Filtration_value; - using Kernel = CGAL::Epeck_d; - using FT = typename Kernel::FT; + using Kernel = CGAL::Epeck_d>; using Point = typename Kernel::Point_d; using Point_cloud = std::vector; using Cech_complex = Gudhi::cech_complex::Cech_complex; Point_cloud points; - - std::vector point0({1., 0.}); - points.emplace_back(point0.begin(), point0.end()); - std::vector point1({0., 1.}); - points.emplace_back(point1.begin(), point1.end()); - std::vector point2({2., 1.}); - points.emplace_back(point2.begin(), point2.end()); - std::vector point3({3., 2.}); - points.emplace_back(point3.begin(), point3.end()); - std::vector point4({0., 3.}); - points.emplace_back(point4.begin(), point4.end()); - std::vector point5({3. + std::sqrt(3.), 3.}); - points.emplace_back(point5.begin(), point5.end()); - std::vector point6({1., 4.}); - points.emplace_back(point6.begin(), point6.end()); - std::vector point7({3., 4.}); - points.emplace_back(point7.begin(), point7.end()); - std::vector point8({2., 4. + std::sqrt(3.)}); - points.emplace_back(point8.begin(), point8.end()); - std::vector point9({0., 4.}); - points.emplace_back(point9.begin(), point9.end()); - std::vector point10({-0.5, 2.}); - points.emplace_back(point10.begin(), point10.end()); + points.emplace_back(1., 0.); // 0 + points.emplace_back(0., 1.); // 1 + points.emplace_back(2., 1.); // 2 + points.emplace_back(3., 2.); // 3 + points.emplace_back(0., 3.); // 4 + points.emplace_back(3. + std::sqrt(3.), 3.); // 5 + points.emplace_back(1., 4.); // 6 + points.emplace_back(3., 4.); // 7 + points.emplace_back(2., 4. + std::sqrt(3.)); // 8 + points.emplace_back(0., 4.); // 9 + points.emplace_back(-0.5, 2.); // 10 // ---------------------------------------------------------------------------- // Init of a Cech complex from points -- cgit v1.2.3 From aec7ab1737a5284f4b7c2d1f7fe3eb7977df7537 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Tue, 26 Apr 2022 16:52:26 +0200 Subject: Modify cech doc Use Filtration_value instead of double for casting Use a templated range of points instead of vector in cech constructor Capitalize sphere_circumradius.h file name and make it private in doc --- src/Cech_complex/include/gudhi/Cech_complex.h | 19 +++---- .../include/gudhi/Cech_complex_blocker.h | 18 +++---- .../include/gudhi/Sphere_circumradius.h | 62 ++++++++++++++++++++++ .../include/gudhi/sphere_circumradius.h | 62 ---------------------- src/Cech_complex/test/test_cech_complex.cpp | 15 +++--- src/Cech_complex/utilities/cech_persistence.cpp | 1 - 6 files changed, 88 insertions(+), 89 deletions(-) create mode 100644 src/Cech_complex/include/gudhi/Sphere_circumradius.h delete mode 100644 src/Cech_complex/include/gudhi/sphere_circumradius.h (limited to 'src') diff --git a/src/Cech_complex/include/gudhi/Cech_complex.h b/src/Cech_complex/include/gudhi/Cech_complex.h index 375be1d2..fc39f75b 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex.h +++ b/src/Cech_complex/include/gudhi/Cech_complex.h @@ -11,7 +11,7 @@ #ifndef CECH_COMPLEX_H_ #define CECH_COMPLEX_H_ -#include // for Gudhi::cech_complex::Sphere_circumradius +#include // for Gudhi::cech_complex::Sphere_circumradius #include // for Gudhi::Proximity_graph #include // for GUDHI_CHECK #include // for Gudhi::cech_complex::Cech_blocker @@ -25,15 +25,15 @@ namespace cech_complex { /** * \class Cech_complex - * \brief Cech complex data structure. + * \brief Cech complex class. * * \ingroup cech_complex * * \details - * The data structure is a proximity graph, containing edges when the edge length is less or equal - * to a given max_radius. Edge length is computed from `Gudhi::cech_complex::Sphere_circumradius` distance function. + * Cech complex is a simplicial complex constructed from a proximity graph, where the set of all simplices is filtered + * by the radius of their minimal enclosing ball and bounded by the given max_radius. * - * \tparam Kernel CGAL kernel. + * \tparam Kernel CGAL kernel: either Epick_d or Epeck_d. * * \tparam SimplicialComplexForCechComplex furnishes `Vertex_handle` and `Filtration_value` type definition required * by `Gudhi::Proximity_graph` and Cech blocker. @@ -58,15 +58,16 @@ class Cech_complex { using Sphere = typename cech_blocker::Sphere; public: - /** \brief Cech_complex constructor from a list of points. + /** \brief Cech_complex constructor from a range of points. * - * @param[in] points Vector of points where each point is defined as `kernel::Point_d`. + * @param[in] points Range of points where each point is defined as `kernel::Point_d`. * @param[in] max_radius Maximal radius value. * */ - Cech_complex(const Point_cloud & points, Filtration_value max_radius) : max_radius_(max_radius) { + template + Cech_complex(const InputPointRange & points, Filtration_value max_radius) : max_radius_(max_radius) { - point_cloud_.assign(points.begin(), points.end()); + point_cloud_.assign(std::begin(points), std::end(points)); cech_skeleton_graph_ = Gudhi::compute_proximity_graph( point_cloud_, max_radius_, Sphere_circumradius()); diff --git a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h index 1a696422..1a09f7e1 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h +++ b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h @@ -11,7 +11,7 @@ #ifndef CECH_COMPLEX_BLOCKER_H_ #define CECH_COMPLEX_BLOCKER_H_ -#include // for casting from FT to double +#include // for casting from FT to Filtration_value and double to FT #include #include @@ -36,7 +36,7 @@ namespace cech_complex { * * \tparam Cech_complex is required by the blocker. * - * \tparam Kernel CGAL kernel. + * \tparam Kernel CGAL kernel: either Epick_d or Epeck_d. */ template class Cech_blocker { @@ -69,8 +69,8 @@ class Cech_blocker { * \return true if the simplex radius is greater than the Cech_complex max_radius*/ bool operator()(Simplex_handle sh) { using Point_cloud = std::vector; - CGAL::NT_converter cast_to_double; - Filtration_value radius = 0.; + CGAL::NT_converter cast_to_fv; + Filtration_value radius = 0; // for each face of simplex sh, test outsider point is indeed inside enclosing ball, if yes, take it and exit loop, otherwise, new sphere is circumsphere of all vertices Sphere min_enclos_ball; @@ -105,18 +105,18 @@ class Cech_blocker { face_points.clear(); if (kernel_.squared_distance_d_object()(sph.first, cc_ptr_->get_point(extra)) <= sph.second) { - radius = std::sqrt(cast_to_double(sph.second)); + radius = std::sqrt(cast_to_fv(sph.second)); #ifdef DEBUG_TRACES std::clog << "center: " << sph.first << ", radius: " << radius << std::endl; #endif // DEBUG_TRACES - if (cast_to_double(sph.second) < cast_to_double(min_enclos_ball.second)) + if (sph.second < min_enclos_ball.second) min_enclos_ball = sph; break; } } // Get the minimal radius of all faces enclosing balls if exists - if(cast_to_double(min_enclos_ball.second) != std::numeric_limits::max()) { - radius = std::sqrt(cast_to_double(min_enclos_ball.second)); + if(min_enclos_ball.second != std::numeric_limits::max()) { + radius = std::sqrt(cast_to_fv(min_enclos_ball.second)); sc_ptr_->assign_key(sh, cc_ptr_->get_cache().size()); cc_ptr_->get_cache().push_back(min_enclos_ball); @@ -128,7 +128,7 @@ class Cech_blocker { points.push_back(cc_ptr_->get_point(vertex)); } Sphere sph = get_sphere(points.cbegin(), points.cend()); - radius = std::sqrt(cast_to_double(sph.second)); + radius = std::sqrt(cast_to_fv(sph.second)); sc_ptr_->assign_key(sh, cc_ptr_->get_cache().size()); cc_ptr_->get_cache().push_back(sph); diff --git a/src/Cech_complex/include/gudhi/Sphere_circumradius.h b/src/Cech_complex/include/gudhi/Sphere_circumradius.h new file mode 100644 index 00000000..b0d9f7cc --- /dev/null +++ b/src/Cech_complex/include/gudhi/Sphere_circumradius.h @@ -0,0 +1,62 @@ +/* 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): Hind Montassif + * + * Copyright (C) 2021 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#ifndef SPHERE_CIRCUMRADIUS_H_ +#define SPHERE_CIRCUMRADIUS_H_ + +#include // for #include + +#include // for std::sqrt +#include + +namespace Gudhi { + +namespace cech_complex { + +/** \private @brief Compute the circumradius of the sphere passing through points given by a range of coordinates. + * The points are assumed to have the same dimension. */ +template +class Sphere_circumradius { + private: + Kernel kernel_; + public: + using Point = typename Kernel::Point_d; + using Point_cloud = typename std::vector; + + /** \brief Circumradius of sphere passing through two points using CGAL. + * + * @param[in] point_1 + * @param[in] point_2 + * @return Sphere circumradius passing through two points. + * \tparam Point must be a Kernel::Point_d from CGAL. + * + */ + double operator()(const Point& point_1, const Point& point_2) const { + return std::sqrt(CGAL::to_double(kernel_.squared_distance_d_object()(point_1, point_2))) / 2.; + } + + /** \brief Circumradius of sphere passing through point cloud using CGAL. + * + * @param[in] point_cloud The points. + * @return Sphere circumradius passing through the points. + * \tparam Point_cloud must be a range of Kernel::Point_d points from CGAL. + * + */ + double operator()(const Point_cloud& point_cloud) const { + return std::sqrt(CGAL::to_double(kernel_.compute_squared_radius_d_object()(point_cloud.begin(), point_cloud.end()))); + } + +}; + +} // namespace cech_complex + +} // namespace Gudhi + +#endif // SPHERE_CIRCUMRADIUS_H_ diff --git a/src/Cech_complex/include/gudhi/sphere_circumradius.h b/src/Cech_complex/include/gudhi/sphere_circumradius.h deleted file mode 100644 index a6dec3dc..00000000 --- a/src/Cech_complex/include/gudhi/sphere_circumradius.h +++ /dev/null @@ -1,62 +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): Hind Montassif - * - * Copyright (C) 2021 Inria - * - * Modification(s): - * - YYYY/MM Author: Description of the modification - */ - -#ifndef SPHERE_CIRCUMRADIUS_H_ -#define SPHERE_CIRCUMRADIUS_H_ - -#include // for #include - -#include // for std::sqrt -#include - -namespace Gudhi { - -namespace cech_complex { - -/** @brief Compute the circumradius of the sphere passing through points given by a range of coordinates. - * The points are assumed to have the same dimension. */ -template -class Sphere_circumradius { - private: - Kernel kernel_; - public: - using Point = typename Kernel::Point_d; - using Point_cloud = typename std::vector; - - /** \brief Circumradius of sphere passing through two points using CGAL. - * - * @param[in] point_1 - * @param[in] point_2 - * @return Sphere circumradius passing through two points. - * \tparam Point must be a Kernel::Point_d from CGAL. - * - */ - double operator()(const Point& point_1, const Point& point_2) const { - return std::sqrt(CGAL::to_double(kernel_.squared_distance_d_object()(point_1, point_2))) / 2.; - } - - /** \brief Circumradius of sphere passing through point cloud using CGAL. - * - * @param[in] point_cloud The points. - * @return Sphere circumradius passing through the points. - * \tparam Point_cloud must be a range of Kernel::Point_d points from CGAL. - * - */ - double operator()(const Point_cloud& point_cloud) const { - return std::sqrt(CGAL::to_double(kernel_.compute_squared_radius_d_object()(point_cloud.begin(), point_cloud.end()))); - } - -}; - -} // namespace cech_complex - -} // namespace Gudhi - -#endif // SPHERE_CIRCUMRADIUS_H_ diff --git a/src/Cech_complex/test/test_cech_complex.cpp b/src/Cech_complex/test/test_cech_complex.cpp index 4cf8b68f..ea32f596 100644 --- a/src/Cech_complex/test/test_cech_complex.cpp +++ b/src/Cech_complex/test/test_cech_complex.cpp @@ -22,7 +22,6 @@ // to construct Cech_complex from a OFF file of points #include #include -#include #include #include // For EXACT or SAFE version @@ -139,18 +138,18 @@ BOOST_AUTO_TEST_CASE(Cech_complex_for_documentation) { } Kernel kern; - Simplex_tree::Filtration_value f012 = st2.filtration(st2.find({0, 1, 2})); + Filtration_value f012 = st2.filtration(st2.find({0, 1, 2})); std::clog << "f012= " << f012 << std::endl; - CGAL::NT_converter cast_to_double; - GUDHI_TEST_FLOAT_EQUALITY_CHECK(f012, std::sqrt(cast_to_double(kern.compute_squared_radius_d_object()(points012.begin(), points012.end())))); + CGAL::NT_converter cast_to_fv; + GUDHI_TEST_FLOAT_EQUALITY_CHECK(f012, std::sqrt(cast_to_fv(kern.compute_squared_radius_d_object()(points012.begin(), points012.end())))); Point_cloud points1410; points1410.push_back(cech_complex_for_doc.get_point(1)); points1410.push_back(cech_complex_for_doc.get_point(4)); points1410.push_back(cech_complex_for_doc.get_point(10)); - Simplex_tree::Filtration_value f1410 = st2.filtration(st2.find({1, 4, 10})); + Filtration_value f1410 = st2.filtration(st2.find({1, 4, 10})); std::clog << "f1410= " << f1410 << std::endl; // In this case, the computed circumsphere using CGAL kernel does not match the minimal enclosing ball; the filtration value check is therefore done against a hardcoded value @@ -161,10 +160,10 @@ BOOST_AUTO_TEST_CASE(Cech_complex_for_documentation) { points469.push_back(cech_complex_for_doc.get_point(6)); points469.push_back(cech_complex_for_doc.get_point(9)); - Simplex_tree::Filtration_value f469 = st2.filtration(st2.find({4, 6, 9})); + Filtration_value f469 = st2.filtration(st2.find({4, 6, 9})); std::clog << "f469= " << f469 << std::endl; - GUDHI_TEST_FLOAT_EQUALITY_CHECK(f469, std::sqrt(cast_to_double(kern.compute_squared_radius_d_object()(points469.begin(), points469.end())))); + GUDHI_TEST_FLOAT_EQUALITY_CHECK(f469, std::sqrt(cast_to_fv(kern.compute_squared_radius_d_object()(points469.begin(), points469.end())))); BOOST_CHECK((st2.find({6, 7, 8}) == st2.null_simplex())); BOOST_CHECK((st2.find({3, 5, 7}) == st2.null_simplex())); @@ -246,7 +245,7 @@ BOOST_AUTO_TEST_CASE(Cech_create_complex_throw) { // // ---------------------------------------------------------------------------- std::string off_file_name("alphacomplexdoc.off"); - double max_radius = 12.0; + Filtration_value max_radius = 12.0; std::clog << "========== OFF FILE NAME = " << off_file_name << " - Cech max_radius=" << max_radius << "==========" << std::endl; diff --git a/src/Cech_complex/utilities/cech_persistence.cpp b/src/Cech_complex/utilities/cech_persistence.cpp index 82992f2d..75d10c0f 100644 --- a/src/Cech_complex/utilities/cech_persistence.cpp +++ b/src/Cech_complex/utilities/cech_persistence.cpp @@ -9,7 +9,6 @@ */ #include -#include #include #include #include -- cgit v1.2.3 From b9119a92c5316a36e0ae8ff041f0625b51973321 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Wed, 27 Apr 2022 11:58:39 +0200 Subject: update doc + remove numpy/tensorflow mixup --- src/python/gudhi/tensorflow/cubical_layer.py | 2 +- src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py | 2 +- src/python/gudhi/tensorflow/rips_layer.py | 2 +- src/python/test/test_diff.py | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/tensorflow/cubical_layer.py b/src/python/gudhi/tensorflow/cubical_layer.py index 31c44205..8db46a8e 100644 --- a/src/python/gudhi/tensorflow/cubical_layer.py +++ b/src/python/gudhi/tensorflow/cubical_layer.py @@ -58,7 +58,7 @@ class CubicalLayer(tf.keras.layers.Layer): X (TensorFlow variable): pixel values of the cubical complex Returns: - dgms (list of TensorFlow variables): list of cubical persistence diagrams of length self.dimensions, where each element contains a finite persistence diagram of shape [num_finite_points, 2] + dgms (list of TensorFlow variables): list of cubical persistence diagrams. The length of this list is the same than that of dimensions, i.e., there is one persistence diagram per homology dimension provided in the input list dimensions. Moreover, each element of this list is an array containing the finite part of the corresponding persistence diagram, of shape [num_finite_points, 2]. Note that there is no essential part since this part is always empty in cubical persistence diagrams, except in homology dimension zero, where the essential part always contains a single point, with abscissa equal to the smallest value in the complex, and infinite ordinate. """ # Compute pixels associated to positive and negative simplices # Don't compute gradient for this operation diff --git a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py index cf7df6de..a2e48d8a 100644 --- a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py +++ b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py @@ -65,7 +65,7 @@ class LowerStarSimplexTreeLayer(tf.keras.layers.Layer): F (TensorFlow variable): filter function values over the vertices of the simplex tree. The ith entry of F corresponds to vertex i in self.simplextree Returns: - dgms (list of tuple of TensorFlow variables): list of lower-star persistence diagrams of length self.dimensions, where each element of the list is a tuple that contains the finite and essential persistence diagrams of shapes [num_finite_points, 2] and [num_essential_points, 1] respectively + dgms (list of tuple of TensorFlow variables): list of lower-star persistence diagrams. The length of this list is the same than that of dimensions, i.e., there is one persistence diagram per homology dimension provided in the input list dimensions. Moreover, the finite and essential parts of the persistence diagrams are provided separately: each element of this list is a tuple of size two that contains the finite and essential parts of the corresponding persistence diagram, of shapes [num_finite_points, 2] and [num_essential_points, 1] respectively """ # Don't try to compute gradients for the vertex pairs indices = _LowerStarSimplexTree(self.simplextree, filtration.numpy(), self.dimensions) diff --git a/src/python/gudhi/tensorflow/rips_layer.py b/src/python/gudhi/tensorflow/rips_layer.py index 7b5edfa3..b5b58ab4 100644 --- a/src/python/gudhi/tensorflow/rips_layer.py +++ b/src/python/gudhi/tensorflow/rips_layer.py @@ -63,7 +63,7 @@ class RipsLayer(tf.keras.layers.Layer): X (TensorFlow variable): point cloud of shape [number of points, number of dimensions] Returns: - dgms (list of tuple of TensorFlow variables): list of Rips persistence diagrams of length self.dimensions, where each element of the list is a tuple that contains the finite and essential persistence diagrams of shapes [num_finite_points, 2] and [num_essential_points, 1] respectively + dgms (list of tuple of TensorFlow variables): list of Rips persistence diagrams. The length of this list is the same than that of dimensions, i.e., there is one persistence diagram per homology dimension provided in the input list dimensions. Moreover, the finite and essential parts of the persistence diagrams are provided separately: each element of this list is a tuple of size two that contains the finite and essential parts of the corresponding persistence diagram, of shapes [num_finite_points, 2] and [num_essential_points, 1] respectively """ # Compute distance matrix DX = tf.norm(tf.expand_dims(X, 1)-tf.expand_dims(X, 0), axis=2) diff --git a/src/python/test/test_diff.py b/src/python/test/test_diff.py index e0c99d07..2529cf22 100644 --- a/src/python/test/test_diff.py +++ b/src/python/test/test_diff.py @@ -13,7 +13,7 @@ def test_rips_diff(): dgm = rl.call(X)[0][0] loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) grads = tape.gradient(loss, [X]) - assert np.abs(grads[0].numpy()-np.array([[-.5,-.5],[.5,.5]])).sum() <= 1e-6 + assert tf.norm(grads[0]-tf.constant([[-.5,-.5],[.5,.5]]),1) <= 1e-6 def test_cubical_diff(): @@ -25,7 +25,7 @@ def test_cubical_diff(): dgm = cl.call(X)[0] loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) grads = tape.gradient(loss, [X]) - assert np.abs(grads[0].numpy()-np.array([[0.,0.,0.],[0.,.5,0.],[0.,0.,-.5]])).sum() <= 1e-6 + assert tf.norm(grads[0]-tf.constant([[0.,0.,0.],[0.,.5,0.],[0.,0.,-.5]]),1) <= 1e-6 def test_nonsquare_cubical_diff(): @@ -37,7 +37,7 @@ def test_nonsquare_cubical_diff(): dgm = cl.call(X)[0] loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) grads = tape.gradient(loss, [X]) - assert np.abs(grads[0].numpy()-np.array([[0.,0.5,-0.5],[0.,0.,0.]])).sum() <= 1e-6 + assert tf.norm(grads[0]-tf.constant([[0.,0.5,-0.5],[0.,0.,0.]]),1) <= 1e-6 def test_st_diff(): @@ -73,6 +73,6 @@ def test_st_diff(): loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) grads = tape.gradient(loss, [F]) - assert np.array_equal(np.array(grads[0].indices), np.array([2,4])) - assert np.array_equal(np.array(grads[0].values), np.array([-1,1])) + assert tf.math.reduce_all(tf.math.equal(grads[0].indices, tf.constant([2,4]))) + assert tf.math.reduce_all(tf.math.equal(grads[0].values, tf.constant([-1.,1.]))) -- cgit v1.2.3 From 70c20c20f89e2037544e7906c5743a30a7e3beb7 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Wed, 27 Apr 2022 16:41:22 +0200 Subject: Remove unnecessary code from cech blocker --- .../include/gudhi/Cech_complex_blocker.h | 41 +++++++++------------- 1 file changed, 17 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h index 1a09f7e1..72876512 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h +++ b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h @@ -12,6 +12,7 @@ #define CECH_COMPLEX_BLOCKER_H_ #include // for casting from FT to Filtration_value and double to FT +#include #include #include @@ -73,10 +74,7 @@ class Cech_blocker { Filtration_value radius = 0; // for each face of simplex sh, test outsider point is indeed inside enclosing ball, if yes, take it and exit loop, otherwise, new sphere is circumsphere of all vertices - Sphere min_enclos_ball; - CGAL::NT_converter cast_to_FT; - min_enclos_ball.second = cast_to_FT(std::numeric_limits::max()); - Point_cloud face_points; + boost::optional min_enclos_ball; for (auto face : sc_ptr_->boundary_simplex_range(sh)) { // Find which vertex of sh is missing in face. We rely on the fact that simplex_vertex_range is sorted. auto longlist = sc_ptr_->simplex_vertex_range(sh); @@ -88,41 +86,36 @@ class Cech_blocker { while(shortiter != enditer && *longiter == *shortiter) { ++longiter; ++shortiter; } auto extra = *longiter; // Vertex_handle - for (auto vertex : sc_ptr_->simplex_vertex_range(face)) { - face_points.push_back(cc_ptr_->get_point(vertex)); - #ifdef DEBUG_TRACES - std::clog << "#(" << vertex << ")#"; - #endif // DEBUG_TRACES - } Sphere sph; auto k = sc_ptr_->key(face); if(k != sc_ptr_->null_key()) { sph = cc_ptr_->get_cache().at(k); } else { + Point_cloud face_points; + for (auto vertex : sc_ptr_->simplex_vertex_range(face)) { + face_points.push_back(cc_ptr_->get_point(vertex)); + #ifdef DEBUG_TRACES + std::clog << "#(" << vertex << ")#"; + #endif // DEBUG_TRACES + } sph = get_sphere(face_points.cbegin(), face_points.cend()); + face_points.clear(); } - face_points.clear(); - + // Check if the minimal enclosing ball of current face contains the extra point if (kernel_.squared_distance_d_object()(sph.first, cc_ptr_->get_point(extra)) <= sph.second) { - radius = std::sqrt(cast_to_fv(sph.second)); #ifdef DEBUG_TRACES std::clog << "center: " << sph.first << ", radius: " << radius << std::endl; #endif // DEBUG_TRACES - if (sph.second < min_enclos_ball.second) - min_enclos_ball = sph; + radius = std::sqrt(cast_to_fv(sph.second)); + sc_ptr_->assign_key(sh, cc_ptr_->get_cache().size()); + cc_ptr_->get_cache().push_back(sph); + min_enclos_ball.emplace(cc_ptr_->get_cache().back()); break; } } - // Get the minimal radius of all faces enclosing balls if exists - if(min_enclos_ball.second != std::numeric_limits::max()) { - radius = std::sqrt(cast_to_fv(min_enclos_ball.second)); - - sc_ptr_->assign_key(sh, cc_ptr_->get_cache().size()); - cc_ptr_->get_cache().push_back(min_enclos_ball); - } - - if (radius == 0.) { // Spheres of each face don't contain the whole simplex + // Spheres of each face don't contain the whole simplex + if(!min_enclos_ball) { Point_cloud points; for (auto vertex : sc_ptr_->simplex_vertex_range(sh)) { points.push_back(cc_ptr_->get_point(vertex)); -- cgit v1.2.3 From a14d9becec7361a2559fa239c1ca8f2c1b5c5768 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Wed, 27 Apr 2022 17:08:14 +0200 Subject: Ultimately, we don't really need to store the min enclosing ball in case it contains the extra point, a bool is enough --- src/Cech_complex/include/gudhi/Cech_complex_blocker.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h index 72876512..fb12946a 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h +++ b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h @@ -11,8 +11,7 @@ #ifndef CECH_COMPLEX_BLOCKER_H_ #define CECH_COMPLEX_BLOCKER_H_ -#include // for casting from FT to Filtration_value and double to FT -#include +#include // for casting from FT to Filtration_value #include #include @@ -72,9 +71,9 @@ class Cech_blocker { using Point_cloud = std::vector; CGAL::NT_converter cast_to_fv; Filtration_value radius = 0; + bool is_min_enclos_ball = false; // for each face of simplex sh, test outsider point is indeed inside enclosing ball, if yes, take it and exit loop, otherwise, new sphere is circumsphere of all vertices - boost::optional min_enclos_ball; for (auto face : sc_ptr_->boundary_simplex_range(sh)) { // Find which vertex of sh is missing in face. We rely on the fact that simplex_vertex_range is sorted. auto longlist = sc_ptr_->simplex_vertex_range(sh); @@ -107,15 +106,15 @@ class Cech_blocker { #ifdef DEBUG_TRACES std::clog << "center: " << sph.first << ", radius: " << radius << std::endl; #endif // DEBUG_TRACES + is_min_enclos_ball = true; radius = std::sqrt(cast_to_fv(sph.second)); sc_ptr_->assign_key(sh, cc_ptr_->get_cache().size()); cc_ptr_->get_cache().push_back(sph); - min_enclos_ball.emplace(cc_ptr_->get_cache().back()); break; } } // Spheres of each face don't contain the whole simplex - if(!min_enclos_ball) { + if(!is_min_enclos_ball) { Point_cloud points; for (auto vertex : sc_ptr_->simplex_vertex_range(sh)) { points.push_back(cc_ptr_->get_point(vertex)); -- cgit v1.2.3 From a6a68c11455a554619d8a5b5d2f92c1ddbf45e99 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Thu, 28 Apr 2022 16:38:34 +0200 Subject: Put edge sphere in cache --- src/Cech_complex/include/gudhi/Cech_complex_blocker.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h index fb12946a..3141d27a 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h +++ b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h @@ -99,6 +99,10 @@ class Cech_blocker { #endif // DEBUG_TRACES } sph = get_sphere(face_points.cbegin(), face_points.cend()); + // Put edge sphere in cache + sc_ptr_->assign_key(face, cc_ptr_->get_cache().size()); + cc_ptr_->get_cache().push_back(sph); + // Clear face_points face_points.clear(); } // Check if the minimal enclosing ball of current face contains the extra point -- cgit v1.2.3 From 7fff2e5e725ced71da812d9f0bede1c8e0666e2e Mon Sep 17 00:00:00 2001 From: albert-github Date: Fri, 20 May 2022 12:07:56 +0200 Subject: Documentation: make it easier to build only the documentation After review: - option is now: ``` -DWITH_GUDHI_CPP_DOCUMENTATION_ONLY=ON ``` - added some instructions to the installation description. --- CMakeLists.txt | 6 +++--- src/common/doc/installation.h | 11 ++++++++--- 2 files changed, 11 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/CMakeLists.txt b/CMakeLists.txt index bff6d74d..47d87cd1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ -option(build_documentation_only "Build only the documentation with doxygen." OFF) +option(WITH_GUDHI_CPP_DOCUMENTATION_ONLY "Build only the GUDHI C++ documentation (with doxygen)." OFF) cmake_minimum_required(VERSION 3.5) @@ -16,7 +16,7 @@ set(GUDHI_MISSING_MODULES "" CACHE INTERNAL "GUDHI_MISSING_MODULES") # This variable is used by Cython CMakeLists.txt and by GUDHI_third_party_libraries to know its path set(GUDHI_PYTHON_PATH "src/python") -if (NOT build_documentation_only) +if (NOT WITH_GUDHI_CPP_DOCUMENTATION_ONLY) # For third parties libraries management - To be done last as CGAL updates CMAKE_MODULE_PATH include(GUDHI_third_party_libraries NO_POLICY_SCOPE) endif() @@ -57,7 +57,7 @@ foreach(GUDHI_MODULE ${GUDHI_MODULES}) endforeach() endforeach() -if (NOT build_documentation_only) +if (NOT WITH_GUDHI_CPP_DOCUMENTATION_ONLY) add_subdirectory(src/GudhUI) endif() diff --git a/src/common/doc/installation.h b/src/common/doc/installation.h index 67d026bd..91043983 100644 --- a/src/common/doc/installation.h +++ b/src/common/doc/installation.h @@ -41,11 +41,16 @@ make \endverbatim * program). If some of the tests are failing, please send us the result of the following command: * \verbatim ctest --output-on-failure \endverbatim * - * \subsection documentationgeneration Documentation - * To generate the documentation, Doxygen is required. - * Run the following command in a terminal: + * \subsection documentationgeneration C++ documentation + * To generate the C++ documentation, for this the doxygen program + * is required, run the following command in a terminal: * \verbatim make doxygen \endverbatim * Documentation will be generated in a folder named html. + * + * In case there is not a full setup present and only the documentation should be build the following command sequence + * can be used: +\verbatim cmake -DWITH_GUDHI_CPP_DOCUMENTATION_ONLY=ON .. +make doxygen\endverbatim * * \subsection helloworld Hello world ! * The Hello world for GUDHI -- cgit v1.2.3 From 6e5b348cb02acd16f990df629a9d938ecb3a318f Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Mon, 23 May 2022 10:23:25 +0200 Subject: updated output for cubical complexes --- ext/gudhi-deploy | 2 +- src/python/doc/cubical_complex_tflow_itf_ref.rst | 2 +- src/python/gudhi/tensorflow/cubical_layer.py | 18 +++++++++++------- src/python/test/test_diff.py | 4 ++-- 4 files changed, 15 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/ext/gudhi-deploy b/ext/gudhi-deploy index 975d1bff..290ade10 160000 --- a/ext/gudhi-deploy +++ b/ext/gudhi-deploy @@ -1 +1 @@ -Subproject commit 975d1bffb317f3b84bf1a3d576cdfdbf7b45861c +Subproject commit 290ade1086bedbc96a35df886cadecabbf4072e6 diff --git a/src/python/doc/cubical_complex_tflow_itf_ref.rst b/src/python/doc/cubical_complex_tflow_itf_ref.rst index 881a2950..18b97adf 100644 --- a/src/python/doc/cubical_complex_tflow_itf_ref.rst +++ b/src/python/doc/cubical_complex_tflow_itf_ref.rst @@ -19,7 +19,7 @@ Example of gradient computed from cubical persistence cl = CubicalLayer(dimensions=[0]) with tf.GradientTape() as tape: - dgm = cl.call(X)[0] + dgm = cl.call(X)[0][0] loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) grads = tape.gradient(loss, [X]) diff --git a/src/python/gudhi/tensorflow/cubical_layer.py b/src/python/gudhi/tensorflow/cubical_layer.py index 8db46a8e..e8674d7b 100644 --- a/src/python/gudhi/tensorflow/cubical_layer.py +++ b/src/python/gudhi/tensorflow/cubical_layer.py @@ -58,19 +58,23 @@ class CubicalLayer(tf.keras.layers.Layer): X (TensorFlow variable): pixel values of the cubical complex Returns: - dgms (list of TensorFlow variables): list of cubical persistence diagrams. The length of this list is the same than that of dimensions, i.e., there is one persistence diagram per homology dimension provided in the input list dimensions. Moreover, each element of this list is an array containing the finite part of the corresponding persistence diagram, of shape [num_finite_points, 2]. Note that there is no essential part since this part is always empty in cubical persistence diagrams, except in homology dimension zero, where the essential part always contains a single point, with abscissa equal to the smallest value in the complex, and infinite ordinate. + dgms (list of tuple of TensorFlow variables): list of cubical persistence diagrams. The length of this list is the same than that of dimensions, i.e., there is one persistence diagram per homology dimension provided in the input list dimensions. Moreover, the finite and essential parts of the persistence diagrams are provided separately: each element of this list is a tuple of size two that contains the finite and essential parts of the corresponding persistence diagram, of shapes [num_finite_points, 2] and [num_essential_points, 1] respectively. Note that the essential part is always empty in cubical persistence diagrams, except in homology dimension zero, where the essential part always contains a single point, with abscissa equal to the smallest value in the complex, and infinite ordinate """ # Compute pixels associated to positive and negative simplices # Don't compute gradient for this operation Xflat = tf.reshape(X, [-1]) - Xdim = X.shape - indices_list = _Cubical(Xflat.numpy(), Xdim, self.dimensions) + Xdim, Xflat_numpy = X.shape, Xflat.numpy() + indices_list = _Cubical(Xflat_numpy, Xdim, self.dimensions) + index_essential = np.argmin(Xflat_numpy) # index of minimum pixel value for essential persistence diagram # Get persistence diagram by simply picking the corresponding entries in the image - self.dgms = [tf.reshape(tf.gather(Xflat, indices), [-1,2]) for indices in indices_list] - for idx_dim in range(len(self.min_persistence)): + self.dgms = [] + for idx_dim, dimension in enumerate(self.dimensions): + finite_dgm = tf.reshape(tf.gather(Xflat, indices_list[idx_dim]), [-1,2]) + essential_dgm = tf.reshape(tf.gather(Xflat, index_essential), [-1,1]) if dimension == 0 else tf.zeros([0, 1]) min_pers = self.min_persistence[idx_dim] if min_pers >= 0: - finite_dgm = self.dgms[idx_dim] persistent_indices = tf.where(tf.math.abs(finite_dgm[:,1]-finite_dgm[:,0]) > min_pers) - self.dgms[idx_dim] = tf.reshape(tf.gather(finite_dgm, indices=persistent_indices), [-1,2]) + self.dgms.append((tf.reshape(tf.gather(finite_dgm, indices=persistent_indices), [-1,2]), essential_dgm)) + else: + self.dgms.append((finite_dgm, essential_dgm)) return self.dgms diff --git a/src/python/test/test_diff.py b/src/python/test/test_diff.py index 2529cf22..e0a4717c 100644 --- a/src/python/test/test_diff.py +++ b/src/python/test/test_diff.py @@ -22,7 +22,7 @@ def test_cubical_diff(): cl = CubicalLayer(dimensions=[0]) with tf.GradientTape() as tape: - dgm = cl.call(X)[0] + dgm = cl.call(X)[0][0] loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) grads = tape.gradient(loss, [X]) assert tf.norm(grads[0]-tf.constant([[0.,0.,0.],[0.,.5,0.],[0.,0.,-.5]]),1) <= 1e-6 @@ -34,7 +34,7 @@ def test_nonsquare_cubical_diff(): cl = CubicalLayer(dimensions=[0]) with tf.GradientTape() as tape: - dgm = cl.call(X)[0] + dgm = cl.call(X)[0][0] loss = tf.math.reduce_sum(tf.square(.5*(dgm[:,1]-dgm[:,0]))) grads = tape.gradient(loss, [X]) assert tf.norm(grads[0]-tf.constant([[0.,0.5,-0.5],[0.,0.,0.]]),1) <= 1e-6 -- cgit v1.2.3 From ba96ba348dc9163a51752cab639f0333f5af0533 Mon Sep 17 00:00:00 2001 From: albert-github Date: Thu, 26 May 2022 15:54:43 +0200 Subject: issue #613 [cpp documentation] Footer needs to be generated with a more recent version of doxygen The problem was that the update of the treeview was not called in the footer, though after correcting this some new problems occurred in respect to the treeview (the small pointers ran into the text) and with the in page table of cvontents (e.g. in installation.html). - footer.html: call proper update function by means of `
`, as base the 1.9.4 version served but also older version work. - stylesheet.css, Doxyfile.in: implemented as extra stylesheet and for the "small pointer" problem adjusted the `.arrow` settings - header.html: - placed the `` more as an extra stylesheet as otherwise a second scrollbar would occur - placed the `div id="top">` at the right place - corrected `` tag so it is properly closed (so XML compliant) - corrected setting of `data-topbar` to `data-topbar=true` as HTML5 does not allow attributes without values. --- src/Doxyfile.in | 4 +- src/common/doc/footer.html | 13 +- src/common/doc/header.html | 12 +- src/common/doc/stylesheet.css | 1363 +---------------------------------------- 4 files changed, 24 insertions(+), 1368 deletions(-) mode change 100644 => 100755 src/common/doc/stylesheet.css (limited to 'src') diff --git a/src/Doxyfile.in b/src/Doxyfile.in index 54ec9078..dc11d217 100644 --- a/src/Doxyfile.in +++ b/src/Doxyfile.in @@ -1117,7 +1117,7 @@ HTML_FOOTER = @GUDHI_DOXYGEN_COMMON_DOC_PATH@/footer.html # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_STYLESHEET = @GUDHI_DOXYGEN_COMMON_DOC_PATH@/stylesheet.css +HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets @@ -1130,7 +1130,7 @@ HTML_STYLESHEET = @GUDHI_DOXYGEN_COMMON_DOC_PATH@/stylesheet.css # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_EXTRA_STYLESHEET = +HTML_EXTRA_STYLESHEET = @GUDHI_DOXYGEN_COMMON_DOC_PATH@/stylesheet.css # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note diff --git a/src/common/doc/footer.html b/src/common/doc/footer.html index 4168c6bc..08a2cbd0 100644 --- a/src/common/doc/footer.html +++ b/src/common/doc/footer.html @@ -1,5 +1,9 @@ - + + + + diff --git a/src/common/doc/header.html b/src/common/doc/header.html index 7c20478b..a97e1b2f 100644 --- a/src/common/doc/header.html +++ b/src/common/doc/header.html @@ -8,9 +8,6 @@ $projectname: $title $title - - - @@ -18,13 +15,17 @@ $treeview $search $mathjax + + + $extrastylesheet +
-
diff --git a/src/common/doc/stylesheet.css b/src/common/doc/stylesheet.css old mode 100644 new mode 100755 index 1df177a4..f31f5df4 --- a/src/common/doc/stylesheet.css +++ b/src/common/doc/stylesheet.css @@ -1,1367 +1,18 @@ -/* The standard CSS for doxygen 1.8.6 */ - -body, table, div, p, dl { - font: 400 14px/22px Roboto,sans-serif; -} - -/* @group Heading Levels */ - -h1.groupheader { - font-size: 150%; -} - -.title { - font: 400 14px/28px Roboto,sans-serif; - font-size: 150%; - font-weight: bold; - margin: 10px 2px; -} - -h2.groupheader { - border-bottom: 1px solid #879ECB; - color: #354C7B; - font-size: 150%; - font-weight: normal; - margin-top: 1.75em; - padding-top: 8px; - padding-bottom: 4px; - width: 100%; -} - -h3.groupheader { - font-size: 100%; -} - -h1, h2, h3, h4, h5, h6 { - -webkit-transition: text-shadow 0.5s linear; - -moz-transition: text-shadow 0.5s linear; - -ms-transition: text-shadow 0.5s linear; - -o-transition: text-shadow 0.5s linear; - transition: text-shadow 0.5s linear; - margin-right: 15px; -} - -h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow { - text-shadow: 0 0 15px cyan; -} - -dt { - font-weight: bold; -} - -div.multicol { - -moz-column-gap: 1em; - -webkit-column-gap: 1em; - -moz-column-count: 3; - -webkit-column-count: 3; -} - -p.startli, p.startdd { - margin-top: 2px; -} - -p.starttd { - margin-top: 0px; -} - -p.endli { - margin-bottom: 0px; -} - -p.enddd { - margin-bottom: 4px; -} - -p.endtd { - margin-bottom: 2px; -} - -/* @end */ - -caption { - font-weight: bold; -} - -span.legend { - font-size: 70%; - text-align: center; -} - -h3.version { - font-size: 90%; - text-align: center; -} - -div.qindex, div.navtab{ - background-color: #EBEFF6; - border: 1px solid #A3B4D7; - text-align: center; -} - -div.qindex, div.navpath { - width: 100%; - line-height: 140%; -} - -div.navtab { - margin-right: 15px; -} - -/* @group Link Styling */ - -a { - color: #3D578C; - font-weight: normal; - text-decoration: none; -} - -.contents a:visited { - color: #4665A2; -} - -a:hover { - text-decoration: underline; -} - -a.qindex { - font-weight: bold; -} - -a.qindexHL { - font-weight: bold; - background-color: #9CAFD4; - color: #ffffff; - border: 1px double #869DCA; -} - -.contents a.qindexHL:visited { - color: #ffffff; -} - -a.el { - font-weight: bold; -} - -a.elRef { -} - -a.code, a.code:visited, a.line, a.line:visited { - color: #4665A2; -} - -a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited { - color: #4665A2; -} - -/* @end */ - -dl.el { - margin-left: -1cm; -} - -pre.fragment { - border: 1px solid #C4CFE5; - background-color: #FBFCFD; - padding: 4px 6px; - margin: 4px 8px 4px 2px; - overflow: auto; - word-wrap: break-word; - font-size: 9pt; - line-height: 125%; - font-family: monospace, fixed; - font-size: 105%; -} - -div.fragment { - padding: 4px 6px; - margin: 4px 8px 4px 2px; - background-color: #FBFCFD; - border: 1px solid #C4CFE5; -} - -div.line { - font-family: monospace, fixed; - font-size: 13px; - min-height: 13px; - line-height: 1.0; - text-wrap: unrestricted; - white-space: -moz-pre-wrap; /* Moz */ - white-space: -pre-wrap; /* Opera 4-6 */ - white-space: -o-pre-wrap; /* Opera 7 */ - white-space: pre-wrap; /* CSS3 */ - word-wrap: break-word; /* IE 5.5+ */ - text-indent: -53px; - padding-left: 53px; - padding-bottom: 0px; - margin: 0px; - -webkit-transition-property: background-color, box-shadow; - -webkit-transition-duration: 0.5s; - -moz-transition-property: background-color, box-shadow; - -moz-transition-duration: 0.5s; - -ms-transition-property: background-color, box-shadow; - -ms-transition-duration: 0.5s; - -o-transition-property: background-color, box-shadow; - -o-transition-duration: 0.5s; - transition-property: background-color, box-shadow; - transition-duration: 0.5s; -} - -div.line.glow { - background-color: cyan; - box-shadow: 0 0 10px cyan; -} - - -span.lineno { - padding-right: 4px; - text-align: right; - border-right: 2px solid #0F0; - background-color: #E8E8E8; - white-space: pre; -} -span.lineno a { - background-color: #D8D8D8; -} - -span.lineno a:hover { - background-color: #C8C8C8; -} - -div.ah { - background-color: black; - font-weight: bold; - color: #ffffff; - margin-bottom: 3px; - margin-top: 3px; - padding: 0.2em; - border: solid thin #333; - border-radius: 0.5em; - -webkit-border-radius: .5em; - -moz-border-radius: .5em; - box-shadow: 2px 2px 3px #999; - -webkit-box-shadow: 2px 2px 3px #999; - -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; - background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444)); - background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000); -} - -div.groupHeader { - margin-left: 16px; - margin-top: 12px; - font-weight: bold; -} - -div.groupText { - margin-left: 16px; - font-style: italic; -} - -body { - background-color: white; - color: black; - margin: 0; -} - -div.contents { - margin-top: 10px; - margin-left: 12px; - margin-right: 8px; -} - -td.indexkey { - background-color: #EBEFF6; - font-weight: bold; - border: 1px solid #C4CFE5; - margin: 2px 0px 2px 0; - padding: 2px 10px; - white-space: nowrap; - vertical-align: top; -} - -td.indexvalue { - background-color: #EBEFF6; - border: 1px solid #C4CFE5; - padding: 2px 10px; - margin: 2px 0px; -} - -tr.memlist { - background-color: #EEF1F7; -} - -p.formulaDsp { - text-align: center; -} - -img.formulaDsp { - -} - -img.formulaInl { - vertical-align: middle; -} - -div.center { - text-align: center; - margin-top: 0px; - margin-bottom: 0px; - padding: 0px; -} - -div.center img { - border: 0px; -} - -address.footer { - text-align: right; - padding-right: 12px; -} - -img.footer { - border: 0px; - vertical-align: middle; -} - -/* @group Code Colorization */ - -span.keyword { - color: #008000 -} - -span.keywordtype { - color: #604020 -} - -span.keywordflow { - color: #e08000 -} - -span.comment { - color: #800000 -} - -span.preprocessor { - color: #806020 -} - -span.stringliteral { - color: #002080 -} - -span.charliteral { - color: #008080 -} - -span.vhdldigit { - color: #ff00ff -} - -span.vhdlchar { - color: #000000 -} - -span.vhdlkeyword { - color: #700070 -} - -span.vhdllogic { - color: #ff0000 -} - -blockquote { - background-color: #F7F8FB; - border-left: 2px solid #9CAFD4; - margin: 0 24px 0 4px; - padding: 0 12px 0 16px; -} - -/* @end */ - -/* -.search { - color: #003399; - font-weight: bold; -} - -form.search { - margin-bottom: 0px; - margin-top: 0px; -} - -input.search { - font-size: 75%; - color: #000080; - font-weight: normal; - background-color: #e8eef2; -} -*/ - -td.tiny { - font-size: 75%; -} - -.dirtab { - padding: 4px; - border-collapse: collapse; - border: 1px solid #A3B4D7; -} - -th.dirtab { - background: #EBEFF6; - font-weight: bold; -} - -hr { - height: 0px; - border: none; - border-top: 1px solid #4A6AAA; -} - -hr.footer { - height: 1px; -} - -/* @group Member Descriptions */ - -table.memberdecls { - border-spacing: 0px; - padding: 0px; -} - -.memberdecls td, .fieldtable tr { - -webkit-transition-property: background-color, box-shadow; - -webkit-transition-duration: 0.5s; - -moz-transition-property: background-color, box-shadow; - -moz-transition-duration: 0.5s; - -ms-transition-property: background-color, box-shadow; - -ms-transition-duration: 0.5s; - -o-transition-property: background-color, box-shadow; - -o-transition-duration: 0.5s; - transition-property: background-color, box-shadow; - transition-duration: 0.5s; -} - -.memberdecls td.glow, .fieldtable tr.glow { - background-color: cyan; - box-shadow: 0 0 15px cyan; -} - -.mdescLeft, .mdescRight, -.memItemLeft, .memItemRight, -.memTemplItemLeft, .memTemplItemRight, .memTemplParams { - background-color: #F9FAFC; - border: none; - margin: 4px; - padding: 1px 0 0 8px; -} - -.mdescLeft, .mdescRight { - padding: 0px 8px 4px 8px; - color: #555; -} - -.memSeparator { - border-bottom: 1px solid #DEE4F0; - line-height: 1px; - margin: 0px; - padding: 0px; -} - -.memItemLeft, .memTemplItemLeft { - white-space: nowrap; -} - -.memItemRight { - width: 100%; -} - -.memTemplParams { - color: #4665A2; - white-space: nowrap; - font-size: 80%; -} - -/* @end */ - -/* @group Member Details */ - -/* Styles for detailed member documentation */ - -.memtemplate { - font-size: 80%; - color: #4665A2; - font-weight: normal; - margin-left: 9px; -} - -.memnav { - background-color: #EBEFF6; - border: 1px solid #A3B4D7; - text-align: center; - margin: 2px; - margin-right: 15px; - padding: 2px; -} - -.mempage { - width: 100%; -} - -.memitem { - padding: 0; - margin-bottom: 10px; - margin-right: 5px; - -webkit-transition: box-shadow 0.5s linear; - -moz-transition: box-shadow 0.5s linear; - -ms-transition: box-shadow 0.5s linear; - -o-transition: box-shadow 0.5s linear; - transition: box-shadow 0.5s linear; - display: table !important; - width: 100%; -} - -.memitem.glow { - box-shadow: 0 0 15px cyan; -} - -.memname { - font-weight: bold; - margin-left: 6px; -} - -.memname td { - vertical-align: bottom; -} - -.memproto, dl.reflist dt { - border-top: 1px solid #A8B8D9; - border-left: 1px solid #A8B8D9; - border-right: 1px solid #A8B8D9; - padding: 6px 0px 6px 0px; - color: #253555; - font-weight: bold; - text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); - background-image:url('nav_f.png'); - background-repeat:repeat-x; - background-color: #E2E8F2; - /* opera specific markup */ - box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); - border-top-right-radius: 4px; - border-top-left-radius: 4px; - /* firefox specific markup */ - -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; - -moz-border-radius-topright: 4px; - -moz-border-radius-topleft: 4px; - /* webkit specific markup */ - -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); - -webkit-border-top-right-radius: 4px; - -webkit-border-top-left-radius: 4px; - -} - -.memdoc, dl.reflist dd { - border-bottom: 1px solid #A8B8D9; - border-left: 1px solid #A8B8D9; - border-right: 1px solid #A8B8D9; - padding: 6px 10px 2px 10px; - background-color: #FBFCFD; - border-top-width: 0; - background-image:url('nav_g.png'); - background-repeat:repeat-x; - background-color: #FFFFFF; - /* opera specific markup */ - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; - box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); - /* firefox specific markup */ - -moz-border-radius-bottomleft: 4px; - -moz-border-radius-bottomright: 4px; - -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; - /* webkit specific markup */ - -webkit-border-bottom-left-radius: 4px; - -webkit-border-bottom-right-radius: 4px; - -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); -} - -dl.reflist dt { - padding: 5px; -} - -dl.reflist dd { - margin: 0px 0px 10px 0px; - padding: 5px; -} - -.paramkey { - text-align: right; -} - -.paramtype { - white-space: nowrap; -} - -.paramname { - color: #602020; - white-space: nowrap; -} -.paramname em { - font-style: normal; -} -.paramname code { - line-height: 14px; -} - -.params, .retval, .exception, .tparams { - margin-left: 0px; - padding-left: 0px; -} - -.params .paramname, .retval .paramname { - font-weight: bold; - vertical-align: top; -} - -.params .paramtype { - font-style: italic; - vertical-align: top; -} - -.params .paramdir { - font-family: "courier new",courier,monospace; - vertical-align: top; -} - -table.mlabels { - border-spacing: 0px; -} - -td.mlabels-left { - width: 100%; - padding: 0px; -} - -td.mlabels-right { - vertical-align: bottom; - padding: 0px; - white-space: nowrap; -} - -span.mlabels { - margin-left: 8px; -} - -span.mlabel { - background-color: #728DC1; - border-top:1px solid #5373B4; - border-left:1px solid #5373B4; - border-right:1px solid #C4CFE5; - border-bottom:1px solid #C4CFE5; - text-shadow: none; - color: white; - margin-right: 4px; - padding: 2px 3px; - border-radius: 3px; - font-size: 7pt; - white-space: nowrap; - vertical-align: middle; -} - - - -/* @end */ - -/* these are for tree view when not used as main index */ - -div.directory { - margin: 10px 0px; - border-top: 1px solid #A8B8D9; - border-bottom: 1px solid #A8B8D9; - width: 100%; -} - -.directory table { - border-collapse:collapse; -} - -.directory td { - margin: 0px; - padding: 0px; - vertical-align: top; -} - -.directory td.entry { - white-space: nowrap; - padding-right: 6px; - padding-top: 3px; -} - -.directory td.entry a { - outline:none; -} - -.directory td.entry a img { - border: none; -} - -.directory td.desc { - width: 100%; - padding-left: 6px; - padding-right: 6px; - padding-top: 3px; - border-left: 1px solid rgba(0,0,0,0.05); -} - -.directory tr.even { - padding-left: 6px; - background-color: #F7F8FB; -} - -.directory img { - vertical-align: -30%; -} - -.directory .levels { - white-space: nowrap; - width: 100%; - text-align: right; - font-size: 9pt; -} - -.directory .levels span { - cursor: pointer; - padding-left: 2px; - padding-right: 2px; - color: #3D578C; -} - -div.dynheader { - margin-top: 8px; - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -address { - font-style: normal; - color: #2A3D61; -} - -table.doxtable { - border-collapse:collapse; - margin-top: 4px; - margin-bottom: 4px; -} - -table.doxtable td, table.doxtable th { - border: 1px solid #2D4068; - padding: 3px 7px 2px; -} - -table.doxtable th { - background-color: #374F7F; - color: #FFFFFF; - font-size: 110%; - padding-bottom: 4px; - padding-top: 5px; -} - -table.fieldtable { - /*width: 100%;*/ - margin-bottom: 10px; - border: 1px solid #A8B8D9; - border-spacing: 0px; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; - -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); - box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); -} - -.fieldtable td, .fieldtable th { - padding: 3px 7px 2px; -} - -.fieldtable td.fieldtype, .fieldtable td.fieldname { - white-space: nowrap; - border-right: 1px solid #A8B8D9; - border-bottom: 1px solid #A8B8D9; - vertical-align: top; -} - -.fieldtable td.fieldname { - padding-top: 3px; -} - -.fieldtable td.fielddoc { - border-bottom: 1px solid #A8B8D9; - /*width: 100%;*/ -} - -.fieldtable td.fielddoc p:first-child { - margin-top: 0px; -} - -.fieldtable td.fielddoc p:last-child { - margin-bottom: 2px; -} - -.fieldtable tr:last-child td { - border-bottom: none; -} - -.fieldtable th { - background-image:url('nav_f.png'); - background-repeat:repeat-x; - background-color: #E2E8F2; - font-size: 90%; - color: #253555; - padding-bottom: 4px; - padding-top: 5px; - text-align:left; - -moz-border-radius-topleft: 4px; - -moz-border-radius-topright: 4px; - -webkit-border-top-left-radius: 4px; - -webkit-border-top-right-radius: 4px; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - border-bottom: 1px solid #A8B8D9; -} - - -.tabsearch { - top: 0px; - left: 10px; - height: 36px; - background-image: url('tab_b.png'); - z-index: 101; - overflow: hidden; - font-size: 13px; -} - -.navpath ul -{ - font-size: 11px; - background-image:url('tab_b.png'); - background-repeat:repeat-x; - background-position: 0 -5px; - height:30px; - line-height:30px; - color:#8AA0CC; - border:solid 1px #C2CDE4; - overflow:hidden; - margin:0px; - padding:0px; -} - -.navpath li -{ - list-style-type:none; - float:left; - padding-left:10px; - padding-right:15px; - background-image:url('bc_s.png'); - background-repeat:no-repeat; - background-position:right; - color:#364D7C; -} - -.navpath li.navelem a -{ - height:32px; - display:block; - text-decoration: none; - outline: none; - color: #283A5D; - font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; - text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); - text-decoration: none; -} - -.navpath li.navelem a:hover -{ - color:#6884BD; -} - -.navpath li.footer -{ - list-style-type:none; - float:right; - padding-left:10px; - padding-right:15px; - background-image:none; - background-repeat:no-repeat; - background-position:right; - color:#364D7C; - font-size: 8pt; -} - - -div.summary -{ - float: right; - font-size: 8pt; - padding-right: 5px; - width: 50%; - text-align: right; -} - -div.summary a -{ - white-space: nowrap; -} - -div.ingroups -{ - font-size: 8pt; - width: 50%; - text-align: left; -} - -div.ingroups a -{ - white-space: nowrap; -} - -div.header -{ - background-image:url('nav_h.png'); - background-repeat:repeat-x; - background-color: #F9FAFC; - margin: 0px; - border-bottom: 1px solid #C4CFE5; -} - -div.headertitle -{ - padding: 5px 5px 5px 10px; -} - -dl -{ - padding: 0 0 0 10px; -} - -/* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug */ -dl.section -{ - margin-left: 0px; - padding-left: 0px; -} - -dl.note -{ - margin-left:-7px; - padding-left: 3px; - border-left:4px solid; - border-color: #D0C000; -} - -dl.warning, dl.attention -{ - margin-left:-7px; - padding-left: 3px; - border-left:4px solid; - border-color: #FF0000; -} - -dl.pre, dl.post, dl.invariant -{ - margin-left:-7px; - padding-left: 3px; - border-left:4px solid; - border-color: #00D000; -} - -dl.deprecated -{ - margin-left:-7px; - padding-left: 3px; - border-left:4px solid; - border-color: #505050; -} - -dl.todo -{ - margin-left:-7px; - padding-left: 3px; - border-left:4px solid; - border-color: #00C0E0; -} - -dl.test -{ - margin-left:-7px; - padding-left: 3px; - border-left:4px solid; - border-color: #3030E0; -} - -dl.bug -{ - margin-left:-7px; - padding-left: 3px; - border-left:4px solid; - border-color: #C08050; -} - -dl.section dd { - margin-bottom: 6px; -} - - -#projectlogo -{ - text-align: center; - vertical-align: bottom; - border-collapse: separate; -} - -#projectlogo img -{ - border: 0px none; -} - #projectname { - border: 0px none; - font: 300% Tahoma, Arial,sans-serif; - margin: 0px; - padding: 2px 0px; + border: 0px none; } - #projectbrief { - font: 60% Tahoma, Arial,sans-serif; - margin: 0px; - padding: 0px; + font: 60% Tahoma, Arial,sans-serif; } - #projectnumber { - font: 80% Tahoma, Arial,sans-serif; - margin: 0px; - padding: 0px; -} - -#titlearea -{ - padding: 0px; - margin: 0px; - width: 100%; - border-bottom: 1px solid #5373B4; -} - -.image -{ - text-align: center; -} - -.dotgraph -{ - text-align: center; -} - -.mscgraph -{ - text-align: center; -} - -.diagraph -{ - text-align: center; -} - -.caption -{ - font-weight: bold; -} - -div.zoom -{ - border: 1px solid #90A5CE; -} - -dl.citelist { - margin-bottom:50px; -} - -dl.citelist dt { - color:#334975; - float:left; - font-weight:bold; - margin-right:10px; - padding:5px; -} - -dl.citelist dd { - margin:2px 0; - padding:5px 0; -} - -div.toc { - padding: 14px 25px; - background-color: #F4F6FA; - border: 1px solid #D8DFEE; - border-radius: 7px 7px 7px 7px; - float: right; - height: auto; - margin: 0 20px 10px 10px; - width: 200px; -} - -div.toc li { - background: url("bdwn.png") no-repeat scroll 0 5px transparent; - font: 10px/1.2 Verdana,DejaVu Sans,Geneva,sans-serif; - margin-top: 5px; - padding-left: 10px; - padding-top: 2px; -} - -div.toc h3 { - font: bold 12px/1.2 Arial,FreeSans,sans-serif; - color: #4665A2; - border-bottom: 0 none; - margin: 0; -} - -div.toc ul { - list-style: none outside none; - border: medium none; - padding: 0px; -} - -div.toc li.level1 { - margin-left: 0px; -} - -div.toc li.level2 { - margin-left: 15px; -} - -div.toc li.level3 { - margin-left: 30px; -} - -div.toc li.level4 { - margin-left: 45px; -} - -.inherit_header { - font-weight: bold; - color: gray; - cursor: pointer; - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.inherit_header td { - padding: 6px 0px 2px 5px; -} - -.inherit { - display: none; -} - -tr.heading h2 { - margin-top: 12px; - margin-bottom: 4px; -} - -/* tooltip related style info */ - -.ttc { - position: absolute; - display: none; -} - -#powerTip { - cursor: default; - white-space: nowrap; - background-color: white; - border: 1px solid gray; - border-radius: 4px 4px 4px 4px; - box-shadow: 1px 1px 7px gray; - display: none; - font-size: smaller; - max-width: 80%; - opacity: 0.9; - padding: 1ex 1em 1em; - position: absolute; - z-index: 2147483647; -} - -#powerTip div.ttdoc { - color: grey; - font-style: italic; -} - -#powerTip div.ttname a { - font-weight: bold; -} - -#powerTip div.ttname { - font-weight: bold; -} - -#powerTip div.ttdeci { - color: #006318; -} - -#powerTip div { - margin: 0px; - padding: 0px; - font: 12px/16px Roboto,sans-serif; -} - -#powerTip:before, #powerTip:after { - content: ""; - position: absolute; - margin: 0px; -} - -#powerTip.n:after, #powerTip.n:before, -#powerTip.s:after, #powerTip.s:before, -#powerTip.w:after, #powerTip.w:before, -#powerTip.e:after, #powerTip.e:before, -#powerTip.ne:after, #powerTip.ne:before, -#powerTip.se:after, #powerTip.se:before, -#powerTip.nw:after, #powerTip.nw:before, -#powerTip.sw:after, #powerTip.sw:before { - border: solid transparent; - content: " "; - height: 0; - width: 0; - position: absolute; -} - -#powerTip.n:after, #powerTip.s:after, -#powerTip.w:after, #powerTip.e:after, -#powerTip.nw:after, #powerTip.ne:after, -#powerTip.sw:after, #powerTip.se:after { - border-color: rgba(255, 255, 255, 0); -} - -#powerTip.n:before, #powerTip.s:before, -#powerTip.w:before, #powerTip.e:before, -#powerTip.nw:before, #powerTip.ne:before, -#powerTip.sw:before, #powerTip.se:before { - border-color: rgba(128, 128, 128, 0); -} - -#powerTip.n:after, #powerTip.n:before, -#powerTip.ne:after, #powerTip.ne:before, -#powerTip.nw:after, #powerTip.nw:before { - top: 100%; -} - -#powerTip.n:after, #powerTip.ne:after, #powerTip.nw:after { - border-top-color: #ffffff; - border-width: 10px; - margin: 0px -10px; -} -#powerTip.n:before { - border-top-color: #808080; - border-width: 11px; - margin: 0px -11px; -} -#powerTip.n:after, #powerTip.n:before { - left: 50%; -} - -#powerTip.nw:after, #powerTip.nw:before { - right: 14px; -} - -#powerTip.ne:after, #powerTip.ne:before { - left: 14px; -} - -#powerTip.s:after, #powerTip.s:before, -#powerTip.se:after, #powerTip.se:before, -#powerTip.sw:after, #powerTip.sw:before { - bottom: 100%; -} - -#powerTip.s:after, #powerTip.se:after, #powerTip.sw:after { - border-bottom-color: #ffffff; - border-width: 10px; - margin: 0px -10px; -} - -#powerTip.s:before, #powerTip.se:before, #powerTip.sw:before { - border-bottom-color: #808080; - border-width: 11px; - margin: 0px -11px; -} - -#powerTip.s:after, #powerTip.s:before { - left: 50%; -} - -#powerTip.sw:after, #powerTip.sw:before { - right: 14px; -} - -#powerTip.se:after, #powerTip.se:before { - left: 14px; -} - -#powerTip.e:after, #powerTip.e:before { - left: 100%; -} -#powerTip.e:after { - border-left-color: #ffffff; - border-width: 10px; - top: 50%; - margin-top: -10px; -} -#powerTip.e:before { - border-left-color: #808080; - border-width: 11px; - top: 50%; - margin-top: -11px; -} - -#powerTip.w:after, #powerTip.w:before { - right: 100%; -} -#powerTip.w:after { - border-right-color: #ffffff; - border-width: 10px; - top: 50%; - margin-top: -10px; -} -#powerTip.w:before { - border-right-color: #808080; - border-width: 11px; - top: 50%; - margin-top: -11px; + font: 80% Tahoma, Arial,sans-serif; } - -@media print +.arrow { - #top { display: none; } - #side-nav { display: none; } - #nav-path { display: none; } - body { overflow:visible; } - h1, h2, h3, h4, h5, h6 { page-break-after: avoid; } - .summary { display: none; } - .memitem { page-break-inside: avoid; } - #doc-content - { - margin-left:0 !important; - height:auto !important; - width:auto !important; - overflow:inherit; - display:inline; - } + width: auto; + height: auto; + padding-left: 16px; } - -- cgit v1.2.3 From 6da9c23287bd98f018b3817d71e6c78fbd6665a7 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Wed, 1 Jun 2022 18:51:53 +0200 Subject: fix doc --- src/python/gudhi/tensorflow/cubical_layer.py | 2 +- src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py | 2 +- src/python/gudhi/tensorflow/rips_layer.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/tensorflow/cubical_layer.py b/src/python/gudhi/tensorflow/cubical_layer.py index e8674d7b..918ff43e 100644 --- a/src/python/gudhi/tensorflow/cubical_layer.py +++ b/src/python/gudhi/tensorflow/cubical_layer.py @@ -58,7 +58,7 @@ class CubicalLayer(tf.keras.layers.Layer): X (TensorFlow variable): pixel values of the cubical complex Returns: - dgms (list of tuple of TensorFlow variables): list of cubical persistence diagrams. The length of this list is the same than that of dimensions, i.e., there is one persistence diagram per homology dimension provided in the input list dimensions. Moreover, the finite and essential parts of the persistence diagrams are provided separately: each element of this list is a tuple of size two that contains the finite and essential parts of the corresponding persistence diagram, of shapes [num_finite_points, 2] and [num_essential_points, 1] respectively. Note that the essential part is always empty in cubical persistence diagrams, except in homology dimension zero, where the essential part always contains a single point, with abscissa equal to the smallest value in the complex, and infinite ordinate + List[Tuple[tf.Tensor,tf.Tensor]]: List of cubical persistence diagrams. The length of this list is the same than that of dimensions, i.e., there is one persistence diagram per homology dimension provided in the input list dimensions. Moreover, the finite and essential parts of the persistence diagrams are provided separately: each element of this list is a tuple of size two that contains the finite and essential parts of the corresponding persistence diagram, of shapes [num_finite_points, 2] and [num_essential_points, 1] respectively. Note that the essential part is always empty in cubical persistence diagrams, except in homology dimension zero, where the essential part always contains a single point, with abscissa equal to the smallest value in the complex, and infinite ordinate """ # Compute pixels associated to positive and negative simplices # Don't compute gradient for this operation diff --git a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py index a2e48d8a..5606d1a4 100644 --- a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py +++ b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py @@ -65,7 +65,7 @@ class LowerStarSimplexTreeLayer(tf.keras.layers.Layer): F (TensorFlow variable): filter function values over the vertices of the simplex tree. The ith entry of F corresponds to vertex i in self.simplextree Returns: - dgms (list of tuple of TensorFlow variables): list of lower-star persistence diagrams. The length of this list is the same than that of dimensions, i.e., there is one persistence diagram per homology dimension provided in the input list dimensions. Moreover, the finite and essential parts of the persistence diagrams are provided separately: each element of this list is a tuple of size two that contains the finite and essential parts of the corresponding persistence diagram, of shapes [num_finite_points, 2] and [num_essential_points, 1] respectively + List[Tuple[tf.Tensor,tf.Tensor]]: List of lower-star persistence diagrams. The length of this list is the same than that of dimensions, i.e., there is one persistence diagram per homology dimension provided in the input list dimensions. Moreover, the finite and essential parts of the persistence diagrams are provided separately: each element of this list is a tuple of size two that contains the finite and essential parts of the corresponding persistence diagram, of shapes [num_finite_points, 2] and [num_essential_points, 1] respectively """ # Don't try to compute gradients for the vertex pairs indices = _LowerStarSimplexTree(self.simplextree, filtration.numpy(), self.dimensions) diff --git a/src/python/gudhi/tensorflow/rips_layer.py b/src/python/gudhi/tensorflow/rips_layer.py index b5b58ab4..97c2692d 100644 --- a/src/python/gudhi/tensorflow/rips_layer.py +++ b/src/python/gudhi/tensorflow/rips_layer.py @@ -63,7 +63,7 @@ class RipsLayer(tf.keras.layers.Layer): X (TensorFlow variable): point cloud of shape [number of points, number of dimensions] Returns: - dgms (list of tuple of TensorFlow variables): list of Rips persistence diagrams. The length of this list is the same than that of dimensions, i.e., there is one persistence diagram per homology dimension provided in the input list dimensions. Moreover, the finite and essential parts of the persistence diagrams are provided separately: each element of this list is a tuple of size two that contains the finite and essential parts of the corresponding persistence diagram, of shapes [num_finite_points, 2] and [num_essential_points, 1] respectively + List[Tuple[tf.Tensor,tf.Tensor]]: List of Rips persistence diagrams. The length of this list is the same than that of dimensions, i.e., there is one persistence diagram per homology dimension provided in the input list dimensions. Moreover, the finite and essential parts of the persistence diagrams are provided separately: each element of this list is a tuple of size two that contains the finite and essential parts of the corresponding persistence diagram, of shapes [num_finite_points, 2] and [num_essential_points, 1] respectively """ # Compute distance matrix DX = tf.norm(tf.expand_dims(X, 1)-tf.expand_dims(X, 0), axis=2) -- cgit v1.2.3 From c3199271e3e6cff0ae4e134c0409c9bb604fa1be Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Tue, 7 Jun 2022 10:47:40 +0200 Subject: fix doc + added homology field coeff --- src/python/gudhi/tensorflow/cubical_layer.py | 10 ++++++---- .../gudhi/tensorflow/lower_star_simplex_tree_layer.py | 15 +++++++++------ src/python/gudhi/tensorflow/rips_layer.py | 10 ++++++---- 3 files changed, 21 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/tensorflow/cubical_layer.py b/src/python/gudhi/tensorflow/cubical_layer.py index 918ff43e..16dc7d35 100644 --- a/src/python/gudhi/tensorflow/cubical_layer.py +++ b/src/python/gudhi/tensorflow/cubical_layer.py @@ -8,7 +8,7 @@ from ..cubical_complex import CubicalComplex # The parameters of the model are the pixel values. -def _Cubical(Xflat, Xdim, dimensions): +def _Cubical(Xflat, Xdim, dimensions, homology_coeff_field=11): # Parameters: Xflat (flattened image), # Xdim (shape of non-flattened image) # dimensions (homology dimensions) @@ -16,7 +16,7 @@ def _Cubical(Xflat, Xdim, dimensions): # Compute the persistence pairs with Gudhi # We reverse the dimensions because CubicalComplex uses Fortran ordering cc = CubicalComplex(dimensions=Xdim[::-1], top_dimensional_cells=Xflat) - cc.compute_persistence() + cc.compute_persistence(homology_coeff_field=homology_coeff_field) # Retrieve and ouput image indices/pixels corresponding to positive and negative simplices cof_pp = cc.cofaces_of_persistence_pairs() @@ -37,17 +37,19 @@ class CubicalLayer(tf.keras.layers.Layer): """ TensorFlow layer for computing the persistent homology of a cubical complex """ - def __init__(self, dimensions, min_persistence=None, **kwargs): + def __init__(self, dimensions, min_persistence=None, homology_coeff_field=11, **kwargs): """ Constructor for the CubicalLayer class Parameters: dimensions (List[int]): homology dimensions min_persistence (List[float]): minimum distance-to-diagonal of the points in the output persistence diagrams (default None, in which case 0. is used for all dimensions) + homology_coeff_field (int): homology field coefficient. Must be a prime number. Default value is 11. Max is 46337. """ super().__init__(dynamic=True, **kwargs) self.dimensions = dimensions self.min_persistence = min_persistence if min_persistence != None else [0.] * len(self.dimensions) + self.hcf = homology_coeff_field assert len(self.min_persistence) == len(self.dimensions) def call(self, X): @@ -64,7 +66,7 @@ class CubicalLayer(tf.keras.layers.Layer): # Don't compute gradient for this operation Xflat = tf.reshape(X, [-1]) Xdim, Xflat_numpy = X.shape, Xflat.numpy() - indices_list = _Cubical(Xflat_numpy, Xdim, self.dimensions) + indices_list = _Cubical(Xflat_numpy, Xdim, self.dimensions, self.hcf) index_essential = np.argmin(Xflat_numpy) # index of minimum pixel value for essential persistence diagram # Get persistence diagram by simply picking the corresponding entries in the image self.dgms = [] diff --git a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py index 5606d1a4..e0a5b457 100644 --- a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py +++ b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py @@ -7,10 +7,11 @@ import tensorflow as tf # The parameters of the model are the vertex function values of the simplex tree. -def _LowerStarSimplexTree(simplextree, filtration, dimensions): +def _LowerStarSimplexTree(simplextree, filtration, dimensions, homology_coeff_field=11): # Parameters: simplextree (simplex tree on which to compute persistence) # filtration (function values on the vertices of st), # dimensions (homology dimensions), + # homology_coeff_field (homology field coefficient) simplextree.reset_filtration(-np.inf, 0) @@ -20,7 +21,7 @@ def _LowerStarSimplexTree(simplextree, filtration, dimensions): simplextree.make_filtration_non_decreasing() # Compute persistence diagram - simplextree.compute_persistence() + simplextree.compute_persistence(homology_coeff_field=homology_coeff_field) # Get vertex pairs for optimization. First, get all simplex pairs pairs = simplextree.lower_star_persistence_generators() @@ -42,19 +43,21 @@ class LowerStarSimplexTreeLayer(tf.keras.layers.Layer): """ TensorFlow layer for computing lower-star persistence out of a simplex tree """ - def __init__(self, simplextree, dimensions, min_persistence=None, **kwargs): + def __init__(self, simplextree, dimensions, min_persistence=None, homology_coeff_field=11, **kwargs): """ Constructor for the LowerStarSimplexTreeLayer class Parameters: - simplextree (gudhi.SimplexTree): underlying simplex tree. Its vertices MUST be named with integers from 0 to n = number of vertices. Note that its filtration values are modified in each call of the class. + simplextree (gudhi.SimplexTree): underlying simplex tree. Its vertices MUST be named with integers from 0 to n-1, where n is its number of vertices. Note that its filtration values are modified in each call of the class. dimensions (List[int]): homology dimensions min_persistence (List[float]): minimum distance-to-diagonal of the points in the output persistence diagrams (default None, in which case 0. is used for all dimensions) + homology_coeff_field (int): homology field coefficient. Must be a prime number. Default value is 11. Max is 46337. """ super().__init__(dynamic=True, **kwargs) self.dimensions = dimensions self.simplextree = simplextree - self.min_persistence = min_persistence if min_persistence != None else [0. for _ in range(len(self.dimensions))] + self.min_persistence = min_persistence if min_persistence != None else [0. for _ in range(len(self.dimensions))] + self.hcf = homology_coeff_field assert len(self.min_persistence) == len(self.dimensions) def call(self, filtration): @@ -68,7 +71,7 @@ class LowerStarSimplexTreeLayer(tf.keras.layers.Layer): List[Tuple[tf.Tensor,tf.Tensor]]: List of lower-star persistence diagrams. The length of this list is the same than that of dimensions, i.e., there is one persistence diagram per homology dimension provided in the input list dimensions. Moreover, the finite and essential parts of the persistence diagrams are provided separately: each element of this list is a tuple of size two that contains the finite and essential parts of the corresponding persistence diagram, of shapes [num_finite_points, 2] and [num_essential_points, 1] respectively """ # Don't try to compute gradients for the vertex pairs - indices = _LowerStarSimplexTree(self.simplextree, filtration.numpy(), self.dimensions) + indices = _LowerStarSimplexTree(self.simplextree, filtration.numpy(), self.dimensions, self.hcf) # Get persistence diagrams self.dgms = [] for idx_dim, dimension in enumerate(self.dimensions): diff --git a/src/python/gudhi/tensorflow/rips_layer.py b/src/python/gudhi/tensorflow/rips_layer.py index 97c2692d..e4d6d4c6 100644 --- a/src/python/gudhi/tensorflow/rips_layer.py +++ b/src/python/gudhi/tensorflow/rips_layer.py @@ -8,7 +8,7 @@ from ..rips_complex import RipsComplex # The parameters of the model are the point coordinates. -def _Rips(DX, max_edge, dimensions): +def _Rips(DX, max_edge, dimensions, homology_coeff_field=11): # Parameters: DX (distance matrix), # max_edge (maximum edge length for Rips filtration), # dimensions (homology dimensions) @@ -16,7 +16,7 @@ def _Rips(DX, max_edge, dimensions): # Compute the persistence pairs with Gudhi rc = RipsComplex(distance_matrix=DX, max_edge_length=max_edge) st = rc.create_simplex_tree(max_dimension=max(dimensions)+1) - st.compute_persistence() + st.compute_persistence(homology_coeff_field=homology_coeff_field) pairs = st.flag_persistence_generators() L_indices = [] @@ -40,7 +40,7 @@ class RipsLayer(tf.keras.layers.Layer): """ TensorFlow layer for computing Rips persistence out of a point cloud """ - def __init__(self, dimensions, maximum_edge_length=np.inf, min_persistence=None, **kwargs): + def __init__(self, dimensions, maximum_edge_length=np.inf, min_persistence=None, homology_coeff_field=11, **kwargs): """ Constructor for the RipsLayer class @@ -48,11 +48,13 @@ class RipsLayer(tf.keras.layers.Layer): maximum_edge_length (float): maximum edge length for the Rips complex dimensions (List[int]): homology dimensions min_persistence (List[float]): minimum distance-to-diagonal of the points in the output persistence diagrams (default None, in which case 0. is used for all dimensions) + homology_coeff_field (int): homology field coefficient. Must be a prime number. Default value is 11. Max is 46337. """ super().__init__(dynamic=True, **kwargs) self.max_edge = maximum_edge_length self.dimensions = dimensions self.min_persistence = min_persistence if min_persistence != None else [0. for _ in range(len(self.dimensions))] + self.hcf = homology_coeff_field assert len(self.min_persistence) == len(self.dimensions) def call(self, X): @@ -69,7 +71,7 @@ class RipsLayer(tf.keras.layers.Layer): DX = tf.norm(tf.expand_dims(X, 1)-tf.expand_dims(X, 0), axis=2) # Compute vertices associated to positive and negative simplices # Don't compute gradient for this operation - indices = _Rips(DX.numpy(), self.max_edge, self.dimensions) + indices = _Rips(DX.numpy(), self.max_edge, self.dimensions, self.hcf) # Get persistence diagrams by simply picking the corresponding entries in the distance matrix self.dgms = [] for idx_dim, dimension in enumerate(self.dimensions): -- cgit v1.2.3 From 0ac4c3383495a78c7a7e5dab9eb573df49f32004 Mon Sep 17 00:00:00 2001 From: albert-github Date: Wed, 8 Jun 2022 12:41:38 +0200 Subject: issue #613 [cpp documentation] Footer needs to be generated with a more recent version of doxygen With the doxygen versions <= 1.9.2 the default setting 'overflow: hidden;' causes problems. With the commit: ``` Commit: 590198b416cd53313d150428d2f912586065ea0d [590198b] Date: Wednesday, December 1, 2021 1:37:26 PM issue #8924 Horizontal scroll bar missing in HTML for wide class="dotgraph" objects ``` for the doxygen 1.9.3 version this has already been corrected but to run properly with the <= 1.9.2 version this setting is required --- src/common/doc/stylesheet.css | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src') diff --git a/src/common/doc/stylesheet.css b/src/common/doc/stylesheet.css index f31f5df4..fb030e1f 100755 --- a/src/common/doc/stylesheet.css +++ b/src/common/doc/stylesheet.css @@ -16,3 +16,13 @@ height: auto; padding-left: 16px; } +// With the doxygen versions <= 1.9.2 the default setting 'overflow: hidden;' causes problems. +// With the commit: +// Commit: 590198b416cd53313d150428d2f912586065ea0d [590198b] +// Date: Wednesday, December 1, 2021 1:37:26 PM +// issue #8924 Horizontal scroll bar missing in HTML for wide class="dotgraph" objects +// for the doxygen 1.9.3 version this has already been corrected but to run properly with the <= 1.9.2 version +// this setting is required +ul { + overflow: visible; +} -- cgit v1.2.3 From 45d8f7c9b84d6123d117298eea38310117cc06f8 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Thu, 9 Jun 2022 15:44:53 +0200 Subject: removed default field coefficient --- src/python/gudhi/tensorflow/cubical_layer.py | 2 +- src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py | 2 +- src/python/gudhi/tensorflow/rips_layer.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/tensorflow/cubical_layer.py b/src/python/gudhi/tensorflow/cubical_layer.py index 16dc7d35..d68c7556 100644 --- a/src/python/gudhi/tensorflow/cubical_layer.py +++ b/src/python/gudhi/tensorflow/cubical_layer.py @@ -8,7 +8,7 @@ from ..cubical_complex import CubicalComplex # The parameters of the model are the pixel values. -def _Cubical(Xflat, Xdim, dimensions, homology_coeff_field=11): +def _Cubical(Xflat, Xdim, dimensions, homology_coeff_field): # Parameters: Xflat (flattened image), # Xdim (shape of non-flattened image) # dimensions (homology dimensions) diff --git a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py index e0a5b457..4ec3f7c7 100644 --- a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py +++ b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py @@ -7,7 +7,7 @@ import tensorflow as tf # The parameters of the model are the vertex function values of the simplex tree. -def _LowerStarSimplexTree(simplextree, filtration, dimensions, homology_coeff_field=11): +def _LowerStarSimplexTree(simplextree, filtration, dimensions, homology_coeff_field): # Parameters: simplextree (simplex tree on which to compute persistence) # filtration (function values on the vertices of st), # dimensions (homology dimensions), diff --git a/src/python/gudhi/tensorflow/rips_layer.py b/src/python/gudhi/tensorflow/rips_layer.py index e4d6d4c6..fca336f3 100644 --- a/src/python/gudhi/tensorflow/rips_layer.py +++ b/src/python/gudhi/tensorflow/rips_layer.py @@ -8,7 +8,7 @@ from ..rips_complex import RipsComplex # The parameters of the model are the point coordinates. -def _Rips(DX, max_edge, dimensions, homology_coeff_field=11): +def _Rips(DX, max_edge, dimensions, homology_coeff_field): # Parameters: DX (distance matrix), # max_edge (maximum edge length for Rips filtration), # dimensions (homology dimensions) -- cgit v1.2.3 From 4f4fef6c14afa8371d9273b7161c78213bfcd6f6 Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Mon, 13 Jun 2022 16:40:09 +0200 Subject: Move new option in the correct file. Disable every option when only build cpp documentation --- CMakeLists.txt | 3 --- src/cmake/modules/GUDHI_options.cmake | 9 +++++++++ 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/CMakeLists.txt b/CMakeLists.txt index 47d87cd1..f946e111 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,3 @@ - -option(WITH_GUDHI_CPP_DOCUMENTATION_ONLY "Build only the GUDHI C++ documentation (with doxygen)." OFF) - cmake_minimum_required(VERSION 3.5) project(GUDHIdev) diff --git a/src/cmake/modules/GUDHI_options.cmake b/src/cmake/modules/GUDHI_options.cmake index bffb3ffc..6655d605 100644 --- a/src/cmake/modules/GUDHI_options.cmake +++ b/src/cmake/modules/GUDHI_options.cmake @@ -3,3 +3,12 @@ option(WITH_GUDHI_EXAMPLE "Activate/deactivate examples compilation and installa option(WITH_GUDHI_PYTHON "Activate/deactivate python module compilation and installation" ON) option(WITH_GUDHI_TEST "Activate/deactivate examples compilation and installation" ON) option(WITH_GUDHI_UTILITIES "Activate/deactivate utilities compilation and installation" ON) +option(WITH_GUDHI_CPP_DOCUMENTATION_ONLY "Build only the GUDHI C++ documentation (with doxygen)." OFF) + +if (WITH_GUDHI_CPP_DOCUMENTATION_ONLY) + set (WITH_GUDHI_BENCHMARK OFF) + set (WITH_GUDHI_EXAMPLE OFF) + set (WITH_GUDHI_PYTHON OFF) + set (WITH_GUDHI_TEST OFF) + set (WITH_GUDHI_UTILITIES OFF) +endif() \ No newline at end of file -- cgit v1.2.3 From 87d153e2f8d4c2a59f380fc07862609f124dc70c Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Mon, 13 Jun 2022 16:42:11 +0200 Subject: Warns no more on standard output but in a file --- src/Doxyfile.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/Doxyfile.in b/src/Doxyfile.in index 06a74012..4af327c7 100644 --- a/src/Doxyfile.in +++ b/src/Doxyfile.in @@ -711,7 +711,7 @@ CITE_BIB_FILES = @CMAKE_SOURCE_DIR@/biblio/bibliography.bib \ # messages are off. # The default value is: NO. -QUIET = NO +QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by doxygen. If WARNINGS is set to YES @@ -765,7 +765,7 @@ WARN_FORMAT = "$file:$line: $text" # messages should be written. If left blank the output is written to standard # error (stderr). -WARN_LOGFILE = +WARN_LOGFILE = doxygen.log #--------------------------------------------------------------------------- # Configuration options related to the input files -- cgit v1.2.3 From 7d5206157185a184303b8eda42e2f9a2b39bf7d4 Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Mon, 13 Jun 2022 16:42:47 +0200 Subject: Also apply the feature for user version also --- src/CMakeLists.txt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8023e04c..a4fcfcad 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,8 +12,10 @@ set(GUDHI_MISSING_MODULES "" CACHE INTERNAL "GUDHI_MISSING_MODULES") # This variable is used by Cython CMakeLists.txt and by GUDHI_third_party_libraries to know its path set(GUDHI_PYTHON_PATH "python") -# For third parties libraries management - To be done last as CGAL updates CMAKE_MODULE_PATH -include(GUDHI_third_party_libraries NO_POLICY_SCOPE) +if (NOT WITH_GUDHI_CPP_DOCUMENTATION_ONLY) + # For third parties libraries management - To be done last as CGAL updates CMAKE_MODULE_PATH + include(GUDHI_third_party_libraries NO_POLICY_SCOPE) +endif() include(GUDHI_compilation_flags) @@ -67,7 +69,9 @@ foreach(GUDHI_MODULE ${GUDHI_MODULES}) endforeach() endforeach() -add_subdirectory(GudhUI) +if (NOT WITH_GUDHI_CPP_DOCUMENTATION_ONLY) + add_subdirectory(GudhUI) +endif() message("++ GUDHI_MODULES list is:\"${GUDHI_MODULES}\"") message("++ GUDHI_MISSING_MODULES list is:\"${GUDHI_MISSING_MODULES}\"") -- cgit v1.2.3 From c69c9eec18336d44be157e4fd6ee5261b47ddb49 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Tue, 14 Jun 2022 10:34:34 +0200 Subject: Use boundary_opposite_vertex_simplex_range instead of boundary_simplex_range in cech --- .../include/gudhi/Cech_complex_blocker.h | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h index 3141d27a..9917999f 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h +++ b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h @@ -74,25 +74,15 @@ class Cech_blocker { bool is_min_enclos_ball = false; // for each face of simplex sh, test outsider point is indeed inside enclosing ball, if yes, take it and exit loop, otherwise, new sphere is circumsphere of all vertices - for (auto face : sc_ptr_->boundary_simplex_range(sh)) { - // Find which vertex of sh is missing in face. We rely on the fact that simplex_vertex_range is sorted. - auto longlist = sc_ptr_->simplex_vertex_range(sh); - auto shortlist = sc_ptr_->simplex_vertex_range(face); - - auto longiter = std::begin(longlist); - auto shortiter = std::begin(shortlist); - auto enditer = std::end(shortlist); - while(shortiter != enditer && *longiter == *shortiter) { ++longiter; ++shortiter; } - auto extra = *longiter; // Vertex_handle - + for (auto face_opposite_vertex : sc_ptr_->boundary_opposite_vertex_simplex_range(sh)) { Sphere sph; - auto k = sc_ptr_->key(face); + auto k = sc_ptr_->key(face_opposite_vertex.first); if(k != sc_ptr_->null_key()) { sph = cc_ptr_->get_cache().at(k); } else { Point_cloud face_points; - for (auto vertex : sc_ptr_->simplex_vertex_range(face)) { + for (auto vertex : sc_ptr_->simplex_vertex_range(face_opposite_vertex.first)) { face_points.push_back(cc_ptr_->get_point(vertex)); #ifdef DEBUG_TRACES std::clog << "#(" << vertex << ")#"; @@ -100,13 +90,13 @@ class Cech_blocker { } sph = get_sphere(face_points.cbegin(), face_points.cend()); // Put edge sphere in cache - sc_ptr_->assign_key(face, cc_ptr_->get_cache().size()); + sc_ptr_->assign_key(face_opposite_vertex.first, cc_ptr_->get_cache().size()); cc_ptr_->get_cache().push_back(sph); // Clear face_points face_points.clear(); } - // Check if the minimal enclosing ball of current face contains the extra point - if (kernel_.squared_distance_d_object()(sph.first, cc_ptr_->get_point(extra)) <= sph.second) { + // Check if the minimal enclosing ball of current face contains the extra point/opposite vertex + if (kernel_.squared_distance_d_object()(sph.first, cc_ptr_->get_point(face_opposite_vertex.second)) <= sph.second) { #ifdef DEBUG_TRACES std::clog << "center: " << sph.first << ", radius: " << radius << std::endl; #endif // DEBUG_TRACES -- cgit v1.2.3 From ccb0e44f107d1d09c178620566eeefd02feb6a4e Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Wed, 15 Jun 2022 08:35:58 +0200 Subject: doc review: no need 'for this' --- src/common/doc/installation.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/common/doc/installation.h b/src/common/doc/installation.h index 131130f8..b97142b6 100644 --- a/src/common/doc/installation.h +++ b/src/common/doc/installation.h @@ -42,8 +42,8 @@ make \endverbatim * \verbatim ctest --output-on-failure \endverbatim * * \subsection documentationgeneration C++ documentation - * To generate the C++ documentation, for this the doxygen program - * is required, run the following command in a terminal: + * To generate the C++ documentation, the doxygen program + * is required. Run the following command in a terminal: * \verbatim make doxygen \endverbatim * Documentation will be generated in a folder named html. * -- cgit v1.2.3 From bcc2c9584dc07d1cfcb870746110c524827d3bfa Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Wed, 15 Jun 2022 09:35:49 +0200 Subject: Use boundary_opposite_vertex_simplex_range instead of boundary_simplex_range in alpha --- src/Alpha_complex/include/gudhi/Alpha_complex.h | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex.h b/src/Alpha_complex/include/gudhi/Alpha_complex.h index b1a9407b..5a0f0643 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex.h @@ -464,7 +464,8 @@ class Alpha_complex { using Vertex_handle = typename SimplicialComplexForAlpha::Vertex_handle; // ### Foreach Tau face of Sigma - for (auto f_boundary : complex.boundary_simplex_range(f_simplex)) { + for (auto face_opposite_vertex : complex.boundary_opposite_vertex_simplex_range(f_simplex)) { + auto f_boundary = face_opposite_vertex.first; #ifdef DEBUG_TRACES std::clog << " | --------------------------------------------------\n"; std::clog << " | Tau "; @@ -485,16 +486,8 @@ class Alpha_complex { #endif // DEBUG_TRACES // ### Else } else { - // Find which vertex of f_simplex is missing in f_boundary. We could actually write a variant of boundary_simplex_range that gives pairs (f_boundary, vertex). We rely on the fact that simplex_vertex_range is sorted. - auto longlist = complex.simplex_vertex_range(f_simplex); - auto shortlist = complex.simplex_vertex_range(f_boundary); - auto longiter = std::begin(longlist); - auto shortiter = std::begin(shortlist); - auto enditer = std::end(shortlist); - while(shortiter != enditer && *longiter == *shortiter) { ++longiter; ++shortiter; } - Vertex_handle extra = *longiter; auto const& cache=get_cache(complex, f_boundary); - bool is_gab = kernel_.is_gabriel(cache, get_point_(extra)); + bool is_gab = kernel_.is_gabriel(cache, get_point_(face_opposite_vertex.second)); #ifdef DEBUG_TRACES std::clog << " | Tau is_gabriel(Sigma)=" << is_gab << " - vertexForGabriel=" << extra << std::endl; #endif // DEBUG_TRACES -- cgit v1.2.3 From 70cc462f71703f011fd5b8ba9da668b58f09059c Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Wed, 15 Jun 2022 09:41:57 +0200 Subject: Fix warning and debug traces --- src/Alpha_complex/include/gudhi/Alpha_complex.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex.h b/src/Alpha_complex/include/gudhi/Alpha_complex.h index 5a0f0643..aec8c1b1 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex.h @@ -461,7 +461,6 @@ class Alpha_complex { void propagate_alpha_filtration(SimplicialComplexForAlpha& complex, Simplex_handle f_simplex) { // From SimplicialComplexForAlpha type required to assign filtration values. using Filtration_value = typename SimplicialComplexForAlpha::Filtration_value; - using Vertex_handle = typename SimplicialComplexForAlpha::Vertex_handle; // ### Foreach Tau face of Sigma for (auto face_opposite_vertex : complex.boundary_opposite_vertex_simplex_range(f_simplex)) { @@ -489,7 +488,7 @@ class Alpha_complex { auto const& cache=get_cache(complex, f_boundary); bool is_gab = kernel_.is_gabriel(cache, get_point_(face_opposite_vertex.second)); #ifdef DEBUG_TRACES - std::clog << " | Tau is_gabriel(Sigma)=" << is_gab << " - vertexForGabriel=" << extra << std::endl; + std::clog << " | Tau is_gabriel(Sigma)=" << is_gab << " - vertexForGabriel=" << face_opposite_vertex.second << std::endl; #endif // DEBUG_TRACES // ### If Tau is not Gabriel of Sigma if (false == is_gab) { -- cgit v1.2.3 From 868369dd61fb6ef475ffa3af724907927121b6bb Mon Sep 17 00:00:00 2001 From: Hind-M Date: Thu, 16 Jun 2022 15:54:21 +0200 Subject: Add exact option for exact cech variant --- .../benchmark/cech_complex_benchmark.cpp | 22 ++++++++++++++-------- src/Cech_complex/include/gudhi/Cech_complex.h | 6 ++++-- .../include/gudhi/Cech_complex_blocker.h | 21 ++++++++++++++------- 3 files changed, 32 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/benchmark/cech_complex_benchmark.cpp b/src/Cech_complex/benchmark/cech_complex_benchmark.cpp index d2a71879..19142780 100644 --- a/src/Cech_complex/benchmark/cech_complex_benchmark.cpp +++ b/src/Cech_complex/benchmark/cech_complex_benchmark.cpp @@ -31,7 +31,7 @@ using Points_off_reader = Gudhi::Points_off_reader; using Rips_complex = Gudhi::rips_complex::Rips_complex; template -Simplex_tree benchmark_cech(const std::string& off_file_points, const Filtration_value& radius, const int& dim_max) { +Simplex_tree benchmark_cech(const std::string& off_file_points, const Filtration_value& radius, const int& dim_max, const bool exact) { using Point_cgal = typename Kernel::Point_d; using Points_off_reader_cgal = Gudhi::Points_off_reader; using Cech_complex = Gudhi::cech_complex::Cech_complex; @@ -42,7 +42,7 @@ Simplex_tree benchmark_cech(const std::string& off_file_points, const Filtration Gudhi::Clock cech_clock("Cech computation"); Cech_complex cech_complex_from_points(off_reader_cgal.get_point_cloud(), radius); Simplex_tree cech_stree; - cech_complex_from_points.create_complex(cech_stree, dim_max); + cech_complex_from_points.create_complex(cech_stree, dim_max, exact); // ------------------------------------------ // Display information about the Cech complex @@ -56,8 +56,9 @@ int main(int argc, char* argv[]) { boost::filesystem::path full_path(boost::filesystem::current_path()); std::clog << "Current path is : " << full_path << std::endl; - std::clog << "File name ; Radius ; Rips time ; Dim-3 Epick Cech time ; Dynamic_dim Epick Cech time ; " - "Dim-3 Epeck Cech time ; Dynamic_dim Epeck Cech time ; Cech nb simplices ; Rips nb simplices;" + std::clog << "File name ; Radius ; Rips time ; Dim-3 Fast Cech time ; Dynamic_dim Fast Cech time ; " + "Dim-3 Safe Cech time ; Dynamic_dim Safe Cech time ; Dim-3 Exact Cech time ; Dynamic_dim Exact Cech time ; " + "Cech nb simplices ; Rips nb simplices;" << std::endl; boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end for (boost::filesystem::directory_iterator itr(boost::filesystem::current_path()); itr != end_itr; ++itr) { @@ -83,10 +84,15 @@ int main(int argc, char* argv[]) { // -------------- // Cech complex // -------------- - benchmark_cech>>(itr->path().string(), radius, p0.size() - 1); - benchmark_cech>(itr->path().string(), radius, p0.size() - 1); - benchmark_cech>>(itr->path().string(), radius, p0.size() - 1); - auto cech_stree = benchmark_cech>(itr->path().string(), radius, p0.size() - 1); + // Fast + benchmark_cech>>(itr->path().string(), radius, p0.size() - 1, false); + benchmark_cech>(itr->path().string(), radius, p0.size() - 1, false); + // Safe + benchmark_cech>>(itr->path().string(), radius, p0.size() - 1, false); + benchmark_cech>(itr->path().string(), radius, p0.size() - 1, false); + // Exact + benchmark_cech>>(itr->path().string(), radius, p0.size() - 1, true); + auto cech_stree = benchmark_cech>(itr->path().string(), radius, p0.size() - 1, true); std::clog << cech_stree.num_simplices() << " ; "; std::clog << rips_stree.num_simplices() << ";" << std::endl; diff --git a/src/Cech_complex/include/gudhi/Cech_complex.h b/src/Cech_complex/include/gudhi/Cech_complex.h index fc39f75b..2c6d3df5 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex.h +++ b/src/Cech_complex/include/gudhi/Cech_complex.h @@ -78,17 +78,19 @@ class Cech_complex { * * @param[in] complex SimplicialComplexForCech to be created. * @param[in] dim_max graph expansion until this given maximal dimension. + * @param[in] exact Exact filtration values computation. Not exact if `Kernel` is not CGAL::Epeck_d. * @exception std::invalid_argument In debug mode, if `complex.num_vertices()` does not return 0. * */ - void create_complex(SimplicialComplexForCechComplex& complex, int dim_max) { + void create_complex(SimplicialComplexForCechComplex& complex, int dim_max, const bool exact = false) { GUDHI_CHECK(complex.num_vertices() == 0, std::invalid_argument("Cech_complex::create_complex - simplicial complex is not empty")); // insert the proximity graph in the simplicial complex complex.insert_graph(cech_skeleton_graph_); // expand the graph until dimension dim_max - complex.expansion_with_blockers(dim_max, cech_blocker(&complex, this)); + complex.expansion_with_blockers(dim_max, cech_blocker(&complex, this, exact)); } /** @return max_radius value given at construction. */ diff --git a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h index 3141d27a..087390b6 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h +++ b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h @@ -94,9 +94,9 @@ class Cech_blocker { Point_cloud face_points; for (auto vertex : sc_ptr_->simplex_vertex_range(face)) { face_points.push_back(cc_ptr_->get_point(vertex)); - #ifdef DEBUG_TRACES - std::clog << "#(" << vertex << ")#"; - #endif // DEBUG_TRACES +#ifdef DEBUG_TRACES + std::clog << "#(" << vertex << ")#"; +#endif // DEBUG_TRACES } sph = get_sphere(face_points.cbegin(), face_points.cend()); // Put edge sphere in cache @@ -107,10 +107,13 @@ class Cech_blocker { } // Check if the minimal enclosing ball of current face contains the extra point if (kernel_.squared_distance_d_object()(sph.first, cc_ptr_->get_point(extra)) <= sph.second) { - #ifdef DEBUG_TRACES - std::clog << "center: " << sph.first << ", radius: " << radius << std::endl; - #endif // DEBUG_TRACES +#ifdef DEBUG_TRACES + std::clog << "center: " << sph.first << ", radius: " << radius << std::endl; +#endif // DEBUG_TRACES is_min_enclos_ball = true; +#if CGAL_VERSION_NR >= 1050000000 + if(exact_) CGAL::exact(sph.second); +#endif radius = std::sqrt(cast_to_fv(sph.second)); sc_ptr_->assign_key(sh, cc_ptr_->get_cache().size()); cc_ptr_->get_cache().push_back(sph); @@ -124,6 +127,9 @@ class Cech_blocker { points.push_back(cc_ptr_->get_point(vertex)); } Sphere sph = get_sphere(points.cbegin(), points.cend()); +#if CGAL_VERSION_NR >= 1050000000 + if(exact_) CGAL::exact(sph.second); +#endif radius = std::sqrt(cast_to_fv(sph.second)); sc_ptr_->assign_key(sh, cc_ptr_->get_cache().size()); @@ -138,12 +144,13 @@ class Cech_blocker { } /** \internal \brief Čech complex blocker constructor. */ - Cech_blocker(SimplicialComplexForCech* sc_ptr, Cech_complex* cc_ptr) : sc_ptr_(sc_ptr), cc_ptr_(cc_ptr) {} + Cech_blocker(SimplicialComplexForCech* sc_ptr, Cech_complex* cc_ptr, const bool exact) : sc_ptr_(sc_ptr), cc_ptr_(cc_ptr), exact_(exact) {} private: SimplicialComplexForCech* sc_ptr_; Cech_complex* cc_ptr_; Kernel kernel_; + const bool exact_; }; } // namespace cech_complex -- cgit v1.2.3 From 3fa972970514333d4db22ec7628c5c1a4de3c6e8 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Tue, 21 Jun 2022 15:04:27 +0200 Subject: -Add/modify some comments -Some other minor changes -Change license to LGPL --- .../benchmark/cech_complex_benchmark.cpp | 20 +++++++++++--------- src/Cech_complex/include/gudhi/Cech_complex.h | 2 +- .../include/gudhi/Cech_complex_blocker.h | 2 +- src/common/doc/main_page.md | 2 +- 4 files changed, 14 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/benchmark/cech_complex_benchmark.cpp b/src/Cech_complex/benchmark/cech_complex_benchmark.cpp index 19142780..a9dc5d0d 100644 --- a/src/Cech_complex/benchmark/cech_complex_benchmark.cpp +++ b/src/Cech_complex/benchmark/cech_complex_benchmark.cpp @@ -61,20 +61,22 @@ int main(int argc, char* argv[]) { "Cech nb simplices ; Rips nb simplices;" << std::endl; boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end + // For every ".off" file in the current directory, and for 3 predefined thresholds, compare Rips and various Cech constructions for (boost::filesystem::directory_iterator itr(boost::filesystem::current_path()); itr != end_itr; ++itr) { if (!boost::filesystem::is_directory(itr->status())) { if (itr->path().extension() == ".off") { Points_off_reader off_reader(itr->path().string()); Point p0 = off_reader.get_point_cloud()[0]; - - for (Filtration_value radius = 0.1; radius < 0.4; radius += 0.1) { + // Loop over the different thresholds + for (Filtration_value radius = 0.1; radius < 0.35; radius += 0.1) { std::clog << itr->path().stem() << " ; "; std::clog << radius << " ; "; Gudhi::Clock rips_clock("Rips computation"); Rips_complex rips_complex_from_points(off_reader.get_point_cloud(), radius, Gudhi::Euclidean_distance()); Simplex_tree rips_stree; - rips_complex_from_points.create_complex(rips_stree, p0.size() - 1); + int dim_max = p0.size() - 1; + rips_complex_from_points.create_complex(rips_stree, dim_max); // ------------------------------------------ // Display information about the Rips complex // ------------------------------------------ @@ -85,14 +87,14 @@ int main(int argc, char* argv[]) { // Cech complex // -------------- // Fast - benchmark_cech>>(itr->path().string(), radius, p0.size() - 1, false); - benchmark_cech>(itr->path().string(), radius, p0.size() - 1, false); + benchmark_cech>>(itr->path().string(), radius, dim_max, false); + benchmark_cech>(itr->path().string(), radius, dim_max, false); // Safe - benchmark_cech>>(itr->path().string(), radius, p0.size() - 1, false); - benchmark_cech>(itr->path().string(), radius, p0.size() - 1, false); + benchmark_cech>>(itr->path().string(), radius, dim_max, false); + benchmark_cech>(itr->path().string(), radius, dim_max, false); // Exact - benchmark_cech>>(itr->path().string(), radius, p0.size() - 1, true); - auto cech_stree = benchmark_cech>(itr->path().string(), radius, p0.size() - 1, true); + benchmark_cech>>(itr->path().string(), radius, dim_max, true); + auto cech_stree = benchmark_cech>(itr->path().string(), radius, dim_max, true); std::clog << cech_stree.num_simplices() << " ; "; std::clog << rips_stree.num_simplices() << ";" << std::endl; diff --git a/src/Cech_complex/include/gudhi/Cech_complex.h b/src/Cech_complex/include/gudhi/Cech_complex.h index 2c6d3df5..bae21d28 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex.h +++ b/src/Cech_complex/include/gudhi/Cech_complex.h @@ -30,7 +30,7 @@ namespace cech_complex { * \ingroup cech_complex * * \details - * Cech complex is a simplicial complex constructed from a proximity graph, where the set of all simplices is filtered + * Cech complex is a simplicial complex where the set of all simplices is filtered * by the radius of their minimal enclosing ball and bounded by the given max_radius. * * \tparam Kernel CGAL kernel: either Epick_d or Epeck_d. diff --git a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h index 087390b6..9cd49a52 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h +++ b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h @@ -133,7 +133,7 @@ class Cech_blocker { radius = std::sqrt(cast_to_fv(sph.second)); sc_ptr_->assign_key(sh, cc_ptr_->get_cache().size()); - cc_ptr_->get_cache().push_back(sph); + cc_ptr_->get_cache().push_back(std::move(sph)); } #ifdef DEBUG_TRACES diff --git a/src/common/doc/main_page.md b/src/common/doc/main_page.md index 2cb02e3f..ce903405 100644 --- a/src/common/doc/main_page.md +++ b/src/common/doc/main_page.md @@ -180,7 +180,7 @@ Author: Vincent Rouvreau
Introduced in: GUDHI 2.2.0
- Copyright: MIT [(GPL v3)](../../licensing/)
+ Copyright: MIT [(LGPL v3)](../../licensing/)
Requires: \ref cgal -- cgit v1.2.3 From b829a198e16fbef4c0cb2698b2c723fa353aac55 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Fri, 24 Jun 2022 11:03:22 +0200 Subject: Use CGAL::NT_converter instead of CGAL::to_double in Sphere_circumradius --- src/Cech_complex/include/gudhi/Cech_complex.h | 2 +- src/Cech_complex/include/gudhi/Cech_complex_blocker.h | 1 + src/Cech_complex/include/gudhi/Sphere_circumradius.h | 15 +++++++++------ src/Cech_complex/test/test_cech_complex.cpp | 4 ++-- 4 files changed, 13 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/include/gudhi/Cech_complex.h b/src/Cech_complex/include/gudhi/Cech_complex.h index bae21d28..08b7a72f 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex.h +++ b/src/Cech_complex/include/gudhi/Cech_complex.h @@ -70,7 +70,7 @@ class Cech_complex { point_cloud_.assign(std::begin(points), std::end(points)); cech_skeleton_graph_ = Gudhi::compute_proximity_graph( - point_cloud_, max_radius_, Sphere_circumradius()); + point_cloud_, max_radius_, Sphere_circumradius()); } /** \brief Initializes the simplicial complex from the proximity graph and expands it until a given maximal diff --git a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h index 9cd49a52..25d9a71f 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h +++ b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h @@ -12,6 +12,7 @@ #define CECH_COMPLEX_BLOCKER_H_ #include // for casting from FT to Filtration_value +#include // for CGAL::exact #include #include diff --git a/src/Cech_complex/include/gudhi/Sphere_circumradius.h b/src/Cech_complex/include/gudhi/Sphere_circumradius.h index b0d9f7cc..790f6950 100644 --- a/src/Cech_complex/include/gudhi/Sphere_circumradius.h +++ b/src/Cech_complex/include/gudhi/Sphere_circumradius.h @@ -11,7 +11,7 @@ #ifndef SPHERE_CIRCUMRADIUS_H_ #define SPHERE_CIRCUMRADIUS_H_ -#include // for #include +#include // for #include which is not working/compiling alone #include // for std::sqrt #include @@ -22,14 +22,17 @@ namespace cech_complex { /** \private @brief Compute the circumradius of the sphere passing through points given by a range of coordinates. * The points are assumed to have the same dimension. */ -template +template class Sphere_circumradius { private: Kernel kernel_; public: + using FT = typename Kernel::FT; using Point = typename Kernel::Point_d; using Point_cloud = typename std::vector; + CGAL::NT_converter cast_to_fv; + /** \brief Circumradius of sphere passing through two points using CGAL. * * @param[in] point_1 @@ -38,8 +41,8 @@ class Sphere_circumradius { * \tparam Point must be a Kernel::Point_d from CGAL. * */ - double operator()(const Point& point_1, const Point& point_2) const { - return std::sqrt(CGAL::to_double(kernel_.squared_distance_d_object()(point_1, point_2))) / 2.; + Filtration_value operator()(const Point& point_1, const Point& point_2) const { + return std::sqrt(cast_to_fv(kernel_.squared_distance_d_object()(point_1, point_2))) / 2.; } /** \brief Circumradius of sphere passing through point cloud using CGAL. @@ -49,8 +52,8 @@ class Sphere_circumradius { * \tparam Point_cloud must be a range of Kernel::Point_d points from CGAL. * */ - double operator()(const Point_cloud& point_cloud) const { - return std::sqrt(CGAL::to_double(kernel_.compute_squared_radius_d_object()(point_cloud.begin(), point_cloud.end()))); + Filtration_value operator()(const Point_cloud& point_cloud) const { + return std::sqrt(cast_to_fv(kernel_.compute_squared_radius_d_object()(point_cloud.begin(), point_cloud.end()))); } }; diff --git a/src/Cech_complex/test/test_cech_complex.cpp b/src/Cech_complex/test/test_cech_complex.cpp index ea32f596..f5980e6d 100644 --- a/src/Cech_complex/test/test_cech_complex.cpp +++ b/src/Cech_complex/test/test_cech_complex.cpp @@ -107,11 +107,11 @@ BOOST_AUTO_TEST_CASE(Cech_complex_for_documentation) { std::clog << vertex << ","; vp.push_back(points.at(vertex)); } - std::clog << ") - distance =" << Gudhi::cech_complex::Sphere_circumradius()(vp.at(0), vp.at(1)) + std::clog << ") - distance =" << Gudhi::cech_complex::Sphere_circumradius()(vp.at(0), vp.at(1)) << " - filtration =" << st.filtration(f_simplex) << std::endl; BOOST_CHECK(vp.size() == 2); GUDHI_TEST_FLOAT_EQUALITY_CHECK(st.filtration(f_simplex), - Gudhi::cech_complex::Sphere_circumradius()(vp.at(0), vp.at(1))); + Gudhi::cech_complex::Sphere_circumradius()(vp.at(0), vp.at(1))); } } -- cgit v1.2.3 From 370c09100d94dc73f582ebbabb994bcd2a3820eb Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Fri, 24 Jun 2022 16:19:34 +0200 Subject: changed dimensions into homology_dimensions --- src/python/doc/cubical_complex_tflow_itf_ref.rst | 2 +- src/python/doc/ls_simplex_tree_tflow_itf_ref.rst | 2 +- src/python/doc/rips_complex_tflow_itf_ref.rst | 2 +- src/python/gudhi/tensorflow/cubical_layer.py | 6 +++--- src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py | 6 +++--- src/python/gudhi/tensorflow/rips_layer.py | 6 +++--- src/python/test/test_diff.py | 8 ++++---- 7 files changed, 16 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/python/doc/cubical_complex_tflow_itf_ref.rst b/src/python/doc/cubical_complex_tflow_itf_ref.rst index 18b97adf..b32f5e47 100644 --- a/src/python/doc/cubical_complex_tflow_itf_ref.rst +++ b/src/python/doc/cubical_complex_tflow_itf_ref.rst @@ -16,7 +16,7 @@ Example of gradient computed from cubical persistence import tensorflow as tf X = tf.Variable([[0.,2.,2.],[2.,2.,2.],[2.,2.,1.]], dtype=tf.float32, trainable=True) - cl = CubicalLayer(dimensions=[0]) + cl = CubicalLayer(homology_dimensions=[0]) with tf.GradientTape() as tape: dgm = cl.call(X)[0][0] diff --git a/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst b/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst index b8518cdb..9d7d633f 100644 --- a/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst +++ b/src/python/doc/ls_simplex_tree_tflow_itf_ref.rst @@ -29,7 +29,7 @@ Example of gradient computed from lower-star filtration of a simplex tree st.insert([9, 10]) F = tf.Variable([6.,4.,3.,4.,5.,4.,3.,2.,3.,4.,5.], dtype=tf.float32, trainable=True) - sl = LowerStarSimplexTreeLayer(simplextree=st, dimensions=[0]) + sl = LowerStarSimplexTreeLayer(simplextree=st, homology_dimensions=[0]) with tf.GradientTape() as tape: dgm = sl.call(F)[0][0] diff --git a/src/python/doc/rips_complex_tflow_itf_ref.rst b/src/python/doc/rips_complex_tflow_itf_ref.rst index 6c65c562..3ce75868 100644 --- a/src/python/doc/rips_complex_tflow_itf_ref.rst +++ b/src/python/doc/rips_complex_tflow_itf_ref.rst @@ -21,7 +21,7 @@ Example of gradient computed from Vietoris-Rips persistence import tensorflow as tf X = tf.Variable([[1.,1.],[2.,2.]], dtype=tf.float32, trainable=True) - rl = RipsLayer(maximum_edge_length=2., dimensions=[0]) + rl = RipsLayer(maximum_edge_length=2., homology_dimensions=[0]) with tf.GradientTape() as tape: dgm = rl.call(X)[0][0] diff --git a/src/python/gudhi/tensorflow/cubical_layer.py b/src/python/gudhi/tensorflow/cubical_layer.py index d68c7556..3304e719 100644 --- a/src/python/gudhi/tensorflow/cubical_layer.py +++ b/src/python/gudhi/tensorflow/cubical_layer.py @@ -37,17 +37,17 @@ class CubicalLayer(tf.keras.layers.Layer): """ TensorFlow layer for computing the persistent homology of a cubical complex """ - def __init__(self, dimensions, min_persistence=None, homology_coeff_field=11, **kwargs): + def __init__(self, homology_dimensions, min_persistence=None, homology_coeff_field=11, **kwargs): """ Constructor for the CubicalLayer class Parameters: - dimensions (List[int]): homology dimensions + homology_dimensions (List[int]): list of homology dimensions min_persistence (List[float]): minimum distance-to-diagonal of the points in the output persistence diagrams (default None, in which case 0. is used for all dimensions) homology_coeff_field (int): homology field coefficient. Must be a prime number. Default value is 11. Max is 46337. """ super().__init__(dynamic=True, **kwargs) - self.dimensions = dimensions + self.dimensions = homology_dimensions self.min_persistence = min_persistence if min_persistence != None else [0.] * len(self.dimensions) self.hcf = homology_coeff_field assert len(self.min_persistence) == len(self.dimensions) diff --git a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py index 4ec3f7c7..5a8e5b75 100644 --- a/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py +++ b/src/python/gudhi/tensorflow/lower_star_simplex_tree_layer.py @@ -43,18 +43,18 @@ class LowerStarSimplexTreeLayer(tf.keras.layers.Layer): """ TensorFlow layer for computing lower-star persistence out of a simplex tree """ - def __init__(self, simplextree, dimensions, min_persistence=None, homology_coeff_field=11, **kwargs): + def __init__(self, simplextree, homology_dimensions, min_persistence=None, homology_coeff_field=11, **kwargs): """ Constructor for the LowerStarSimplexTreeLayer class Parameters: simplextree (gudhi.SimplexTree): underlying simplex tree. Its vertices MUST be named with integers from 0 to n-1, where n is its number of vertices. Note that its filtration values are modified in each call of the class. - dimensions (List[int]): homology dimensions + homology_dimensions (List[int]): list of homology dimensions min_persistence (List[float]): minimum distance-to-diagonal of the points in the output persistence diagrams (default None, in which case 0. is used for all dimensions) homology_coeff_field (int): homology field coefficient. Must be a prime number. Default value is 11. Max is 46337. """ super().__init__(dynamic=True, **kwargs) - self.dimensions = dimensions + self.dimensions = homology_dimensions self.simplextree = simplextree self.min_persistence = min_persistence if min_persistence != None else [0. for _ in range(len(self.dimensions))] self.hcf = homology_coeff_field diff --git a/src/python/gudhi/tensorflow/rips_layer.py b/src/python/gudhi/tensorflow/rips_layer.py index fca336f3..2a73472c 100644 --- a/src/python/gudhi/tensorflow/rips_layer.py +++ b/src/python/gudhi/tensorflow/rips_layer.py @@ -40,19 +40,19 @@ class RipsLayer(tf.keras.layers.Layer): """ TensorFlow layer for computing Rips persistence out of a point cloud """ - def __init__(self, dimensions, maximum_edge_length=np.inf, min_persistence=None, homology_coeff_field=11, **kwargs): + def __init__(self, homology_dimensions, maximum_edge_length=np.inf, min_persistence=None, homology_coeff_field=11, **kwargs): """ Constructor for the RipsLayer class Parameters: maximum_edge_length (float): maximum edge length for the Rips complex - dimensions (List[int]): homology dimensions + homology_dimensions (List[int]): list of homology dimensions min_persistence (List[float]): minimum distance-to-diagonal of the points in the output persistence diagrams (default None, in which case 0. is used for all dimensions) homology_coeff_field (int): homology field coefficient. Must be a prime number. Default value is 11. Max is 46337. """ super().__init__(dynamic=True, **kwargs) self.max_edge = maximum_edge_length - self.dimensions = dimensions + self.dimensions = homology_dimensions self.min_persistence = min_persistence if min_persistence != None else [0. for _ in range(len(self.dimensions))] self.hcf = homology_coeff_field assert len(self.min_persistence) == len(self.dimensions) diff --git a/src/python/test/test_diff.py b/src/python/test/test_diff.py index e0a4717c..dca001a9 100644 --- a/src/python/test/test_diff.py +++ b/src/python/test/test_diff.py @@ -7,7 +7,7 @@ def test_rips_diff(): Xinit = np.array([[1.,1.],[2.,2.]], dtype=np.float32) X = tf.Variable(initial_value=Xinit, trainable=True) - rl = RipsLayer(maximum_edge_length=2., dimensions=[0]) + rl = RipsLayer(maximum_edge_length=2., homology_dimensions=[0]) with tf.GradientTape() as tape: dgm = rl.call(X)[0][0] @@ -19,7 +19,7 @@ def test_cubical_diff(): Xinit = np.array([[0.,2.,2.],[2.,2.,2.],[2.,2.,1.]], dtype=np.float32) X = tf.Variable(initial_value=Xinit, trainable=True) - cl = CubicalLayer(dimensions=[0]) + cl = CubicalLayer(homology_dimensions=[0]) with tf.GradientTape() as tape: dgm = cl.call(X)[0][0] @@ -31,7 +31,7 @@ def test_nonsquare_cubical_diff(): Xinit = np.array([[-1.,1.,0.],[1.,1.,1.]], dtype=np.float32) X = tf.Variable(initial_value=Xinit, trainable=True) - cl = CubicalLayer(dimensions=[0]) + cl = CubicalLayer(homology_dimensions=[0]) with tf.GradientTape() as tape: dgm = cl.call(X)[0][0] @@ -66,7 +66,7 @@ def test_st_diff(): Finit = np.array([6.,4.,3.,4.,5.,4.,3.,2.,3.,4.,5.], dtype=np.float32) F = tf.Variable(initial_value=Finit, trainable=True) - sl = LowerStarSimplexTreeLayer(simplextree=st, dimensions=[0]) + sl = LowerStarSimplexTreeLayer(simplextree=st, homology_dimensions=[0]) with tf.GradientTape() as tape: dgm = sl.call(F)[0][0] -- cgit v1.2.3 From 26a4381c6948338f935e107880cdf0789f65cb12 Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Wed, 29 Jun 2022 10:03:38 +0200 Subject: third parties are not required when WITH_GUDHI_CPP_DOCUMENTATION_ONLY, but hera submodule is mandatory. Moved in a new cmake module --- CMakeLists.txt | 2 ++ src/CMakeLists.txt | 2 ++ src/cmake/modules/GUDHI_submodules.cmake | 5 +++++ src/cmake/modules/GUDHI_third_party_libraries.cmake | 6 ------ 4 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 src/cmake/modules/GUDHI_submodules.cmake (limited to 'src') diff --git a/CMakeLists.txt b/CMakeLists.txt index f946e111..1164eaab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,8 @@ set(GUDHI_MISSING_MODULES "" CACHE INTERNAL "GUDHI_MISSING_MODULES") # This variable is used by Cython CMakeLists.txt and by GUDHI_third_party_libraries to know its path set(GUDHI_PYTHON_PATH "src/python") +include(GUDHI_submodules) + if (NOT WITH_GUDHI_CPP_DOCUMENTATION_ONLY) # For third parties libraries management - To be done last as CGAL updates CMAKE_MODULE_PATH include(GUDHI_third_party_libraries NO_POLICY_SCOPE) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a4fcfcad..d2f12e5f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,6 +12,8 @@ set(GUDHI_MISSING_MODULES "" CACHE INTERNAL "GUDHI_MISSING_MODULES") # This variable is used by Cython CMakeLists.txt and by GUDHI_third_party_libraries to know its path set(GUDHI_PYTHON_PATH "python") +include(GUDHI_submodules) + if (NOT WITH_GUDHI_CPP_DOCUMENTATION_ONLY) # For third parties libraries management - To be done last as CGAL updates CMAKE_MODULE_PATH include(GUDHI_third_party_libraries NO_POLICY_SCOPE) diff --git a/src/cmake/modules/GUDHI_submodules.cmake b/src/cmake/modules/GUDHI_submodules.cmake new file mode 100644 index 00000000..78b045bd --- /dev/null +++ b/src/cmake/modules/GUDHI_submodules.cmake @@ -0,0 +1,5 @@ +# For those who dislike bundled dependencies, this indicates where to find a preinstalled Hera. +set(HERA_WASSERSTEIN_INTERNAL_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/ext/hera/wasserstein/include) +set(HERA_WASSERSTEIN_INCLUDE_DIR ${HERA_WASSERSTEIN_INTERNAL_INCLUDE_DIR} CACHE PATH "Directory where one can find Hera's wasserstein.h") +set(HERA_BOTTLENECK_INTERNAL_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/ext/hera/bottleneck/include) +set(HERA_BOTTLENECK_INCLUDE_DIR ${HERA_BOTTLENECK_INTERNAL_INCLUDE_DIR} CACHE PATH "Directory where one can find Hera's bottleneck.h") \ No newline at end of file diff --git a/src/cmake/modules/GUDHI_third_party_libraries.cmake b/src/cmake/modules/GUDHI_third_party_libraries.cmake index 6ba822ad..2cf6787e 100644 --- a/src/cmake/modules/GUDHI_third_party_libraries.cmake +++ b/src/cmake/modules/GUDHI_third_party_libraries.cmake @@ -48,12 +48,6 @@ if(CGAL_FOUND) include( ${CGAL_USE_FILE} ) endif() -# For those who dislike bundled dependencies, this indicates where to find a preinstalled Hera. -set(HERA_WASSERSTEIN_INTERNAL_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/ext/hera/wasserstein/include) -set(HERA_WASSERSTEIN_INCLUDE_DIR ${HERA_WASSERSTEIN_INTERNAL_INCLUDE_DIR} CACHE PATH "Directory where one can find Hera's wasserstein.h") -set(HERA_BOTTLENECK_INTERNAL_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/ext/hera/bottleneck/include) -set(HERA_BOTTLENECK_INCLUDE_DIR ${HERA_BOTTLENECK_INTERNAL_INCLUDE_DIR} CACHE PATH "Directory where one can find Hera's bottleneck.h") - option(WITH_GUDHI_USE_TBB "Build with Intel TBB parallelization" ON) # Find TBB package for parallel sort - not mandatory, just optional. -- cgit v1.2.3 From 17f68bb5be7a68fab17510740d2bdc28b70decea Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Thu, 30 Jun 2022 11:30:12 +0200 Subject: code review: rename option WITH_GUDHI_THIRD_PARTY --- .circleci/config.yml | 8 ++++---- .github/for_maintainers/tests_strategy.md | 5 +++++ CMakeLists.txt | 4 ++-- src/CMakeLists.txt | 4 ++-- src/cmake/modules/GUDHI_options.cmake | 4 ++-- src/common/doc/installation.h | 4 ++-- 6 files changed, 17 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/.circleci/config.yml b/.circleci/config.yml index 64e7fbb1..82ad19db 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -58,7 +58,7 @@ jobs: git submodule update mkdir build cd build - cmake -DWITH_GUDHI_CPP_DOCUMENTATION_ONLY=ON -DUSER_VERSION_DIR=version .. + cmake -DWITH_GUDHI_THIRD_PARTY=OFF -DUSER_VERSION_DIR=version .. make user_version cd version cmake -DCMAKE_BUILD_TYPE=Release -DWITH_GUDHI_EXAMPLE=OFF -DWITH_GUDHI_UTILITIES=OFF -DWITH_GUDHI_PYTHON=ON -DPython_ADDITIONAL_VERSIONS=3 . @@ -89,13 +89,13 @@ jobs: git submodule update mkdir build cd build - cmake -DWITH_GUDHI_CPP_DOCUMENTATION_ONLY=ON -DUSER_VERSION_DIR=version .. + cmake -DWITH_GUDHI_THIRD_PARTY=OFF -DUSER_VERSION_DIR=version .. make user_version cd version mkdir build cd build - cmake -DWITH_GUDHI_CPP_DOCUMENTATION_ONLY=ON .. - make doxygen 2>&1 + cmake -DWITH_GUDHI_THIRD_PARTY=OFF .. + make doxygen grep warning doxygen.log cp doxygen.log html/ cp -R html /tmp/doxygen diff --git a/.github/for_maintainers/tests_strategy.md b/.github/for_maintainers/tests_strategy.md index c25acf9b..01248d3a 100644 --- a/.github/for_maintainers/tests_strategy.md +++ b/.github/for_maintainers/tests_strategy.md @@ -4,6 +4,11 @@ This document tries to sum up the tests strategy that has been put in place for The aim is to help maintainers to anticipate third parties modifications, updates. +## CMake options + +[CMake GUDHI options](../../src/cmake/modules/GUDHI_options.cmake) allows to activate/deactivate what should be built and tested. +Note the special option `WITH_GUDHI_THIRD_PARTY` that, when set to `OFF`, accelerates doxygen documentation generation or `user_version` for instance. + ## Builds ### Linux diff --git a/CMakeLists.txt b/CMakeLists.txt index 1164eaab..6c233459 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ set(GUDHI_PYTHON_PATH "src/python") include(GUDHI_submodules) -if (NOT WITH_GUDHI_CPP_DOCUMENTATION_ONLY) +if (WITH_GUDHI_THIRD_PARTY) # For third parties libraries management - To be done last as CGAL updates CMAKE_MODULE_PATH include(GUDHI_third_party_libraries NO_POLICY_SCOPE) endif() @@ -56,7 +56,7 @@ foreach(GUDHI_MODULE ${GUDHI_MODULES}) endforeach() endforeach() -if (NOT WITH_GUDHI_CPP_DOCUMENTATION_ONLY) +if (WITH_GUDHI_THIRD_PARTY) add_subdirectory(src/GudhUI) endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d2f12e5f..f9f77ef7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,7 +14,7 @@ set(GUDHI_PYTHON_PATH "python") include(GUDHI_submodules) -if (NOT WITH_GUDHI_CPP_DOCUMENTATION_ONLY) +if (WITH_GUDHI_THIRD_PARTY) # For third parties libraries management - To be done last as CGAL updates CMAKE_MODULE_PATH include(GUDHI_third_party_libraries NO_POLICY_SCOPE) endif() @@ -71,7 +71,7 @@ foreach(GUDHI_MODULE ${GUDHI_MODULES}) endforeach() endforeach() -if (NOT WITH_GUDHI_CPP_DOCUMENTATION_ONLY) +if (WITH_GUDHI_THIRD_PARTY) add_subdirectory(GudhUI) endif() diff --git a/src/cmake/modules/GUDHI_options.cmake b/src/cmake/modules/GUDHI_options.cmake index 6655d605..fe328169 100644 --- a/src/cmake/modules/GUDHI_options.cmake +++ b/src/cmake/modules/GUDHI_options.cmake @@ -3,9 +3,9 @@ option(WITH_GUDHI_EXAMPLE "Activate/deactivate examples compilation and installa option(WITH_GUDHI_PYTHON "Activate/deactivate python module compilation and installation" ON) option(WITH_GUDHI_TEST "Activate/deactivate examples compilation and installation" ON) option(WITH_GUDHI_UTILITIES "Activate/deactivate utilities compilation and installation" ON) -option(WITH_GUDHI_CPP_DOCUMENTATION_ONLY "Build only the GUDHI C++ documentation (with doxygen)." OFF) +option(WITH_GUDHI_THIRD_PARTY "Activate/deactivate third party libraries cmake detection. When set to OFF, it is usefull for doxygen or user_version i.e." ON) -if (WITH_GUDHI_CPP_DOCUMENTATION_ONLY) +if (NOT WITH_GUDHI_THIRD_PARTY) set (WITH_GUDHI_BENCHMARK OFF) set (WITH_GUDHI_EXAMPLE OFF) set (WITH_GUDHI_PYTHON OFF) diff --git a/src/common/doc/installation.h b/src/common/doc/installation.h index b97142b6..c17855b6 100644 --- a/src/common/doc/installation.h +++ b/src/common/doc/installation.h @@ -43,13 +43,13 @@ make \endverbatim * * \subsection documentationgeneration C++ documentation * To generate the C++ documentation, the doxygen program - * is required. Run the following command in a terminal: + * is required (version ≥ 1.9.3 is advised). Run the following command in a terminal: * \verbatim make doxygen \endverbatim * Documentation will be generated in a folder named html. * * In case there is not a full setup present and only the documentation should be build the following command sequence * can be used: -\verbatim cmake -DWITH_GUDHI_CPP_DOCUMENTATION_ONLY=ON .. +\verbatim cmake -DWITH_GUDHI_THIRD_PARTY=OFF .. make doxygen\endverbatim * * \subsection helloworld Hello world ! -- cgit v1.2.3 From 70b9a1e3633ce3bc6112488ea6e0342ea910c772 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Thu, 30 Jun 2022 16:43:47 +0200 Subject: Reuse vector of points and reserve to avoid reallocations --- src/Cech_complex/include/gudhi/Cech_complex_blocker.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h index 1bb205b3..fb452326 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h +++ b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h @@ -14,6 +14,8 @@ #include // for casting from FT to Filtration_value #include // for CGAL::exact +#include + #include #include #include @@ -73,6 +75,8 @@ class Cech_blocker { CGAL::NT_converter cast_to_fv; Filtration_value radius = 0; bool is_min_enclos_ball = false; + Point_cloud points; + points.reserve(boost::size(sc_ptr_->simplex_vertex_range(sh))); // for each face of simplex sh, test outsider point is indeed inside enclosing ball, if yes, take it and exit loop, otherwise, new sphere is circumsphere of all vertices for (auto face_opposite_vertex : sc_ptr_->boundary_opposite_vertex_simplex_range(sh)) { @@ -82,19 +86,18 @@ class Cech_blocker { sph = cc_ptr_->get_cache().at(k); } else { - Point_cloud face_points; for (auto vertex : sc_ptr_->simplex_vertex_range(face_opposite_vertex.first)) { - face_points.push_back(cc_ptr_->get_point(vertex)); + points.push_back(cc_ptr_->get_point(vertex)); #ifdef DEBUG_TRACES std::clog << "#(" << vertex << ")#"; #endif // DEBUG_TRACES } - sph = get_sphere(face_points.cbegin(), face_points.cend()); + sph = get_sphere(points.cbegin(), points.cend()); // Put edge sphere in cache sc_ptr_->assign_key(face_opposite_vertex.first, cc_ptr_->get_cache().size()); cc_ptr_->get_cache().push_back(sph); - // Clear face_points - face_points.clear(); + // Clear face points + points.clear(); } // Check if the minimal enclosing ball of current face contains the extra point/opposite vertex if (kernel_.squared_distance_d_object()(sph.first, cc_ptr_->get_point(face_opposite_vertex.second)) <= sph.second) { @@ -113,7 +116,6 @@ class Cech_blocker { } // Spheres of each face don't contain the whole simplex if(!is_min_enclos_ball) { - Point_cloud points; for (auto vertex : sc_ptr_->simplex_vertex_range(sh)) { points.push_back(cc_ptr_->get_point(vertex)); } -- cgit v1.2.3 From 438f168ea992125382f8c23eb2cd9ad3e92688a7 Mon Sep 17 00:00:00 2001 From: albert-github Date: Fri, 1 Jul 2022 11:09:45 +0200 Subject: Don't reveal full path in the documentation but just the relative path In e,g, https://gudhi.inria.fr/doc/latest/struct_coefficient_field.html (in general in `../struct_coefficient_field.html`) we see lines like: ``` #include ``` and ``` The documentation for this struct was generated from the following file: /home/gailuron/workspace/gudhi/gudhi-devel/build/gudhi.3.5.0/concept/Persistent_cohomology/CoefficientField.h ``` instead of the relative names: ``` #include ``` and ``` The documentation for this struct was generated from the following file: src/Persistent_cohomology/concept/CoefficientField.h ``` (the links are pointing to the correct places in all cases.) This is corrected by stripping the path. --- src/Doxyfile.in | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/Doxyfile.in b/src/Doxyfile.in index fb68ceb1..ad4e95ca 100644 --- a/src/Doxyfile.in +++ b/src/Doxyfile.in @@ -152,7 +152,7 @@ FULL_PATH_NAMES = YES # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. -STRIP_FROM_PATH = +STRIP_FROM_PATH = @CMAKE_SOURCE_DIR@ # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which @@ -162,7 +162,8 @@ STRIP_FROM_PATH = # using the -I flag. STRIP_FROM_INC_PATH = include \ - concept + concept \ + @CMAKE_SOURCE_DIR@ # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't -- cgit v1.2.3 From 3f05f2c81481c9a14357d8674378244a27aaec56 Mon Sep 17 00:00:00 2001 From: albert-github Date: Fri, 1 Jul 2022 15:40:31 +0200 Subject: Ignore complete current build directory when building documentation Found the file ``` build/CMakeFiles/ShowIncludes/foo.h ``` during the build of the documentation. The build directory should be excluded. --- src/Doxyfile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/Doxyfile.in b/src/Doxyfile.in index fb68ceb1..c20dffeb 100644 --- a/src/Doxyfile.in +++ b/src/Doxyfile.in @@ -832,7 +832,7 @@ EXCLUDE = @CMAKE_SOURCE_DIR@/data/ \ @CMAKE_SOURCE_DIR@/ext/ \ @CMAKE_SOURCE_DIR@/README.md \ @CMAKE_SOURCE_DIR@/.github \ - @CMAKE_CURRENT_BINARY_DIR@/new_gudhi_version_creation.md \ + @CMAKE_CURRENT_BINARY_DIR@ \ @GUDHI_DOXYGEN_SOURCE_PREFIX@/GudhUI/ \ @GUDHI_DOXYGEN_SOURCE_PREFIX@/cmake/ \ @GUDHI_DOXYGEN_SOURCE_PREFIX@/python/ -- cgit v1.2.3 From dd41000ec13553787c8575f7aa55ce9156ad6b8a Mon Sep 17 00:00:00 2001 From: Hind-M Date: Fri, 1 Jul 2022 15:42:29 +0200 Subject: Use dimension to get range size to reserve vector in cech --- src/Cech_complex/include/gudhi/Cech_complex_blocker.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src') diff --git a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h index fb452326..e7f548ba 100644 --- a/src/Cech_complex/include/gudhi/Cech_complex_blocker.h +++ b/src/Cech_complex/include/gudhi/Cech_complex_blocker.h @@ -14,8 +14,6 @@ #include // for casting from FT to Filtration_value #include // for CGAL::exact -#include - #include #include #include @@ -76,7 +74,7 @@ class Cech_blocker { Filtration_value radius = 0; bool is_min_enclos_ball = false; Point_cloud points; - points.reserve(boost::size(sc_ptr_->simplex_vertex_range(sh))); + points.reserve(sc_ptr_->dimension(sh)+1); // for each face of simplex sh, test outsider point is indeed inside enclosing ball, if yes, take it and exit loop, otherwise, new sphere is circumsphere of all vertices for (auto face_opposite_vertex : sc_ptr_->boundary_opposite_vertex_simplex_range(sh)) { -- cgit v1.2.3 From eb0ab20bdbf624e6b20896fb2cca1af71af7d4a5 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Wed, 27 Jul 2022 11:36:56 +0200 Subject: Enable keops dtm warnings --- src/python/doc/installation.rst | 2 +- src/python/test/test_dtm.py | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/python/doc/installation.rst b/src/python/doc/installation.rst index dd476054..4eefd415 100644 --- a/src/python/doc/installation.rst +++ b/src/python/doc/installation.rst @@ -175,7 +175,7 @@ A complete configuration would be : Scikit-learn version 1.0.1 POT version 0.8.0 HNSWlib found - PyKeOps version [pyKeOps]: 1.5 + PyKeOps version [pyKeOps]: 2.1 EagerPy version 0.30.0 TensorFlow version 2.7.0 Sphinx version 4.3.0 diff --git a/src/python/test/test_dtm.py b/src/python/test/test_dtm.py index e46d616c..21cff055 100755 --- a/src/python/test/test_dtm.py +++ b/src/python/test/test_dtm.py @@ -91,11 +91,12 @@ def test_density(): def test_dtm_overflow_warnings(): pts = numpy.array([[10., 100000000000000000000000000000.], [1000., 100000000000000000000000000.]]) - + impl_warn = ["keops", "hnsw"] with warnings.catch_warnings(record=True) as w: - # TODO Test "keops" implementation as well when next version of pykeops (current is 1.5) is released (should fix the problem (cf. issue #543)) - dtm = DistanceToMeasure(2, implementation="hnsw") - r = dtm.fit_transform(pts) - assert len(w) == 1 - assert issubclass(w[0].category, RuntimeWarning) - assert "Overflow" in str(w[0].message) + for impl in impl_warn: + dtm = DistanceToMeasure(2, implementation=impl) + r = dtm.fit_transform(pts) + assert len(w) == 2 + for i in range(len(w)): + assert issubclass(w[i].category, RuntimeWarning) + assert "Overflow" in str(w[i].message) -- cgit v1.2.3 From 98106c3130aa2c7988743c50abd562cdf5af5456 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Thu, 28 Jul 2022 12:09:19 +0200 Subject: Modify doc in simplex_tree in order to match code --- src/python/gudhi/simplex_tree.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/python/gudhi/simplex_tree.pyx b/src/python/gudhi/simplex_tree.pyx index 521a7763..05bfe22e 100644 --- a/src/python/gudhi/simplex_tree.pyx +++ b/src/python/gudhi/simplex_tree.pyx @@ -487,9 +487,9 @@ cdef class SimplexTree: otherwise it is kept. The algorithm then proceeds with the next candidate. .. warning:: - Several candidates of the same dimension may be inserted simultaneously before calling `block_simplex`, so - if you examine the complex in `block_simplex`, you may hit a few simplices of the same dimension that have - not been vetted by `block_simplex` yet, or have already been rejected but not yet removed. + Several candidates of the same dimension may be inserted simultaneously before calling `blocker_func`, so + if you examine the complex in `blocker_func`, you may hit a few simplices of the same dimension that have + not been vetted by `blocker_func` yet, or have already been rejected but not yet removed. :param max_dim: Expansion maximal dimension value. :type max_dim: int -- cgit v1.2.3 From 1c5c80fb53075706ba74751468d748960fae9dd9 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Tue, 2 Aug 2022 14:56:51 +0200 Subject: Pull out the for loop in test --- src/python/test/test_dtm.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/python/test/test_dtm.py b/src/python/test/test_dtm.py index 21cff055..b276f041 100755 --- a/src/python/test/test_dtm.py +++ b/src/python/test/test_dtm.py @@ -92,11 +92,10 @@ def test_density(): def test_dtm_overflow_warnings(): pts = numpy.array([[10., 100000000000000000000000000000.], [1000., 100000000000000000000000000.]]) impl_warn = ["keops", "hnsw"] - with warnings.catch_warnings(record=True) as w: - for impl in impl_warn: + for impl in impl_warn: + with warnings.catch_warnings(record=True) as w: dtm = DistanceToMeasure(2, implementation=impl) r = dtm.fit_transform(pts) - assert len(w) == 2 - for i in range(len(w)): - assert issubclass(w[i].category, RuntimeWarning) - assert "Overflow" in str(w[i].message) + assert len(w) == 1 + assert issubclass(w[0].category, RuntimeWarning) + assert "Overflow" in str(w[0].message) -- cgit v1.2.3