summaryrefslogtreecommitdiff
path: root/cython/cython
diff options
context:
space:
mode:
Diffstat (limited to 'cython/cython')
-rwxr-xr-xcython/cython/persistence_graphical_tools.py76
-rw-r--r--cython/cython/reader_utils.pyx95
-rw-r--r--cython/cython/simplex_tree.pyx81
3 files changed, 204 insertions, 48 deletions
diff --git a/cython/cython/persistence_graphical_tools.py b/cython/cython/persistence_graphical_tools.py
index a984633e..fb837e29 100755
--- a/cython/cython/persistence_graphical_tools.py
+++ b/cython/cython/persistence_graphical_tools.py
@@ -1,11 +1,12 @@
import matplotlib.pyplot as plt
import numpy as np
+import os
"""This file is part of the Gudhi Library. The Gudhi library
(Geometric Understanding in Higher Dimensions) is a generic C++
library for computational topology.
- Author(s): Vincent Rouvreau
+ Author(s): Vincent Rouvreau, Bertrand Michel
Copyright (C) 2016 INRIA
@@ -23,15 +24,17 @@ import numpy as np
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
-__author__ = "Vincent Rouvreau"
+__author__ = "Vincent Rouvreau, Bertrand Michel"
__copyright__ = "Copyright (C) 2016 INRIA"
__license__ = "GPL v3"
-def __min_birth_max_death(persistence):
+def __min_birth_max_death(persistence, band_boot=0.):
"""This function returns (min_birth, max_death) from the persistence.
:param persistence: The persistence to plot.
:type persistence: list of tuples(dimension, tuple(birth, death)).
+ :param band_boot: bootstrap band
+ :type band_boot: float.
:returns: (float, float) -- (min_birth, max_death).
"""
# Look for minimum birth date and maximum death date for plot optimisation
@@ -45,6 +48,8 @@ def __min_birth_max_death(persistence):
max_death = float(interval[1][0])
if float(interval[1][0]) < min_birth:
min_birth = float(interval[1][0])
+ if band_boot > 0.:
+ max_death += band_boot
return (min_birth, max_death)
"""
@@ -59,7 +64,7 @@ def show_palette_values(alpha=0.6):
:param alpha: alpha value in [0.0, 1.0] for horizontal bars (default is 0.6).
:type alpha: float.
- :returns: plot -- An horizontal bar plot of dimensions color.
+ :returns: plot the dimension palette values.
"""
colors = []
for color in palette:
@@ -70,18 +75,38 @@ def show_palette_values(alpha=0.6):
plt.barh(y_pos, y_pos + 1, align='center', alpha=alpha, color=colors)
plt.ylabel('Dimension')
plt.title('Dimension palette values')
+ return plt
- plt.show()
-
-def plot_persistence_barcode(persistence, alpha=0.6):
+def plot_persistence_barcode(persistence=[], persistence_file='', alpha=0.6, max_barcodes=0):
"""This function plots the persistence bar code.
:param persistence: The persistence to plot.
:type persistence: list of tuples(dimension, tuple(birth, death)).
+ :param persistence_file: A persistence file style name (reset persistence if both are set).
+ :type persistence_file: string
:param alpha: alpha value in [0.0, 1.0] for horizontal bars (default is 0.6).
:type alpha: float.
+ :param max_barcodes: number of maximal barcodes to be displayed
+ (persistence will be sorted by life time if max_barcodes is set)
+ :type max_barcodes: int.
:returns: plot -- An horizontal bar plot of persistence.
"""
+ if persistence_file is not '':
+ if os.path.isfile(persistence_file):
+ # Reset persistence
+ persistence = []
+ diag = read_persistence_intervals_grouped_by_dimension(persistence_file=persistence_file)
+ for key in diag.keys():
+ for persistence_interval in diag[key]:
+ persistence.append((key, persistence_interval))
+ else:
+ print("file " + persistence_file + " not found.")
+ return None
+
+ if max_barcodes > 0 and max_barcodes < len(persistence):
+ # Sort by life time, then takes only the max_plots elements
+ persistence = sorted(persistence, key=lambda life_time: life_time[1][1]-life_time[1][0], reverse=True)[:max_barcodes]
+
(min_birth, max_death) = __min_birth_max_death(persistence)
ind = 0
delta = ((max_death - min_birth) / 10.0)
@@ -106,18 +131,40 @@ def plot_persistence_barcode(persistence, alpha=0.6):
plt.title('Persistence barcode')
# Ends plot on infinity value and starts a little bit before min_birth
plt.axis([axis_start, infinity, 0, ind])
- plt.show()
+ return plt
-def plot_persistence_diagram(persistence, alpha=0.6):
- """This function plots the persistence diagram.
+def plot_persistence_diagram(persistence=[], persistence_file='', alpha=0.6, band_boot=0., max_plots=0):
+ """This function plots the persistence diagram with an optional confidence band.
:param persistence: The persistence to plot.
:type persistence: list of tuples(dimension, tuple(birth, death)).
+ :param persistence_file: A persistence file style name (reset persistence if both are set).
+ :type persistence_file: string
:param alpha: alpha value in [0.0, 1.0] for points and horizontal infinity line (default is 0.6).
:type alpha: float.
- :returns: plot -- An diagram plot of persistence.
+ :param band_boot: bootstrap band (not displayed if :math:`\leq` 0.)
+ :type band_boot: float.
+ :param max_plots: number of maximal plots to be displayed
+ :type max_plots: int.
+ :returns: plot -- A diagram plot of persistence.
"""
- (min_birth, max_death) = __min_birth_max_death(persistence)
+ if persistence_file is not '':
+ if os.path.isfile(persistence_file):
+ # Reset persistence
+ persistence = []
+ diag = read_persistence_intervals_grouped_by_dimension(persistence_file=persistence_file)
+ for key in diag.keys():
+ for persistence_interval in diag[key]:
+ persistence.append((key, persistence_interval))
+ else:
+ print("file " + persistence_file + " not found.")
+ return None
+
+ if max_plots > 0 and max_plots < len(persistence):
+ # Sort by life time, then takes only the max_plots elements
+ persistence = sorted(persistence, key=lambda life_time: life_time[1][1]-life_time[1][0], reverse=True)[:max_plots]
+
+ (min_birth, max_death) = __min_birth_max_death(persistence, band_boot)
ind = 0
delta = ((max_death - min_birth) / 10.0)
# Replace infinity values with max_death + delta for diagram to be more
@@ -131,6 +178,9 @@ def plot_persistence_diagram(persistence, alpha=0.6):
plt.plot(x, x, color='k', linewidth=1.0)
plt.plot(x, [infinity] * len(x), linewidth=1.0, color='k', alpha=alpha)
plt.text(axis_start, infinity, r'$\infty$', color='k', alpha=alpha)
+ # bootstrap band
+ if band_boot > 0.:
+ plt.fill_between(x, x, x+band_boot, alpha=alpha, facecolor='red')
# Draw points in loop
for interval in reversed(persistence):
@@ -149,4 +199,4 @@ def plot_persistence_diagram(persistence, alpha=0.6):
plt.ylabel('Death')
# Ends plot on infinity value and starts a little bit before min_birth
plt.axis([axis_start, infinity, axis_start, infinity + delta])
- plt.show()
+ return plt
diff --git a/cython/cython/reader_utils.pyx b/cython/cython/reader_utils.pyx
new file mode 100644
index 00000000..3a17c5a0
--- /dev/null
+++ b/cython/cython/reader_utils.pyx
@@ -0,0 +1,95 @@
+from cython cimport numeric
+from libcpp.vector cimport vector
+from libcpp.string cimport string
+from libcpp.map cimport map
+from libcpp.pair cimport pair
+import os
+
+"""This file is part of the Gudhi Library. The Gudhi library
+ (Geometric Understanding in Higher Dimensions) is a generic C++
+ library for computational topology.
+
+ Author(s): Vincent Rouvreau
+
+ Copyright (C) 2017 INRIA
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ 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 <http://www.gnu.org/licenses/>.
+"""
+
+__author__ = "Vincent Rouvreau"
+__copyright__ = "Copyright (C) 2017 INRIA"
+__license__ = "GPL v3"
+
+cdef extern from "Reader_utils_interface.h" namespace "Gudhi":
+ vector[vector[double]] read_matrix_from_csv_file(string off_file, char separator)
+ map[int, vector[pair[double, double]]] read_pers_intervals_grouped_by_dimension(string filename)
+ vector[pair[double, double]] read_pers_intervals_in_dimension(string filename, int only_this_dim)
+
+def read_lower_triangular_matrix_from_csv_file(csv_file='', separator=';'):
+ """Read lower triangular matrix from a CSV style file.
+
+ :param csv_file: A CSV file style name.
+ :type csv_file: string
+ :param separator: The value separator in the CSV file. Default value is ';'
+ :type separator: char
+
+ :returns: The lower triangular matrix.
+ :rtype: vector[vector[double]]
+ """
+ if csv_file is not '':
+ if os.path.isfile(csv_file):
+ return read_matrix_from_csv_file(str.encode(csv_file), ord(separator[0]))
+ print("file " + csv_file + " not set or not found.")
+ return []
+
+def read_persistence_intervals_grouped_by_dimension(persistence_file=''):
+ """Reads a file containing persistence intervals.
+ Each line might contain 2, 3 or 4 values: [[field] dimension] birth death
+ The return value is an `map[dim, vector[pair[birth, death]]]`
+ where `dim` is an `int`, `birth` a `double`, and `death` a `double`.
+ Note: the function does not check that birth <= death.
+
+ :param persistence_file: A persistence file style name.
+ :type persistence_file: string
+
+ :returns: The persistence pairs grouped by dimension.
+ :rtype: map[int, vector[pair[double, double]]]
+ """
+ if persistence_file is not '':
+ if os.path.isfile(persistence_file):
+ return read_pers_intervals_grouped_by_dimension(str.encode(persistence_file))
+ print("file " + persistence_file + " not set or not found.")
+ return []
+
+def read_persistence_intervals_in_dimension(persistence_file='', only_this_dim=-1):
+ """Reads a file containing persistence intervals.
+ Each line might contain 2, 3 or 4 values: [[field] dimension] birth death
+ If `only_this_dim` = -1, dimension is ignored and all lines are returned.
+ If `only_this_dim` is >= 0, only the lines where dimension = `only_this_dim`
+ (or where dimension is not specified) are returned.
+ The return value is an `vector[pair[birth, death]]`
+ where `birth` a `double`, and `death` a `double`.
+ Note: the function does not check that birth <= death.
+
+ :param persistence_file: A persistence file style name.
+ :type persistence_file: string
+
+ :returns: The persistence pairs grouped by dimension.
+ :rtype: map[int, vector[pair[double, double]]]
+ """
+ if persistence_file is not '':
+ if os.path.isfile(persistence_file):
+ return read_pers_intervals_in_dimension(str.encode(persistence_file), only_this_dim)
+ print("file " + persistence_file + " not set or not found.")
+ return []
diff --git a/cython/cython/simplex_tree.pyx b/cython/cython/simplex_tree.pyx
index 9d40a8b5..45487158 100644
--- a/cython/cython/simplex_tree.pyx
+++ b/cython/cython/simplex_tree.pyx
@@ -2,6 +2,7 @@ from cython cimport numeric
from libcpp.vector cimport vector
from libcpp.utility cimport pair
from libcpp cimport bool
+from libcpp.string cimport string
"""This file is part of the Gudhi Library. The Gudhi library
(Geometric Understanding in Higher Dimensions) is a generic C++
@@ -35,9 +36,7 @@ cdef extern from "Simplex_tree_interface.h" namespace "Gudhi":
cdef cppclass Simplex_tree_interface_full_featured "Gudhi::Simplex_tree_interface<Gudhi::Simplex_tree_options_full_featured>":
Simplex_tree()
- double filtration()
double simplex_filtration(vector[int] simplex)
- void set_filtration(double filtration)
void initialize_filtration()
int num_vertices()
int num_simplices()
@@ -61,6 +60,7 @@ cdef extern from "Persistent_cohomology_interface.h" namespace "Gudhi":
vector[int] betti_numbers()
vector[int] persistent_betti_numbers(double from_value, double to_value)
vector[pair[double,double]] intervals_in_dimension(int dimension)
+ void write_output_diagram(string diagram_file_name)
# SimplexTree python interface
cdef class SimplexTree:
@@ -113,14 +113,6 @@ cdef class SimplexTree:
"""
return self.thisptr.simplex_filtration(simplex)
- def set_filtration(self, filtration):
- """This function sets the main simplicial complex filtration value.
-
- :param filtration: The filtration value.
- :type filtration: float.
- """
- self.thisptr.set_filtration(<double> filtration)
-
def initialize_filtration(self):
"""This function initializes and sorts the simplicial complex
filtration vector.
@@ -183,10 +175,10 @@ cdef class SimplexTree:
:returns: true if the simplex was found, false otherwise.
:rtype: bool
"""
- cdef vector[int] complex
+ cdef vector[int] csimplex
for i in simplex:
- complex.push_back(i)
- return self.thisptr.find_simplex(complex)
+ csimplex.push_back(i)
+ return self.thisptr.find_simplex(csimplex)
def insert(self, simplex, filtration=0.0):
"""This function inserts the given N-simplex and its subfaces with the
@@ -200,10 +192,10 @@ cdef class SimplexTree:
:returns: true if the simplex was found, false otherwise.
:rtype: bool
"""
- cdef vector[int] complex
+ cdef vector[int] csimplex
for i in simplex:
- complex.push_back(i)
- return self.thisptr.insert_simplex_and_subfaces(complex,
+ csimplex.push_back(i)
+ return self.thisptr.insert_simplex_and_subfaces(csimplex,
<double>filtration)
def get_filtration(self):
@@ -232,35 +224,35 @@ cdef class SimplexTree:
:returns: The (simplices of the) skeleton of a maximum dimension.
:rtype: list of tuples(simplex, filtration)
"""
- cdef vector[pair[vector[int], double]] skeletons \
+ cdef vector[pair[vector[int], double]] skeleton \
= self.thisptr.get_skeleton(<int>dimension)
ct = []
- for filtered_complex in skeletons:
+ for filtered_simplex in skeleton:
v = []
- for vertex in filtered_complex.first:
+ for vertex in filtered_simplex.first:
v.append(vertex)
- ct.append((v, filtered_complex.second))
+ ct.append((v, filtered_simplex.second))
return ct
def get_star(self, simplex):
- """This function returns the stars of a given N-simplex.
+ """This function returns the star of a given N-simplex.
:param simplex: The N-simplex, represented by a list of vertex.
:type simplex: list of int.
:returns: The (simplices of the) star of a simplex.
:rtype: list of tuples(simplex, filtration)
"""
- cdef vector[int] complex
+ cdef vector[int] csimplex
for i in simplex:
- complex.push_back(i)
- cdef vector[pair[vector[int], double]] stars \
- = self.thisptr.get_star(complex)
+ csimplex.push_back(i)
+ cdef vector[pair[vector[int], double]] star \
+ = self.thisptr.get_star(csimplex)
ct = []
- for filtered_complex in stars:
+ for filtered_simplex in star:
v = []
- for vertex in filtered_complex.first:
+ for vertex in filtered_simplex.first:
v.append(vertex)
- ct.append((v, filtered_complex.second))
+ ct.append((v, filtered_simplex.second))
return ct
def get_cofaces(self, simplex, codimension):
@@ -275,17 +267,17 @@ cdef class SimplexTree:
:returns: The (simplices of the) cofaces of a simplex
:rtype: list of tuples(simplex, filtration)
"""
- cdef vector[int] complex
+ cdef vector[int] csimplex
for i in simplex:
- complex.push_back(i)
+ csimplex.push_back(i)
cdef vector[pair[vector[int], double]] cofaces \
- = self.thisptr.get_cofaces(complex, <int>codimension)
+ = self.thisptr.get_cofaces(csimplex, <int>codimension)
ct = []
- for filtered_complex in cofaces:
+ for filtered_simplex in cofaces:
v = []
- for vertex in filtered_complex.first:
+ for vertex in filtered_simplex.first:
v.append(vertex)
- ct.append((v, filtered_complex.second))
+ ct.append((v, filtered_simplex.second))
return ct
def remove_maximal_simplex(self, simplex):
@@ -385,7 +377,7 @@ cdef class SimplexTree:
complex in a specific dimension.
:param dimension: The specific dimension.
- :type from_value: int.
+ :type dimension: int.
:returns: The persistence intervals.
:rtype: list of pair of float
@@ -399,3 +391,22 @@ cdef class SimplexTree:
print("intervals_in_dim function requires persistence function"
" to be launched first.")
return intervals_result
+
+ def write_persistence_diagram(self, persistence_file=''):
+ """This function writes the persistence intervals of the simplicial
+ complex in a user given file name.
+
+ :param persistence_file: The specific dimension.
+ :type persistence_file: string.
+
+ :note: intervals_in_dim function requires persistence function to be
+ launched first.
+ """
+ if self.pcohptr != NULL:
+ if persistence_file != '':
+ self.pcohptr.write_output_diagram(str.encode(persistence_file))
+ else:
+ print("persistence_file must be specified")
+ else:
+ print("intervals_in_dim function requires persistence function"
+ " to be launched first.")