multipers 2.4.0b1__cp312-cp312-macosx_11_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- multipers/.dylibs/libboost_timer.dylib +0 -0
- multipers/.dylibs/libc++.1.0.dylib +0 -0
- multipers/.dylibs/libtbb.12.17.dylib +0 -0
- multipers/__init__.py +33 -0
- multipers/_signed_measure_meta.py +426 -0
- multipers/_slicer_meta.py +231 -0
- multipers/array_api/__init__.py +62 -0
- multipers/array_api/numpy.py +124 -0
- multipers/array_api/torch.py +133 -0
- multipers/data/MOL2.py +458 -0
- multipers/data/UCR.py +18 -0
- multipers/data/__init__.py +1 -0
- multipers/data/graphs.py +466 -0
- multipers/data/immuno_regions.py +27 -0
- multipers/data/minimal_presentation_to_st_bf.py +0 -0
- multipers/data/pytorch2simplextree.py +91 -0
- multipers/data/shape3d.py +101 -0
- multipers/data/synthetic.py +113 -0
- multipers/distances.py +202 -0
- multipers/filtration_conversions.pxd +736 -0
- multipers/filtration_conversions.pxd.tp +226 -0
- multipers/filtrations/__init__.py +21 -0
- multipers/filtrations/density.py +529 -0
- multipers/filtrations/filtrations.py +480 -0
- multipers/filtrations.pxd +534 -0
- multipers/filtrations.pxd.tp +332 -0
- multipers/function_rips.cpython-312-darwin.so +0 -0
- multipers/function_rips.pyx +104 -0
- multipers/grids.cpython-312-darwin.so +0 -0
- multipers/grids.pyx +538 -0
- multipers/gudhi/Persistence_slices_interface.h +213 -0
- multipers/gudhi/Simplex_tree_interface.h +274 -0
- multipers/gudhi/Simplex_tree_multi_interface.h +648 -0
- multipers/gudhi/gudhi/Bitmap_cubical_complex.h +450 -0
- multipers/gudhi/gudhi/Bitmap_cubical_complex_base.h +1070 -0
- multipers/gudhi/gudhi/Bitmap_cubical_complex_periodic_boundary_conditions_base.h +579 -0
- multipers/gudhi/gudhi/Debug_utils.h +52 -0
- multipers/gudhi/gudhi/Degree_rips_bifiltration.h +2307 -0
- multipers/gudhi/gudhi/Dynamic_multi_parameter_filtration.h +2524 -0
- multipers/gudhi/gudhi/Fields/Multi_field.h +453 -0
- multipers/gudhi/gudhi/Fields/Multi_field_operators.h +460 -0
- multipers/gudhi/gudhi/Fields/Multi_field_shared.h +444 -0
- multipers/gudhi/gudhi/Fields/Multi_field_small.h +584 -0
- multipers/gudhi/gudhi/Fields/Multi_field_small_operators.h +490 -0
- multipers/gudhi/gudhi/Fields/Multi_field_small_shared.h +580 -0
- multipers/gudhi/gudhi/Fields/Z2_field.h +391 -0
- multipers/gudhi/gudhi/Fields/Z2_field_operators.h +389 -0
- multipers/gudhi/gudhi/Fields/Zp_field.h +493 -0
- multipers/gudhi/gudhi/Fields/Zp_field_operators.h +384 -0
- multipers/gudhi/gudhi/Fields/Zp_field_shared.h +492 -0
- multipers/gudhi/gudhi/Flag_complex_edge_collapser.h +337 -0
- multipers/gudhi/gudhi/Matrix.h +2200 -0
- multipers/gudhi/gudhi/Multi_filtration/Multi_parameter_generator.h +1712 -0
- multipers/gudhi/gudhi/Multi_filtration/multi_filtration_conversions.h +237 -0
- multipers/gudhi/gudhi/Multi_filtration/multi_filtration_utils.h +225 -0
- multipers/gudhi/gudhi/Multi_parameter_filtered_complex.h +485 -0
- multipers/gudhi/gudhi/Multi_parameter_filtration.h +2643 -0
- multipers/gudhi/gudhi/Multi_persistence/Box.h +233 -0
- multipers/gudhi/gudhi/Multi_persistence/Line.h +309 -0
- multipers/gudhi/gudhi/Multi_persistence/Multi_parameter_filtered_complex_pcoh_interface.h +268 -0
- multipers/gudhi/gudhi/Multi_persistence/Persistence_interface_cohomology.h +159 -0
- multipers/gudhi/gudhi/Multi_persistence/Persistence_interface_matrix.h +463 -0
- multipers/gudhi/gudhi/Multi_persistence/Point.h +853 -0
- multipers/gudhi/gudhi/Off_reader.h +173 -0
- multipers/gudhi/gudhi/Persistence_matrix/Base_matrix.h +834 -0
- multipers/gudhi/gudhi/Persistence_matrix/Base_matrix_with_column_compression.h +838 -0
- multipers/gudhi/gudhi/Persistence_matrix/Boundary_matrix.h +833 -0
- multipers/gudhi/gudhi/Persistence_matrix/Chain_matrix.h +1367 -0
- multipers/gudhi/gudhi/Persistence_matrix/Id_to_index_overlay.h +1157 -0
- multipers/gudhi/gudhi/Persistence_matrix/Position_to_index_overlay.h +869 -0
- multipers/gudhi/gudhi/Persistence_matrix/RU_matrix.h +905 -0
- multipers/gudhi/gudhi/Persistence_matrix/allocators/entry_constructors.h +122 -0
- multipers/gudhi/gudhi/Persistence_matrix/base_pairing.h +260 -0
- multipers/gudhi/gudhi/Persistence_matrix/base_swap.h +288 -0
- multipers/gudhi/gudhi/Persistence_matrix/chain_pairing.h +170 -0
- multipers/gudhi/gudhi/Persistence_matrix/chain_rep_cycles.h +247 -0
- multipers/gudhi/gudhi/Persistence_matrix/chain_vine_swap.h +571 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/chain_column_extra_properties.h +182 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/column_dimension_holder.h +130 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/column_utilities.h +235 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/entry_types.h +312 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/heap_column.h +1092 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/intrusive_list_column.h +923 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/intrusive_set_column.h +914 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/list_column.h +930 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/naive_vector_column.h +1071 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/row_access.h +203 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/set_column.h +886 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/unordered_set_column.h +984 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/vector_column.h +1213 -0
- multipers/gudhi/gudhi/Persistence_matrix/index_mapper.h +58 -0
- multipers/gudhi/gudhi/Persistence_matrix/matrix_dimension_holders.h +227 -0
- multipers/gudhi/gudhi/Persistence_matrix/matrix_row_access.h +200 -0
- multipers/gudhi/gudhi/Persistence_matrix/ru_pairing.h +166 -0
- multipers/gudhi/gudhi/Persistence_matrix/ru_rep_cycles.h +319 -0
- multipers/gudhi/gudhi/Persistence_matrix/ru_vine_swap.h +562 -0
- multipers/gudhi/gudhi/Persistence_on_a_line.h +152 -0
- multipers/gudhi/gudhi/Persistence_on_rectangle.h +617 -0
- multipers/gudhi/gudhi/Persistent_cohomology/Field_Zp.h +118 -0
- multipers/gudhi/gudhi/Persistent_cohomology/Multi_field.h +173 -0
- multipers/gudhi/gudhi/Persistent_cohomology/Persistent_cohomology_column.h +128 -0
- multipers/gudhi/gudhi/Persistent_cohomology.h +769 -0
- multipers/gudhi/gudhi/Points_off_io.h +171 -0
- multipers/gudhi/gudhi/Projective_cover_kernel.h +379 -0
- multipers/gudhi/gudhi/Simple_object_pool.h +69 -0
- multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_iterators.h +559 -0
- multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h +83 -0
- multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_siblings.h +121 -0
- multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_star_simplex_iterators.h +277 -0
- multipers/gudhi/gudhi/Simplex_tree/filtration_value_utils.h +155 -0
- multipers/gudhi/gudhi/Simplex_tree/hooks_simplex_base.h +62 -0
- multipers/gudhi/gudhi/Simplex_tree/indexing_tag.h +27 -0
- multipers/gudhi/gudhi/Simplex_tree/serialization_utils.h +60 -0
- multipers/gudhi/gudhi/Simplex_tree/simplex_tree_options.h +105 -0
- multipers/gudhi/gudhi/Simplex_tree.h +3170 -0
- multipers/gudhi/gudhi/Slicer.h +848 -0
- multipers/gudhi/gudhi/Thread_safe_slicer.h +393 -0
- multipers/gudhi/gudhi/distance_functions.h +62 -0
- multipers/gudhi/gudhi/graph_simplicial_complex.h +104 -0
- multipers/gudhi/gudhi/multi_simplex_tree_helpers.h +147 -0
- multipers/gudhi/gudhi/persistence_interval.h +263 -0
- multipers/gudhi/gudhi/persistence_matrix_options.h +188 -0
- multipers/gudhi/gudhi/reader_utils.h +367 -0
- multipers/gudhi/gudhi/simple_mdspan.h +484 -0
- multipers/gudhi/gudhi/slicer_helpers.h +779 -0
- multipers/gudhi/tmp_h0_pers/mma_interface_h0.h +223 -0
- multipers/gudhi/tmp_h0_pers/naive_merge_tree.h +536 -0
- multipers/io.cpython-312-darwin.so +0 -0
- multipers/io.pyx +472 -0
- multipers/ml/__init__.py +0 -0
- multipers/ml/accuracies.py +90 -0
- multipers/ml/invariants_with_persistable.py +79 -0
- multipers/ml/kernels.py +176 -0
- multipers/ml/mma.py +713 -0
- multipers/ml/one.py +472 -0
- multipers/ml/point_clouds.py +352 -0
- multipers/ml/signed_measures.py +1667 -0
- multipers/ml/sliced_wasserstein.py +461 -0
- multipers/ml/tools.py +113 -0
- multipers/mma_structures.cpython-312-darwin.so +0 -0
- multipers/mma_structures.pxd +134 -0
- multipers/mma_structures.pyx +1483 -0
- multipers/mma_structures.pyx.tp +1126 -0
- multipers/multi_parameter_rank_invariant/diff_helpers.h +85 -0
- multipers/multi_parameter_rank_invariant/euler_characteristic.h +95 -0
- multipers/multi_parameter_rank_invariant/function_rips.h +317 -0
- multipers/multi_parameter_rank_invariant/hilbert_function.h +761 -0
- multipers/multi_parameter_rank_invariant/persistence_slices.h +149 -0
- multipers/multi_parameter_rank_invariant/rank_invariant.h +350 -0
- multipers/multiparameter_edge_collapse.py +41 -0
- multipers/multiparameter_module_approximation/approximation.h +2541 -0
- multipers/multiparameter_module_approximation/debug.h +107 -0
- multipers/multiparameter_module_approximation/format_python-cpp.h +292 -0
- multipers/multiparameter_module_approximation/utilities.h +428 -0
- multipers/multiparameter_module_approximation.cpython-312-darwin.so +0 -0
- multipers/multiparameter_module_approximation.pyx +286 -0
- multipers/ops.cpython-312-darwin.so +0 -0
- multipers/ops.pyx +231 -0
- multipers/pickle.py +89 -0
- multipers/plots.py +550 -0
- multipers/point_measure.cpython-312-darwin.so +0 -0
- multipers/point_measure.pyx +409 -0
- multipers/simplex_tree_multi.cpython-312-darwin.so +0 -0
- multipers/simplex_tree_multi.pxd +136 -0
- multipers/simplex_tree_multi.pyx +11719 -0
- multipers/simplex_tree_multi.pyx.tp +2102 -0
- multipers/slicer.cpython-312-darwin.so +0 -0
- multipers/slicer.pxd +2097 -0
- multipers/slicer.pxd.tp +263 -0
- multipers/slicer.pyx +13042 -0
- multipers/slicer.pyx.tp +1259 -0
- multipers/tensor/tensor.h +672 -0
- multipers/tensor.pxd +13 -0
- multipers/test.pyx +44 -0
- multipers/tests/__init__.py +70 -0
- multipers/torch/__init__.py +1 -0
- multipers/torch/diff_grids.py +240 -0
- multipers/torch/rips_density.py +310 -0
- multipers/vector_interface.pxd +46 -0
- multipers-2.4.0b1.dist-info/METADATA +131 -0
- multipers-2.4.0b1.dist-info/RECORD +184 -0
- multipers-2.4.0b1.dist-info/WHEEL +6 -0
- multipers-2.4.0b1.dist-info/licenses/LICENSE +21 -0
- multipers-2.4.0b1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,779 @@
|
|
|
1
|
+
/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT.
|
|
2
|
+
* See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details.
|
|
3
|
+
* Author(s): David Loiseaux, Hannah Schreiber
|
|
4
|
+
*
|
|
5
|
+
* Copyright (C) 2023-25 Inria
|
|
6
|
+
*
|
|
7
|
+
* Modification(s):
|
|
8
|
+
* - YYYY/MM Author: Description of the modification
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @file slicer_helpers.h
|
|
13
|
+
* @author David Loiseaux, Hannah Schreiber
|
|
14
|
+
* @brief Contains the helper methods @ref Gudhi::multi_persistence::build_complex_from_scc_file,
|
|
15
|
+
* @ref Gudhi::multi_persistence::write_complex_to_scc_file, @ref Gudhi::multi_persistence::build_slicer_from_scc_file,
|
|
16
|
+
* @ref Gudhi::multi_persistence::build_complex_from_bitmap and @ref Gudhi::multi_persistence::build_slicer_from_bitmap.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
#ifndef MP_SLICER_HELPERS_H_
|
|
20
|
+
#define MP_SLICER_HELPERS_H_
|
|
21
|
+
|
|
22
|
+
#include <algorithm>
|
|
23
|
+
#include <cstddef>
|
|
24
|
+
#include <numeric>
|
|
25
|
+
#include <ostream>
|
|
26
|
+
#include <fstream>
|
|
27
|
+
#include <iostream>
|
|
28
|
+
#include <stdexcept>
|
|
29
|
+
#include <string>
|
|
30
|
+
#include <type_traits>
|
|
31
|
+
#include <vector>
|
|
32
|
+
#include <set>
|
|
33
|
+
#include <limits>
|
|
34
|
+
#include <iomanip>
|
|
35
|
+
#include <cmath>
|
|
36
|
+
|
|
37
|
+
#ifdef GUDHI_USE_TBB
|
|
38
|
+
#include <oneapi/tbb/enumerable_thread_specific.h>
|
|
39
|
+
#include <oneapi/tbb/parallel_for.h>
|
|
40
|
+
#endif
|
|
41
|
+
|
|
42
|
+
#include <boost/range/iterator_range_core.hpp>
|
|
43
|
+
|
|
44
|
+
#include <gudhi/Debug_utils.h>
|
|
45
|
+
#include <gudhi/simple_mdspan.h>
|
|
46
|
+
#include <gudhi/Multi_parameter_filtered_complex.h>
|
|
47
|
+
#include <gudhi/Bitmap_cubical_complex.h>
|
|
48
|
+
#include <gudhi/Simplex_tree.h>
|
|
49
|
+
#include <gudhi/Multi_filtration/multi_filtration_utils.h>
|
|
50
|
+
#include <gudhi/Multi_filtration/multi_filtration_conversions.h>
|
|
51
|
+
#include <gudhi/Multi_persistence/Line.h>
|
|
52
|
+
|
|
53
|
+
namespace Gudhi {
|
|
54
|
+
namespace multi_persistence {
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @ingroup multi_persistence
|
|
58
|
+
*
|
|
59
|
+
* @brief Builds a complex for the scc format file given. Assumes that every index appearing in a boundary in the file
|
|
60
|
+
* corresponds to a real line in the file (for example, the lowest dimension has always empty boundaries).
|
|
61
|
+
*
|
|
62
|
+
* @tparam MultiFiltrationValue Filtration value class respecting the @ref MultiFiltrationValue concept. It will be
|
|
63
|
+
* used as filtration value type of the new complex.
|
|
64
|
+
* @param inFilePath Path to scc file.
|
|
65
|
+
* @param isRivetCompatible Set to true if the file is written such that Rivet can read it. See TODO ref.
|
|
66
|
+
* Default value: false.
|
|
67
|
+
* @param isReversed Set to true if the cells in the file are written in increasing dimension order instead of
|
|
68
|
+
* the standard decreasing order. Default value: false.
|
|
69
|
+
* @param shiftDimensions Indicates if there is a shift in the dimension written in the file: if the value is 0, it
|
|
70
|
+
* means that the smallest dimension is 0, if the value is positive, the smallest dimension is assumed to be
|
|
71
|
+
* `shiftDimensions` instead of 0, and if the value is negative, the `abs(shiftDimensions)` smallest dimensions in
|
|
72
|
+
* the file are ignored and the smallest remaining dimension is interpreted as 0. Default value: 0.
|
|
73
|
+
*/
|
|
74
|
+
template <class MultiFiltrationValue>
|
|
75
|
+
inline Multi_parameter_filtered_complex<MultiFiltrationValue> build_complex_from_scc_file(
|
|
76
|
+
const std::string& inFilePath,
|
|
77
|
+
bool isRivetCompatible = false,
|
|
78
|
+
bool isReversed = false,
|
|
79
|
+
int shiftDimensions = 0)
|
|
80
|
+
{
|
|
81
|
+
using Fil = MultiFiltrationValue;
|
|
82
|
+
using Complex = Multi_parameter_filtered_complex<Fil>;
|
|
83
|
+
using Index = typename Complex::Index;
|
|
84
|
+
|
|
85
|
+
std::string line;
|
|
86
|
+
std::ifstream file(inFilePath);
|
|
87
|
+
unsigned int numberOfParameters;
|
|
88
|
+
|
|
89
|
+
if (!file.is_open()) {
|
|
90
|
+
// TODO: throw instead?
|
|
91
|
+
std::cerr << "Unable to open input file: " << inFilePath << '\n';
|
|
92
|
+
file.setstate(std::ios::failbit);
|
|
93
|
+
return Complex();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
auto error = [&file](const std::string& msg) {
|
|
97
|
+
file.close();
|
|
98
|
+
throw std::invalid_argument(msg);
|
|
99
|
+
};
|
|
100
|
+
auto is_comment_or_empty_line = [](const std::string& line) -> bool {
|
|
101
|
+
size_t current = line.find_first_not_of(' ', 0);
|
|
102
|
+
if (current == std::string::npos) return true; // is empty line
|
|
103
|
+
if (line[current] == '#') return true; // is comment
|
|
104
|
+
return false;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
while (getline(file, line, '\n') && is_comment_or_empty_line(line));
|
|
108
|
+
if (!file) error("Empty file!");
|
|
109
|
+
|
|
110
|
+
if (isRivetCompatible && line != "firep") error("Wrong file format. Should start with 'firep'.");
|
|
111
|
+
if (!isRivetCompatible && line != "scc2020") error("Wrong file format. Should start with 'scc2020'.");
|
|
112
|
+
|
|
113
|
+
while (getline(file, line, '\n') && is_comment_or_empty_line(line));
|
|
114
|
+
if (!file) error("Premature ending of the file. Stops before numbers of parameters.");
|
|
115
|
+
|
|
116
|
+
if (isRivetCompatible) {
|
|
117
|
+
numberOfParameters = 2;
|
|
118
|
+
getline(file, line, '\n'); // second rivet label
|
|
119
|
+
} else {
|
|
120
|
+
std::size_t current = line.find_first_not_of(' ', 0);
|
|
121
|
+
std::size_t next = line.find_first_of(' ', current);
|
|
122
|
+
numberOfParameters = std::stoi(line.substr(current, next - current));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
while (getline(file, line, '\n') && is_comment_or_empty_line(line));
|
|
126
|
+
if (!file) error("Premature ending of the file. Not a single cell was specified.");
|
|
127
|
+
|
|
128
|
+
std::vector<unsigned int> counts;
|
|
129
|
+
Index numberOfCells = 0;
|
|
130
|
+
counts.reserve(line.size() + shiftDimensions);
|
|
131
|
+
std::size_t current = line.find_first_not_of(' ', 0);
|
|
132
|
+
if (shiftDimensions != 0 && isReversed && current != std::string::npos) {
|
|
133
|
+
if (shiftDimensions > 0) {
|
|
134
|
+
counts.resize(shiftDimensions, 0);
|
|
135
|
+
} else {
|
|
136
|
+
for (int i = shiftDimensions; i < 0 && current != std::string::npos; ++i) {
|
|
137
|
+
std::size_t next = line.find_first_of(' ', current);
|
|
138
|
+
current = line.find_first_not_of(' ', next);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
while (current != std::string::npos) {
|
|
143
|
+
std::size_t next = line.find_first_of(' ', current);
|
|
144
|
+
counts.push_back(std::stoi(line.substr(current, next - current)));
|
|
145
|
+
numberOfCells += counts.back();
|
|
146
|
+
current = line.find_first_not_of(' ', next);
|
|
147
|
+
}
|
|
148
|
+
if (shiftDimensions != 0 && !isReversed) {
|
|
149
|
+
counts.resize(counts.size() + shiftDimensions, 0);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
std::size_t dimIt = 0;
|
|
153
|
+
while (dimIt < counts.size() && counts[dimIt] == 0) ++dimIt;
|
|
154
|
+
|
|
155
|
+
if (dimIt == counts.size()) return Complex();
|
|
156
|
+
|
|
157
|
+
std::size_t shift = isReversed ? 0 : counts[dimIt];
|
|
158
|
+
unsigned int nextShift = isReversed ? 0 : counts.size() == 1 ? 0 : counts[dimIt + 1];
|
|
159
|
+
unsigned int tmpNextShift = counts[dimIt];
|
|
160
|
+
|
|
161
|
+
auto get_boundary = [&isReversed, &numberOfCells](
|
|
162
|
+
const std::string& line, std::size_t start, std::size_t shift) -> std::vector<Index> {
|
|
163
|
+
std::vector<Index> res;
|
|
164
|
+
res.reserve(line.size() - start);
|
|
165
|
+
std::size_t current = line.find_first_not_of(' ', start);
|
|
166
|
+
while (current != std::string::npos) {
|
|
167
|
+
std::size_t next = line.find_first_of(' ', current);
|
|
168
|
+
Index idx = std::stoi(line.substr(current, next - current)) + shift;
|
|
169
|
+
res.push_back(isReversed ? idx : numberOfCells - 1 - idx);
|
|
170
|
+
current = line.find_first_not_of(' ', next);
|
|
171
|
+
}
|
|
172
|
+
std::sort(res.begin(), res.end());
|
|
173
|
+
return res;
|
|
174
|
+
};
|
|
175
|
+
auto get_filtration_value = [numberOfParameters, &error](const std::string& line, std::size_t end) -> Fil {
|
|
176
|
+
std::vector<typename Fil::value_type> res;
|
|
177
|
+
res.reserve(end);
|
|
178
|
+
bool isPlusInf = true;
|
|
179
|
+
bool isMinusInf = true;
|
|
180
|
+
std::size_t current = line.find_first_not_of(' ', 0);
|
|
181
|
+
while (current < end) {
|
|
182
|
+
std::size_t next = line.find_first_of(' ', current);
|
|
183
|
+
res.push_back(std::stod(line.substr(current, next - current)));
|
|
184
|
+
if (isPlusInf && res.back() != Fil::T_inf) isPlusInf = false;
|
|
185
|
+
if (isMinusInf && res.back() != Fil::T_m_inf) isMinusInf = false;
|
|
186
|
+
current = line.find_first_not_of(' ', next);
|
|
187
|
+
}
|
|
188
|
+
if (isPlusInf) return Fil::inf(numberOfParameters);
|
|
189
|
+
if (isMinusInf) return Fil::minus_inf(numberOfParameters);
|
|
190
|
+
if (res.size() % numberOfParameters != 0) error("Wrong format. The number of parameters does not match.");
|
|
191
|
+
return Fil(res.begin(), res.end(), numberOfParameters);
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
typename Complex::Boundary_container boundaries(numberOfCells);
|
|
195
|
+
typename Complex::Dimension_container dimensions(numberOfCells);
|
|
196
|
+
typename Complex::Filtration_value_container filtrationValues(numberOfCells);
|
|
197
|
+
std::size_t i = 0;
|
|
198
|
+
// because of possible negative dimension shifts, the document should not always be read to the end
|
|
199
|
+
// therefore `dimIt < counts.size()` is also a stop condition
|
|
200
|
+
while (getline(file, line, '\n') && dimIt < counts.size()) {
|
|
201
|
+
if (!is_comment_or_empty_line(line)) {
|
|
202
|
+
std::size_t sep = line.find_first_of(';', 0);
|
|
203
|
+
filtrationValues[i] = get_filtration_value(line, sep);
|
|
204
|
+
boundaries[i] = get_boundary(line, sep + 1, shift);
|
|
205
|
+
dimensions[i] = isReversed ? dimIt : counts.size() - 1 - dimIt;
|
|
206
|
+
|
|
207
|
+
--counts[dimIt];
|
|
208
|
+
while (dimIt < counts.size() && counts[dimIt] == 0) {
|
|
209
|
+
++dimIt;
|
|
210
|
+
if (dimIt != counts.size()) {
|
|
211
|
+
shift += nextShift;
|
|
212
|
+
nextShift = isReversed ? tmpNextShift : dimIt < counts.size() - 1 ? counts[dimIt + 1] : 0;
|
|
213
|
+
tmpNextShift = counts[dimIt];
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
++i;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (!isReversed) { // to order by dimension
|
|
221
|
+
std::reverse(dimensions.begin(), dimensions.end());
|
|
222
|
+
std::reverse(boundaries.begin(), boundaries.end());
|
|
223
|
+
std::reverse(filtrationValues.begin(), filtrationValues.end());
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
file.close();
|
|
227
|
+
|
|
228
|
+
return Complex(std::move(boundaries), std::move(dimensions), std::move(filtrationValues));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* @ingroup multi_persistence
|
|
233
|
+
*
|
|
234
|
+
* @brief Writes the given complex into a file with scc format. Assumes that every index appearing in a boundary of
|
|
235
|
+
* the complex corresponds to an existing index in the complex (for example, the lowest dimension has always empty
|
|
236
|
+
* boundaries).
|
|
237
|
+
*
|
|
238
|
+
* @tparam MultiFiltrationValue Filtration value of the given complex.
|
|
239
|
+
* @param outFilePath Path with file name into which to write.
|
|
240
|
+
* @param complex Complex to write. Every index appearing in a boundary of the complex has to correspond to an existing
|
|
241
|
+
* index in the complex
|
|
242
|
+
* @param degree TODO Default value: -1.
|
|
243
|
+
* @param rivetCompatible Set to true if the written file has to be Rivet compatible. Note that Rivet only accepts
|
|
244
|
+
* bi-filtrations. Default value: false.
|
|
245
|
+
* @param ignoreLastGenerators Set to true, if the generators with last dimension in the list should be ignored
|
|
246
|
+
* (maximal dimension by default, minimal dimension if `reverse` is true). Default value: false.
|
|
247
|
+
* @param stripComments Set to true, if no comment should be written in the file (comments are lines starting with `#`
|
|
248
|
+
* and which are ignored when read). Default value: false.
|
|
249
|
+
* @param reverse Set to true if the generators should be written in increasing order of dimension instead of
|
|
250
|
+
* decreasing. Default value: false.
|
|
251
|
+
*/
|
|
252
|
+
template <class MultiFiltrationValue>
|
|
253
|
+
inline void write_complex_to_scc_file(const std::string& outFilePath,
|
|
254
|
+
const Multi_parameter_filtered_complex<MultiFiltrationValue>& complex,
|
|
255
|
+
int degree = -1,
|
|
256
|
+
bool rivetCompatible = false,
|
|
257
|
+
bool ignoreLastGenerators = false,
|
|
258
|
+
bool stripComments = false,
|
|
259
|
+
bool reverse = false)
|
|
260
|
+
{
|
|
261
|
+
if (!complex.is_ordered_by_dimension()) {
|
|
262
|
+
// other solution would be to call build_permuted_complex ourself, but this is a good way to make the
|
|
263
|
+
// user aware of it.
|
|
264
|
+
throw std::invalid_argument(
|
|
265
|
+
"The given complex has to be ordered by dimension. If it is not the case, call this method with "
|
|
266
|
+
"`build_permuted_complex(complex).first` or `build_permuted_complex(complex, permutation_by_dim)` instead.");
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
unsigned int numberOfParameters = complex.get_number_of_parameters();
|
|
271
|
+
|
|
272
|
+
std::ofstream file(outFilePath);
|
|
273
|
+
|
|
274
|
+
if (rivetCompatible)
|
|
275
|
+
file << "firep\n";
|
|
276
|
+
else
|
|
277
|
+
file << "scc2020\n";
|
|
278
|
+
|
|
279
|
+
// TODO: change line for gudhi
|
|
280
|
+
if (!stripComments && !rivetCompatible)
|
|
281
|
+
file << "# This file was generated by multipers (https://github.com/DavidLapous/multipers).\n";
|
|
282
|
+
|
|
283
|
+
if (!stripComments && !rivetCompatible) file << "# Number of parameters\n";
|
|
284
|
+
|
|
285
|
+
if (rivetCompatible) {
|
|
286
|
+
GUDHI_CHECK(numberOfParameters == 2, "Rivet only handles bifiltrations.");
|
|
287
|
+
file << "Filtration 1\n";
|
|
288
|
+
file << "Filtration 2\n";
|
|
289
|
+
} else {
|
|
290
|
+
file << std::to_string(numberOfParameters) << "\n";
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (!stripComments) file << "# Sizes of generating sets\n";
|
|
294
|
+
|
|
295
|
+
using Fil = MultiFiltrationValue;
|
|
296
|
+
|
|
297
|
+
int maxDim = complex.get_max_dimension();
|
|
298
|
+
int minDim = maxDim;
|
|
299
|
+
const auto& dimensions = complex.get_dimensions();
|
|
300
|
+
|
|
301
|
+
std::vector<std::vector<std::size_t> > indicesByDim(maxDim + 1);
|
|
302
|
+
std::vector<std::size_t> shiftedIndices(complex.get_number_of_cycle_generators());
|
|
303
|
+
for (std::size_t i = 0; i < complex.get_number_of_cycle_generators(); ++i) {
|
|
304
|
+
auto dim = dimensions[i];
|
|
305
|
+
minDim = dim < minDim ? dim : minDim;
|
|
306
|
+
auto& atDim = indicesByDim[reverse ? dim : maxDim - dim];
|
|
307
|
+
shiftedIndices[i] = atDim.size();
|
|
308
|
+
atDim.push_back(i);
|
|
309
|
+
}
|
|
310
|
+
if (degree < 0) degree = minDim;
|
|
311
|
+
int minIndex = reverse ? degree - 1 : 0;
|
|
312
|
+
int maxIndex = reverse ? maxDim : maxDim - degree + 1;
|
|
313
|
+
maxIndex = std::max(maxIndex, -1);
|
|
314
|
+
if (ignoreLastGenerators) maxIndex--;
|
|
315
|
+
if (rivetCompatible) minIndex = maxIndex - 2;
|
|
316
|
+
|
|
317
|
+
#ifdef DEBUG_TRACES
|
|
318
|
+
std::cout << "minDim = " << minDim << " maxDim = " << maxDim << " minIndex = " << minIndex
|
|
319
|
+
<< " maxIndex = " << maxIndex << " degree = " << degree << std::endl;
|
|
320
|
+
#endif
|
|
321
|
+
|
|
322
|
+
auto print_fil_values = [&](const Fil& fil) {
|
|
323
|
+
GUDHI_CHECK(fil.num_parameters() == numberOfParameters, "Filtration value has wrong number of parameters.");
|
|
324
|
+
for (unsigned int g = 0; g < fil.num_generators(); ++g) {
|
|
325
|
+
for (unsigned int p = 0; p < fil.num_parameters(); ++p) {
|
|
326
|
+
file << fil(g, p) << " ";
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
if (minIndex < 0) file << 0 << " ";
|
|
332
|
+
for (int i = 0; i < minIndex; ++i) file << 0 << " ";
|
|
333
|
+
for (int i = std::max(minIndex, 0); i <= std::min(maxDim, maxIndex); ++i) {
|
|
334
|
+
file << indicesByDim[i].size() << " ";
|
|
335
|
+
}
|
|
336
|
+
if (!rivetCompatible)
|
|
337
|
+
for (int i = maxIndex + 1; i <= maxDim; ++i) file << 0 << " ";
|
|
338
|
+
if (maxIndex > maxDim) file << 0;
|
|
339
|
+
file << "\n";
|
|
340
|
+
|
|
341
|
+
file << std::setprecision(std::numeric_limits<typename Fil::value_type>::digits);
|
|
342
|
+
|
|
343
|
+
std::size_t startIndex = reverse ? minIndex + 1 : minIndex;
|
|
344
|
+
std::size_t endIndex = reverse ? maxIndex : maxIndex - 1;
|
|
345
|
+
const auto& filtValues = complex.get_filtration_values();
|
|
346
|
+
const auto& boundaries = complex.get_boundaries();
|
|
347
|
+
int currDim;
|
|
348
|
+
if (reverse)
|
|
349
|
+
currDim = minIndex == -1 ? 0 : minIndex;
|
|
350
|
+
else
|
|
351
|
+
currDim = maxIndex == maxDim + 1 ? maxDim + 1 : maxDim;
|
|
352
|
+
|
|
353
|
+
if (reverse) {
|
|
354
|
+
if (!stripComments) file << "# Block of dimension " << currDim++ << "\n";
|
|
355
|
+
if (minIndex >= 0) {
|
|
356
|
+
for (auto index : indicesByDim[minIndex]) {
|
|
357
|
+
print_fil_values(filtValues[index]);
|
|
358
|
+
file << ";\n";
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
for (std::size_t i = startIndex; i <= endIndex; ++i) {
|
|
363
|
+
if (!stripComments) {
|
|
364
|
+
file << "# Block of dimension " << currDim << "\n";
|
|
365
|
+
if (reverse)
|
|
366
|
+
++currDim;
|
|
367
|
+
else
|
|
368
|
+
--currDim;
|
|
369
|
+
}
|
|
370
|
+
for (auto index : indicesByDim[i]) {
|
|
371
|
+
print_fil_values(filtValues[index]);
|
|
372
|
+
file << "; ";
|
|
373
|
+
for (auto b : boundaries[index]) file << shiftedIndices[b] << " ";
|
|
374
|
+
file << "\n";
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
if (!reverse) {
|
|
378
|
+
if (!stripComments) file << "# Block of dimension " << currDim << "\n";
|
|
379
|
+
if (maxIndex <= maxDim) {
|
|
380
|
+
for (auto index : indicesByDim[maxIndex]) {
|
|
381
|
+
print_fil_values(filtValues[index]);
|
|
382
|
+
file << ";\n";
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* @ingroup multi_persistence
|
|
390
|
+
*
|
|
391
|
+
* @brief Builds a complex from the given bitmap. The bitmap here is a grid where each node contains a 1-critical
|
|
392
|
+
* filtration value, which will be interpreted as a vertex in a cubical complex. The filtration values of the higher
|
|
393
|
+
* dimensional cells are deduced by taking at each parameter the maximal value of its facets at this parameter.
|
|
394
|
+
*
|
|
395
|
+
* Note that for the bitmap to represent a valid multi-parameter filtration, all filtration values have to have the
|
|
396
|
+
* same number of parameters. The behaviour is undefined otherwise.
|
|
397
|
+
*
|
|
398
|
+
* @tparam OneCriticalMultiFiltrationValue Filtration value class respecting the @ref MultiFiltrationValue concept.
|
|
399
|
+
* It will be used as filtration value type of the new complex.
|
|
400
|
+
* @param vertexValues Bitmap with 1-critical filtration values. Represented as a single vector, the next input
|
|
401
|
+
* parameter @p shape indicates the shape of the real bitmap.
|
|
402
|
+
* @param shape Shape of the bitmap. E.g., if @p shape is \f$ {3, 4} \f$, then the bitmap is a \f$ (4 x 3) \f$ grid
|
|
403
|
+
* with four lines and three columns. The vector @p vertexValues should then contain 12 elements: the three first
|
|
404
|
+
* elements will be read as the first line, the three next elements as the second line etc. until having 4 lines.
|
|
405
|
+
*/
|
|
406
|
+
template <class OneCriticalMultiFiltrationValue>
|
|
407
|
+
inline Multi_parameter_filtered_complex<OneCriticalMultiFiltrationValue> build_complex_from_bitmap(
|
|
408
|
+
const std::vector<OneCriticalMultiFiltrationValue>& vertexValues,
|
|
409
|
+
const std::vector<unsigned int>& shape)
|
|
410
|
+
{
|
|
411
|
+
using Fil = OneCriticalMultiFiltrationValue;
|
|
412
|
+
using Complex = Multi_parameter_filtered_complex<Fil>;
|
|
413
|
+
using Index = typename Complex::Index;
|
|
414
|
+
using Bitmap_cubical_complex_base = Gudhi::cubical_complex::Bitmap_cubical_complex_base<char>;
|
|
415
|
+
using Bitmap_cubical_complex = Gudhi::cubical_complex::Bitmap_cubical_complex<Bitmap_cubical_complex_base>;
|
|
416
|
+
|
|
417
|
+
if (shape.empty() || vertexValues.empty()) return Complex();
|
|
418
|
+
|
|
419
|
+
unsigned int numberOfParameters = vertexValues[0].num_parameters();
|
|
420
|
+
|
|
421
|
+
Bitmap_cubical_complex cub(shape, std::vector<char>(vertexValues.size()), false);
|
|
422
|
+
|
|
423
|
+
const unsigned int numberOfSimplices = cub.num_simplices();
|
|
424
|
+
|
|
425
|
+
typename Complex::Dimension_container dimensions(numberOfSimplices);
|
|
426
|
+
typename Complex::Boundary_container boundaries(numberOfSimplices);
|
|
427
|
+
unsigned int i = 0;
|
|
428
|
+
for (unsigned int d = 0; d < shape.size() + 1; ++d) {
|
|
429
|
+
for (auto sh : cub.skeleton_simplex_range(d)) {
|
|
430
|
+
cub.assign_key(sh, i);
|
|
431
|
+
dimensions[i] = d;
|
|
432
|
+
auto& col = boundaries[i];
|
|
433
|
+
for (auto b : cub.boundary_simplex_range(sh)) col.push_back(cub.key(b));
|
|
434
|
+
std::sort(col.begin(), col.end());
|
|
435
|
+
++i;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
auto get_vertices = [&boundaries](Index i) -> std::set<Index> {
|
|
440
|
+
auto rec_get_vertices = [&boundaries](const auto& self, Index i, std::set<Index>& vertices) -> void {
|
|
441
|
+
if (boundaries[i].empty()) {
|
|
442
|
+
vertices.insert(i);
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
for (auto v : boundaries[i]) self(self, v, vertices);
|
|
446
|
+
};
|
|
447
|
+
std::set<Index> vertices;
|
|
448
|
+
rec_get_vertices(rec_get_vertices, i, vertices);
|
|
449
|
+
return vertices;
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
typename Complex::Filtration_value_container filtrationValues(numberOfSimplices, Fil(numberOfParameters));
|
|
453
|
+
|
|
454
|
+
for (Index g = 0; g < numberOfSimplices; ++g) {
|
|
455
|
+
if constexpr (Gudhi::multi_filtration::RangeTraits<Fil>::is_dynamic_multi_filtration) {
|
|
456
|
+
// should be faster than doing a proper `push_to_least_common_upper_bound` in the loop after
|
|
457
|
+
filtrationValues[g].force_generator_size_to_number_of_parameters(0);
|
|
458
|
+
}
|
|
459
|
+
for (auto v : get_vertices(g)) {
|
|
460
|
+
for (Index p = 0; p < numberOfParameters; ++p) {
|
|
461
|
+
// 1-critical
|
|
462
|
+
filtrationValues[g](0, p) = std::max(filtrationValues[g](0, p), vertexValues[v](0, p));
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
return Complex(std::move(boundaries), std::move(dimensions), std::move(filtrationValues));
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* @ingroup multi_persistence
|
|
472
|
+
*
|
|
473
|
+
* @brief Builds a complex from the given simplex tree. The complex will be ordered by dimension.
|
|
474
|
+
*
|
|
475
|
+
* @note The key values in the simplex tree nodes will be overwritten.
|
|
476
|
+
*
|
|
477
|
+
* @tparam MultiFiltrationValue Class following the @ref MultiFiltrationValue concept.
|
|
478
|
+
* @tparam SimplexTreeOptions Class following the @ref SimplexTreeOptions concept. Additionally, if
|
|
479
|
+
* `SimplexTreeOptions::Filtration_value` and `MultiFiltrationValue` are not the same type, there must
|
|
480
|
+
* be a method `as_type` taking `SimplexTreeOptions::Filtration_value` as argument and returning the value as an
|
|
481
|
+
* `MultiFiltrationValue` type. See @ref Gudhi::multi_filtration::as_type for implementations for
|
|
482
|
+
* @ref Gudhi::multi_filtration::Multi_parameter_filtration,
|
|
483
|
+
* @ref Gudhi::multi_filtration::Dynamic_multi_parameter_filtration and
|
|
484
|
+
* @ref Gudhi::multi_filtration::Degree_rips_bifiltration.
|
|
485
|
+
* @param simplexTree Simplex tree to convert. The key values of the simplex tree will be overwritten.
|
|
486
|
+
*/
|
|
487
|
+
template <class MultiFiltrationValue, class SimplexTreeOptions>
|
|
488
|
+
inline Multi_parameter_filtered_complex<MultiFiltrationValue> build_complex_from_simplex_tree(
|
|
489
|
+
Simplex_tree<SimplexTreeOptions>& simplexTree)
|
|
490
|
+
{
|
|
491
|
+
// declared here to enable custom `as_type` methods which are not in this namespace.
|
|
492
|
+
using namespace Gudhi::multi_filtration;
|
|
493
|
+
|
|
494
|
+
// TODO: is_multi_filtration will discriminate all pre-made multi filtration classes, but not any user made
|
|
495
|
+
// class following the MultiFiltrationValue concept (as it was more thought for inner use). The tests should be
|
|
496
|
+
// re-thought or this one just removed.
|
|
497
|
+
static_assert(RangeTraits<MultiFiltrationValue>::is_multi_filtration,
|
|
498
|
+
"Target filtration value type has to correspond to the MultiFiltrationValue concept.");
|
|
499
|
+
|
|
500
|
+
using Complex = Multi_parameter_filtered_complex<MultiFiltrationValue>;
|
|
501
|
+
|
|
502
|
+
const unsigned int numberOfSimplices = simplexTree.num_simplices();
|
|
503
|
+
|
|
504
|
+
if (numberOfSimplices == 0) return Complex();
|
|
505
|
+
|
|
506
|
+
typename Complex::Dimension_container dimensions(numberOfSimplices);
|
|
507
|
+
typename Complex::Boundary_container boundaries(numberOfSimplices);
|
|
508
|
+
typename Complex::Filtration_value_container filtrationValues(numberOfSimplices);
|
|
509
|
+
|
|
510
|
+
unsigned int i = 0;
|
|
511
|
+
// keys for boundaries have to be assigned first as we cannot use filtration_simplex_range to ensure that a face
|
|
512
|
+
// appears before its cofaces.
|
|
513
|
+
for (auto sh : simplexTree.complex_simplex_range()) {
|
|
514
|
+
simplexTree.assign_key(sh, i);
|
|
515
|
+
dimensions[i] = simplexTree.dimension(sh);
|
|
516
|
+
++i;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// Order simplices by dimension as an ordered Complex is more performant
|
|
520
|
+
std::vector<unsigned int> newToOldIndex(numberOfSimplices);
|
|
521
|
+
std::vector<unsigned int> oldToNewIndex(numberOfSimplices);
|
|
522
|
+
std::iota(newToOldIndex.begin(), newToOldIndex.end(), 0);
|
|
523
|
+
// stable sort to make the new complex more predicable and closer to a lexicographical sort in addition to dimension
|
|
524
|
+
std::stable_sort(newToOldIndex.begin(), newToOldIndex.end(), [&dimensions](unsigned int i, unsigned int j) {
|
|
525
|
+
return dimensions[i] < dimensions[j];
|
|
526
|
+
});
|
|
527
|
+
// Is there a way to directly get oldToNewIndex without constructing newToOldIndex?
|
|
528
|
+
for (unsigned int k = 0; k < numberOfSimplices; ++k) {
|
|
529
|
+
oldToNewIndex[newToOldIndex[k]] = k;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
for (auto sh : simplexTree.complex_simplex_range()) {
|
|
533
|
+
auto index = oldToNewIndex[simplexTree.key(sh)];
|
|
534
|
+
dimensions[index] = simplexTree.dimension(sh);
|
|
535
|
+
if constexpr (std::is_same_v<MultiFiltrationValue, typename SimplexTreeOptions::Filtration_value>) {
|
|
536
|
+
filtrationValues[index] = simplexTree.filtration(sh);
|
|
537
|
+
} else {
|
|
538
|
+
filtrationValues[index] = as_type<MultiFiltrationValue>(simplexTree.filtration(sh));
|
|
539
|
+
}
|
|
540
|
+
typename Complex::Boundary boundary(dimensions[index] == 0 ? 0 : dimensions[index] + 1);
|
|
541
|
+
unsigned int j = 0;
|
|
542
|
+
for (auto b : simplexTree.boundary_simplex_range(sh)) {
|
|
543
|
+
boundary[j] = oldToNewIndex[simplexTree.key(b)];
|
|
544
|
+
++j;
|
|
545
|
+
}
|
|
546
|
+
std::sort(boundary.begin(), boundary.end());
|
|
547
|
+
boundaries[index] = std::move(boundary);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
return Complex(std::move(boundaries), std::move(dimensions), std::move(filtrationValues));
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* @ingroup multi_persistence
|
|
555
|
+
*
|
|
556
|
+
* @brief Builds a slicer for the scc format file given. Assumes that every index appearing in a boundary in the file
|
|
557
|
+
* corresponds to a real line in the file (for example, the lowest dimension has always empty boundaries).
|
|
558
|
+
* See @ref Slicer::write_slicer_to_scc_file "write_slicer_to_scc_file" to write a slicer into a scc format file.
|
|
559
|
+
*
|
|
560
|
+
* @tparam Slicer The @ref Slicer class with any valid template combination.
|
|
561
|
+
* @param inFilePath Path to scc file.
|
|
562
|
+
* @param isRivetCompatible Set to true if the file is written such that Rivet can read it. See TODO ref.
|
|
563
|
+
* Default value: false.
|
|
564
|
+
* @param isReversed Set to true if the cells in the file are written in increasing dimension order instead of
|
|
565
|
+
* the standard decreasing order. Default value: false.
|
|
566
|
+
* @param shiftDimensions Indicates if there is a shift in the dimension written in the file: if the value is 0, it
|
|
567
|
+
* means that the smallest dimension is 0, if the value is positive, the smallest dimension is assumed to be
|
|
568
|
+
* `shiftDimensions` instead of 0, and if the value is negative, the `abs(shiftDimensions)` smallest dimensions in
|
|
569
|
+
* the file are ignored and the smallest remaining dimension is interpreted as 0. Default value: 0.
|
|
570
|
+
*/
|
|
571
|
+
template <class Slicer>
|
|
572
|
+
inline Slicer build_slicer_from_scc_file(const std::string& inFilePath,
|
|
573
|
+
bool isRivetCompatible = false,
|
|
574
|
+
bool isReversed = false,
|
|
575
|
+
int shiftDimensions = 0)
|
|
576
|
+
{
|
|
577
|
+
auto cpx = build_complex_from_scc_file<typename Slicer::Filtration_value>(
|
|
578
|
+
inFilePath, isRivetCompatible, isReversed, shiftDimensions);
|
|
579
|
+
return Slicer(std::move(cpx));
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* @ingroup multi_persistence
|
|
584
|
+
*
|
|
585
|
+
* @brief Builds a slicer from the given bitmap. The bitmap here is a grid where each node contains a 1-critical
|
|
586
|
+
* filtration value, which will be interpreted as a vertex in a cubical complex. The filtration values of the higher
|
|
587
|
+
* dimensional cells are deduced by taking at each parameter the maximal value of its facets at this parameter.
|
|
588
|
+
*
|
|
589
|
+
* Note that for the bitmap to represent a valid multi-parameter filtration, all filtration values have to have the
|
|
590
|
+
* same number of parameters. The behaviour is undefined otherwise.
|
|
591
|
+
*
|
|
592
|
+
* @tparam Slicer The @ref Slicer class with any valid template combination.
|
|
593
|
+
* @param vertexValues Bitmap with 1-critical filtration values. Represented as a single vector, the next input
|
|
594
|
+
* parameter @p shape indicates the shape of the real bitmap.
|
|
595
|
+
* @param shape Shape of the bitmap. E.g., if @p shape is \f$ {3, 4} \f$, then the bitmap is a \f$ (4 x 3) \f$ grid
|
|
596
|
+
* with four lines and three columns. The vector @p vertexValues should then contain 12 elements: the three first
|
|
597
|
+
* elements will be read as the first line, the three next elements as the second line etc. until having 4 lines.
|
|
598
|
+
*/
|
|
599
|
+
template <class Slicer>
|
|
600
|
+
inline Slicer build_slicer_from_bitmap(const std::vector<typename Slicer::Filtration_value>& vertexValues,
|
|
601
|
+
const std::vector<unsigned int>& shape)
|
|
602
|
+
{
|
|
603
|
+
auto cpx = build_complex_from_bitmap<typename Slicer::Filtration_value>(vertexValues, shape);
|
|
604
|
+
return Slicer(std::move(cpx));
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* @ingroup multi_persistence
|
|
609
|
+
*
|
|
610
|
+
* @brief Builds a slicer from the given simplex tree. The inner complex will be ordered by dimension.
|
|
611
|
+
*
|
|
612
|
+
* @tparam Slicer The @ref Slicer class with any valid template combination.
|
|
613
|
+
* @tparam SimplexTreeOptions Class following the @ref SimplexTreeOptions concept such that
|
|
614
|
+
* @ref SimplexTreeOptions::Filtration_value follows the @ref MultiFiltrationValue concept.
|
|
615
|
+
* @param simplexTree Simplex tree to convert.
|
|
616
|
+
*/
|
|
617
|
+
template <class Slicer, class SimplexTreeOptions>
|
|
618
|
+
inline Slicer build_slicer_from_simplex_tree(Simplex_tree<SimplexTreeOptions>& simplexTree)
|
|
619
|
+
{
|
|
620
|
+
auto cpx = build_complex_from_simplex_tree<typename Slicer::Filtration_value, SimplexTreeOptions>(simplexTree);
|
|
621
|
+
return Slicer(std::move(cpx));
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* @private
|
|
626
|
+
*/
|
|
627
|
+
template <bool idx, class U, class Slicer, class F>
|
|
628
|
+
std::vector<typename Slicer::template Multi_dimensional_flat_barcode<U>>
|
|
629
|
+
persistence_on_slices_(Slicer& slicer, F&& ini_slicer, unsigned int size, [[maybe_unused]] bool ignoreInf = true)
|
|
630
|
+
{
|
|
631
|
+
using Barcode = typename Slicer::template Multi_dimensional_flat_barcode<U>;
|
|
632
|
+
|
|
633
|
+
if (size == 0) return {};
|
|
634
|
+
|
|
635
|
+
std::vector<Barcode> out(size);
|
|
636
|
+
|
|
637
|
+
if constexpr (Slicer::Persistence::is_vine) {
|
|
638
|
+
std::forward<F>(ini_slicer)(slicer, 0);
|
|
639
|
+
slicer.initialize_persistence_computation(false);
|
|
640
|
+
out[0] = slicer.template get_flat_barcode<true, U, idx>();
|
|
641
|
+
for (auto i = 1U; i < size; ++i) {
|
|
642
|
+
std::forward<F>(ini_slicer)(slicer, i);
|
|
643
|
+
slicer.vineyard_update();
|
|
644
|
+
out[i] = slicer.template get_flat_barcode<true, U, idx>();
|
|
645
|
+
}
|
|
646
|
+
} else {
|
|
647
|
+
#ifdef GUDHI_USE_TBB
|
|
648
|
+
using Index = typename Slicer::Index;
|
|
649
|
+
tbb::enumerable_thread_specific<typename Slicer::Thread_safe> threadLocals(slicer.weak_copy());
|
|
650
|
+
tbb::parallel_for(static_cast<Index>(0), size, [&](const Index& i) {
|
|
651
|
+
typename Slicer::Thread_safe& s = threadLocals.local();
|
|
652
|
+
std::forward<F>(ini_slicer)(s, i);
|
|
653
|
+
s.initialize_persistence_computation(ignoreInf);
|
|
654
|
+
out[i] = s.template get_flat_barcode<true, U, idx>();
|
|
655
|
+
});
|
|
656
|
+
#else
|
|
657
|
+
for (auto i = 0U; i < size; ++i) {
|
|
658
|
+
std::forward<F>(ini_slicer)(slicer, i);
|
|
659
|
+
slicer.initialize_persistence_computation(ignoreInf);
|
|
660
|
+
out[i] = slicer.template get_flat_barcode<true, U, idx>();
|
|
661
|
+
}
|
|
662
|
+
#endif
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
return out;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* @ingroup multi_persistence
|
|
670
|
+
*
|
|
671
|
+
* @brief Returns the barcodes of all the given lines. A line is represented as a pair with the first element being
|
|
672
|
+
* a point on the line and the second element a vector giving the positive direction of the line. The direction
|
|
673
|
+
* container can be empty: then the slope is assumed to be 1.
|
|
674
|
+
*
|
|
675
|
+
* @tparam Slicer Either @ref Slicer or @ref Thread_safe_slicer class with any valid template combination.
|
|
676
|
+
* @tparam T Type of a coordinate element.
|
|
677
|
+
* @tparam U Type of filtration values in the output barcode. Default value: T.
|
|
678
|
+
* @tparam idx If true, the complex indices instead of the actual filtration values are used for the bars. It is
|
|
679
|
+
* recommended to use an integer type for `U` in that case. Default value: false.
|
|
680
|
+
* @param slicer Slicer from which to compute persistence.
|
|
681
|
+
* @param basePoints Vector of base points for the lines. The dimension of a point has to correspond to the number
|
|
682
|
+
* of parameters in the slicer.
|
|
683
|
+
* @param directions Vector of directions for the lines. A direction has to have the same dimension than a point.
|
|
684
|
+
* Can be empty, then the slope is assumed to be 1.
|
|
685
|
+
* @param ignoreInf If true, all cells at infinity filtration values are ignored when computing, resulting
|
|
686
|
+
* potentially in less storage use and better performance. But the parameter will be ignored if
|
|
687
|
+
* PersistenceAlgorithm::is_vine is true.
|
|
688
|
+
*/
|
|
689
|
+
template <class Slicer, class T, class U = T, bool idx = false>
|
|
690
|
+
std::vector<typename Slicer::template Multi_dimensional_flat_barcode<U>> persistence_on_slices(
|
|
691
|
+
Slicer& slicer,
|
|
692
|
+
const std::vector<std::vector<T>>& basePoints,
|
|
693
|
+
const std::vector<std::vector<T>>& directions,
|
|
694
|
+
bool ignoreInf = true)
|
|
695
|
+
{
|
|
696
|
+
GUDHI_CHECK(directions.empty() || directions.size() == basePoints.size(),
|
|
697
|
+
"There should be as many directions than base points.");
|
|
698
|
+
GUDHI_CHECK(basePoints.empty() || basePoints[0].size() == slicer.get_number_of_parameters(),
|
|
699
|
+
"There should be as many directions than base points.");
|
|
700
|
+
|
|
701
|
+
std::vector<T> dummy;
|
|
702
|
+
auto get_direction = [&](unsigned int i) -> const std::vector<T>& {
|
|
703
|
+
return directions.empty() ? dummy : directions[i];
|
|
704
|
+
};
|
|
705
|
+
|
|
706
|
+
return persistence_on_slices_<idx, U>(
|
|
707
|
+
slicer,
|
|
708
|
+
[&](auto& s, unsigned int i) { s.push_to(Line<T>(basePoints[i], get_direction(i))); },
|
|
709
|
+
basePoints.size(),
|
|
710
|
+
ignoreInf);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* @ingroup multi_persistence
|
|
715
|
+
*
|
|
716
|
+
* @brief Returns the barcodes of all the given slices.
|
|
717
|
+
*
|
|
718
|
+
* @tparam Slicer Either @ref Slicer or @ref Thread_safe_slicer class with any valid template combination.
|
|
719
|
+
* @tparam T Type of a slice element.
|
|
720
|
+
* @tparam U Type of filtration values in the output barcode. Default value: T.
|
|
721
|
+
* @tparam idx If true, the complex indices instead of the actual filtration values are used for the bars. It is
|
|
722
|
+
* recommended to use an integer type for `U` in that case. Default value: false.
|
|
723
|
+
* @param slicer Slicer from which to compute persistence.
|
|
724
|
+
* @param slices Vector of slices. A slice has to has as many elements than cells in the slicer.
|
|
725
|
+
* @param ignoreInf If true, all cells at infinity filtration values are ignored when computing, resulting
|
|
726
|
+
* potentially in less storage use and better performance. But the parameter will be ignored if
|
|
727
|
+
* PersistenceAlgorithm::is_vine is true.
|
|
728
|
+
*/
|
|
729
|
+
template <class Slicer, class T, class U = T, bool idx = false>
|
|
730
|
+
std::vector<typename Slicer::template Multi_dimensional_flat_barcode<U>>
|
|
731
|
+
persistence_on_slices(Slicer& slicer, const std::vector<std::vector<T>>& slices, bool ignoreInf = true)
|
|
732
|
+
{
|
|
733
|
+
GUDHI_CHECK(slices.empty() || slices[0].size() == slicer.get_number_of_cycle_generators(),
|
|
734
|
+
"There should be as many elements in a slice than cells in the slicer.");
|
|
735
|
+
|
|
736
|
+
return persistence_on_slices_<idx, U>(
|
|
737
|
+
slicer, [&](auto& s, unsigned int i) { s.set_slice(slices[i]); }, slices.size(), ignoreInf);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// Mostly for python
|
|
741
|
+
/**
|
|
742
|
+
* @ingroup multi_persistence
|
|
743
|
+
*
|
|
744
|
+
* @brief Returns the barcodes of all the given slices.
|
|
745
|
+
*
|
|
746
|
+
* @tparam Slicer Either @ref Slicer or @ref Thread_safe_slicer class with any valid template combination.
|
|
747
|
+
* @tparam T Type of a slice element.
|
|
748
|
+
* @tparam U Type of filtration values in the output barcode. Default value: T.
|
|
749
|
+
* @tparam idx If true, the complex indices instead of the actual filtration values are used for the bars. It is
|
|
750
|
+
* recommended to use an integer type for `U` in that case. Default value: false.
|
|
751
|
+
* @param slicer Slicer from which to compute persistence.
|
|
752
|
+
* @param slices Pointer to the begining of slices continuously aligned after another in the memory.
|
|
753
|
+
* @param numberOfSlices Number of slices represented by the pointer.
|
|
754
|
+
* @param ignoreInf If true, all cells at infinity filtration values are ignored when computing, resulting
|
|
755
|
+
* potentially in less storage use and better performance. But the parameter will be ignored if
|
|
756
|
+
* PersistenceAlgorithm::is_vine is true.
|
|
757
|
+
*/
|
|
758
|
+
template <class Slicer, class T, class U = T, bool idx = false, class = std::enable_if_t<std::is_arithmetic_v<T>>>
|
|
759
|
+
std::vector<typename Slicer::template Multi_dimensional_flat_barcode<U>>
|
|
760
|
+
persistence_on_slices(Slicer& slicer, T* slices, unsigned int numberOfSlices, bool ignoreInf = true)
|
|
761
|
+
{
|
|
762
|
+
auto num_gen = slicer.get_number_of_cycle_generators();
|
|
763
|
+
auto view = Gudhi::Simple_mdspan(slices, numberOfSlices, num_gen);
|
|
764
|
+
|
|
765
|
+
return persistence_on_slices_<idx, U>(
|
|
766
|
+
slicer,
|
|
767
|
+
[&](auto& s, unsigned int i) {
|
|
768
|
+
T* start = &view(i, 0);
|
|
769
|
+
auto r = boost::iterator_range<T*>(start, start + num_gen);
|
|
770
|
+
s.set_slice(r);
|
|
771
|
+
},
|
|
772
|
+
numberOfSlices,
|
|
773
|
+
ignoreInf);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
} // namespace multi_persistence
|
|
777
|
+
} // namespace Gudhi
|
|
778
|
+
|
|
779
|
+
#endif // MP_SLICER_HELPERS_H_
|