From 5407637f45369a3d454e0e92d6f0a6ae809737f5 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Mon, 17 Jul 2023 14:48:38 +0200 Subject: [PATCH 01/51] restore after merge --- .../benchmark/CMakeLists.txt | 17 + .../benchmark/Zigzag_benchmark.cpp | 198 ++ .../benchmark/Zigzag_old_benchmark.cpp | 112 + .../benchmark/Zigzag_other_benchmark.cpp | 144 + .../benchmark/rips-zigzag-dionysus.h | 211 ++ src/Zigzag_persistence/example/CMakeLists.txt | 29 + .../example/comparison_for_tests.cpp | 440 +++ .../example_rips_zigzag_filtration.cpp | 158 + .../example_simple_zigzag_filtration.cpp | 118 + .../example/ext_zz/dionysus/chain.h | 153 + .../example/ext_zz/dionysus/chain.hpp | 188 ++ .../ext_zz/dionysus/clearing-reduction.h | 45 + .../ext_zz/dionysus/clearing-reduction.hpp | 60 + .../example/ext_zz/dionysus/cnpy.h | 241 ++ .../ext_zz/dionysus/cohomology-persistence.h | 116 + .../dionysus/cohomology-persistence.hpp | 61 + .../example/ext_zz/dionysus/common.h | 25 + .../example/ext_zz/dionysus/diagram.h | 114 + .../example/ext_zz/dionysus/distances.h | 93 + .../example/ext_zz/dionysus/distances.hpp | 30 + .../example/ext_zz/dionysus/dlog/progress.h | 57 + .../example/ext_zz/dionysus/fields/q.h | 63 + .../example/ext_zz/dionysus/fields/z2.h | 31 + .../example/ext_zz/dionysus/fields/zp.h | 55 + .../example/ext_zz/dionysus/filtration.h | 124 + .../example/ext_zz/dionysus/format.h | 8 + .../example/ext_zz/dionysus/format/format.cc | 1156 ++++++++ .../example/ext_zz/dionysus/format/format.h | 2546 +++++++++++++++++ .../example/ext_zz/dionysus/grid/box.h | 136 + .../example/ext_zz/dionysus/grid/box.hpp | 141 + .../example/ext_zz/dionysus/grid/grid.h | 143 + .../example/ext_zz/dionysus/grid/point.h | 132 + .../example/ext_zz/dionysus/grid/vertices.h | 86 + .../ext_zz/dionysus/matrix-filtration.h | 120 + .../ext_zz/dionysus/omni-field-persistence.h | 145 + .../dionysus/omni-field-persistence.hpp | 250 ++ .../example/ext_zz/dionysus/opts/opts.h | 499 ++++ .../ext_zz/dionysus/ordinary-persistence.h | 64 + .../example/ext_zz/dionysus/pair-recorder.h | 78 + .../example/ext_zz/dionysus/reduced-matrix.h | 170 ++ .../ext_zz/dionysus/reduced-matrix.hpp | 78 + .../example/ext_zz/dionysus/reduction.h | 109 + .../dionysus/relative-homology-zigzag.h | 84 + .../dionysus/relative-homology-zigzag.hpp | 122 + .../example/ext_zz/dionysus/rips.h | 147 + .../example/ext_zz/dionysus/rips.hpp | 162 ++ .../example/ext_zz/dionysus/row-reduction.h | 54 + .../example/ext_zz/dionysus/row-reduction.hpp | 103 + .../example/ext_zz/dionysus/simplex.h | 280 ++ .../ext_zz/dionysus/sparse-row-matrix.h | 184 ++ .../ext_zz/dionysus/sparse-row-matrix.hpp | 103 + .../ext_zz/dionysus/standard-reduction.h | 44 + .../ext_zz/dionysus/standard-reduction.hpp | 47 + .../example/ext_zz/dionysus/trails-chains.h | 17 + .../ext_zz/dionysus/zigzag-persistence.h | 142 + .../ext_zz/dionysus/zigzag-persistence.hpp | 541 ++++ .../example/ext_zz/fzz/fzz.cpp | 206 ++ .../example/ext_zz/fzz/fzz.h | 87 + .../ext_zz/phat/algorithms/chunk_reduction.h | 223 ++ .../ext_zz/phat/algorithms/row_reduction.h | 56 + .../algorithms/spectral_sequence_reduction.h | 80 + .../phat/algorithms/standard_reduction.h | 47 + .../ext_zz/phat/algorithms/twist_reduction.h | 51 + .../example/ext_zz/phat/boundary_matrix.h | 343 +++ .../ext_zz/phat/compute_persistence_pairs.h | 128 + .../example/ext_zz/phat/helpers/dualize.h | 74 + .../example/ext_zz/phat/helpers/misc.h | 75 + .../phat/helpers/thread_local_storage.h | 52 + .../example/ext_zz/phat/persistence_pairs.h | 155 + .../representations/abstract_pivot_column.h | 102 + .../representations/bit_tree_pivot_column.h | 165 ++ .../phat/representations/full_pivot_column.h | 100 + .../phat/representations/heap_pivot_column.h | 126 + .../representations/sparse_pivot_column.h | 79 + .../ext_zz/phat/representations/vector_heap.h | 170 ++ .../ext_zz/phat/representations/vector_list.h | 101 + .../ext_zz/phat/representations/vector_set.h | 99 + .../phat/representations/vector_vector.h | 107 + .../example/rips-zigzag-dionysus.h | 211 ++ .../include/gudhi/Zigzag_persistence.h | 725 +++++ .../include/gudhi/Zigzag_persistence_old.h | 1326 +++++++++ src/Zigzag_persistence/test/CMakeLists.txt | 16 + .../test/zigzag_persistence_unit_test.cpp | 468 +++ 83 files changed, 16146 insertions(+) create mode 100644 src/Zigzag_persistence/benchmark/CMakeLists.txt create mode 100644 src/Zigzag_persistence/benchmark/Zigzag_benchmark.cpp create mode 100644 src/Zigzag_persistence/benchmark/Zigzag_old_benchmark.cpp create mode 100644 src/Zigzag_persistence/benchmark/Zigzag_other_benchmark.cpp create mode 100644 src/Zigzag_persistence/benchmark/rips-zigzag-dionysus.h create mode 100644 src/Zigzag_persistence/example/CMakeLists.txt create mode 100644 src/Zigzag_persistence/example/comparison_for_tests.cpp create mode 100644 src/Zigzag_persistence/example/example_rips_zigzag_filtration.cpp create mode 100644 src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/chain.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/chain.hpp create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/clearing-reduction.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/clearing-reduction.hpp create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/cnpy.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/cohomology-persistence.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/cohomology-persistence.hpp create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/common.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/diagram.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/distances.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/distances.hpp create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/dlog/progress.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/fields/q.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/fields/z2.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/fields/zp.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/filtration.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/format.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/format/format.cc create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/format/format.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/grid/box.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/grid/box.hpp create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/grid/grid.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/grid/point.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/grid/vertices.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/matrix-filtration.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/omni-field-persistence.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/omni-field-persistence.hpp create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/opts/opts.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/ordinary-persistence.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/pair-recorder.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/reduced-matrix.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/reduced-matrix.hpp create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/reduction.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/relative-homology-zigzag.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/relative-homology-zigzag.hpp create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/rips.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/rips.hpp create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/row-reduction.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/row-reduction.hpp create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/simplex.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/sparse-row-matrix.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/sparse-row-matrix.hpp create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/standard-reduction.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/standard-reduction.hpp create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/trails-chains.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/zigzag-persistence.h create mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/zigzag-persistence.hpp create mode 100644 src/Zigzag_persistence/example/ext_zz/fzz/fzz.cpp create mode 100644 src/Zigzag_persistence/example/ext_zz/fzz/fzz.h create mode 100644 src/Zigzag_persistence/example/ext_zz/phat/algorithms/chunk_reduction.h create mode 100644 src/Zigzag_persistence/example/ext_zz/phat/algorithms/row_reduction.h create mode 100644 src/Zigzag_persistence/example/ext_zz/phat/algorithms/spectral_sequence_reduction.h create mode 100644 src/Zigzag_persistence/example/ext_zz/phat/algorithms/standard_reduction.h create mode 100644 src/Zigzag_persistence/example/ext_zz/phat/algorithms/twist_reduction.h create mode 100644 src/Zigzag_persistence/example/ext_zz/phat/boundary_matrix.h create mode 100644 src/Zigzag_persistence/example/ext_zz/phat/compute_persistence_pairs.h create mode 100644 src/Zigzag_persistence/example/ext_zz/phat/helpers/dualize.h create mode 100644 src/Zigzag_persistence/example/ext_zz/phat/helpers/misc.h create mode 100644 src/Zigzag_persistence/example/ext_zz/phat/helpers/thread_local_storage.h create mode 100644 src/Zigzag_persistence/example/ext_zz/phat/persistence_pairs.h create mode 100644 src/Zigzag_persistence/example/ext_zz/phat/representations/abstract_pivot_column.h create mode 100644 src/Zigzag_persistence/example/ext_zz/phat/representations/bit_tree_pivot_column.h create mode 100644 src/Zigzag_persistence/example/ext_zz/phat/representations/full_pivot_column.h create mode 100644 src/Zigzag_persistence/example/ext_zz/phat/representations/heap_pivot_column.h create mode 100644 src/Zigzag_persistence/example/ext_zz/phat/representations/sparse_pivot_column.h create mode 100644 src/Zigzag_persistence/example/ext_zz/phat/representations/vector_heap.h create mode 100644 src/Zigzag_persistence/example/ext_zz/phat/representations/vector_list.h create mode 100644 src/Zigzag_persistence/example/ext_zz/phat/representations/vector_set.h create mode 100644 src/Zigzag_persistence/example/ext_zz/phat/representations/vector_vector.h create mode 100644 src/Zigzag_persistence/example/rips-zigzag-dionysus.h create mode 100644 src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h create mode 100644 src/Zigzag_persistence/include/gudhi/Zigzag_persistence_old.h create mode 100644 src/Zigzag_persistence/test/CMakeLists.txt create mode 100644 src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp diff --git a/src/Zigzag_persistence/benchmark/CMakeLists.txt b/src/Zigzag_persistence/benchmark/CMakeLists.txt new file mode 100644 index 0000000000..1f0faeb542 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/CMakeLists.txt @@ -0,0 +1,17 @@ +project(Zigzag_benchmark) + +find_package(benchmark REQUIRED) + +add_executable(Zigzag_benchmark Zigzag_benchmark.cpp) +target_link_libraries(Zigzag_benchmark benchmark::benchmark) +target_include_directories(Zigzag_benchmark PUBLIC . ../example/ext_zz) + +add_executable(Zigzag_old_benchmark Zigzag_old_benchmark.cpp) +target_link_libraries(Zigzag_old_benchmark benchmark::benchmark) +target_include_directories(Zigzag_old_benchmark PUBLIC . ../example/ext_zz) + +add_executable(Zigzag_other_benchmark Zigzag_other_benchmark.cpp ../example/ext_zz/fzz/fzz.cpp) +target_link_libraries(Zigzag_other_benchmark benchmark::benchmark) +target_include_directories(Zigzag_other_benchmark PUBLIC . ../example/ext_zz) +target_compile_options(Zigzag_other_benchmark PUBLIC "-fopenmp") +target_link_options(Zigzag_other_benchmark PUBLIC "-fopenmp") diff --git a/src/Zigzag_persistence/benchmark/Zigzag_benchmark.cpp b/src/Zigzag_persistence/benchmark/Zigzag_benchmark.cpp new file mode 100644 index 0000000000..3edac39c8d --- /dev/null +++ b/src/Zigzag_persistence/benchmark/Zigzag_benchmark.cpp @@ -0,0 +1,198 @@ +/* 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): Hannah Schreiber + * + * Copyright (C) 2022 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#include +#include +#include +#include +#include // for pair +#include +#include + +#include +#include + +#include +#include +#include + +#include "rips-zigzag-dionysus.h" + +using ST = Gudhi::Simplex_tree; +// using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; +using Vertex_handle = ST::Vertex_handle; +using Filtration_value = ST::Filtration_value; +// using interval_filtration = ZP::interval_filtration; + +using Gudhi::persistence_matrix::Zigzag_options; +using CT = Gudhi::persistence_matrix::Column_types; + +template +std::vector< std::pair > print_indices(ZP& zp, unsigned int numberOfSimplices){ + std::set essentials; + std::vector< std::pair > res; + + for (unsigned int i = 0; i < numberOfSimplices; ++i){ + essentials.insert(essentials.end(), i); + } + + for (auto& bar : zp.index_persistence_diagram()){ + res.emplace_back(bar.birth(), bar.death()); + essentials.erase(bar.birth()); + essentials.erase(bar.death()); + } + + for (unsigned int v : essentials){ + res.emplace_back(v, numberOfSimplices); + } + + return res; +} + +template +std::vector< std::pair > compute_with_gudhi( + const std::vector >& simplices, + const std::vector& dirs) +{ + ZP zp(simplices.size()); + + // std::cout << "====================== Gudhi =====================\n"; + + std::vector filValues(simplices.size(), 1.0); + + auto start = simplices.begin(); + auto filIt = filValues.begin(); + unsigned int i = 0; + + while (start != simplices.end()){ + unsigned int c = 1; + auto end = start + 1; + ++i; + while (end != simplices.end() && dirs[i - 1] == dirs[i]) { + ++end; + ++i; + ++c; + } + + if (dirs[i - 1]){ + zp.insert_simplices_contiguously( + start, end, filIt); + } else { + zp.remove_simplices_contiguously( + start, end, filIt); + } + + start = end; + filIt += c; + } + + // std::cout << "==================================================\n"; + + return print_indices(zp, i); +} + +static void Compute_zigzag_with_gudhi_intrusive_list(benchmark::State& state) { + using ZP = Gudhi::zigzag_persistence::Zigzag_persistence >; + + unsigned int numberOfPoints = state.range(0); + int seed = numberOfPoints; + std::vector > simplices; + std::vector dirs; + + build_rips_zigzag_filtration(simplices, dirs, numberOfPoints, seed); + + for (auto _ : state){ + compute_with_gudhi(simplices, dirs); + } +} +BENCHMARK(Compute_zigzag_with_gudhi_intrusive_list)->RangeMultiplier(2)->Range(100, 1000)->Unit(benchmark::kMillisecond)->MinWarmUpTime(1); + +static void Compute_zigzag_with_gudhi_intrusive_set(benchmark::State& state) { + using ZP = Gudhi::zigzag_persistence::Zigzag_persistence >; + + unsigned int numberOfPoints = state.range(0); + int seed = numberOfPoints; + std::vector > simplices; + std::vector dirs; + + build_rips_zigzag_filtration(simplices, dirs, numberOfPoints, seed); + + for (auto _ : state){ + compute_with_gudhi(simplices, dirs); + } +} +BENCHMARK(Compute_zigzag_with_gudhi_intrusive_set)->RangeMultiplier(2)->Range(100, 1000)->Unit(benchmark::kMillisecond)->MinWarmUpTime(1); + +static void Compute_zigzag_with_gudhi_list(benchmark::State& state) { + using ZP = Gudhi::zigzag_persistence::Zigzag_persistence >; + + unsigned int numberOfPoints = state.range(0); + int seed = numberOfPoints; + std::vector > simplices; + std::vector dirs; + + build_rips_zigzag_filtration(simplices, dirs, numberOfPoints, seed); + + for (auto _ : state){ + compute_with_gudhi(simplices, dirs); + } +} +BENCHMARK(Compute_zigzag_with_gudhi_list)->RangeMultiplier(2)->Range(100, 1000)->Unit(benchmark::kMillisecond)->MinWarmUpTime(1); + +// static void Compute_zigzag_with_gudhi_set(benchmark::State& state) { +// using ZP = Gudhi::zigzag_persistence::Zigzag_persistence >; + +// unsigned int numberOfPoints = state.range(0); +// int seed = numberOfPoints; +// std::vector > simplices; +// std::vector dirs; + +// build_rips_zigzag_filtration(simplices, dirs, numberOfPoints, seed); + +// for (auto _ : state){ +// compute_with_gudhi(simplices, dirs); +// } +// } +// BENCHMARK(Compute_zigzag_with_gudhi_set)->RangeMultiplier(2)->Range(100, 1000)->Unit(benchmark::kMillisecond)->MinWarmUpTime(1); + +// static void Compute_zigzag_with_gudhi_unordered_set(benchmark::State& state) { +// using ZP = Gudhi::zigzag_persistence::Zigzag_persistence>; + +// unsigned int numberOfPoints = state.range(0); +// int seed = numberOfPoints; +// std::vector > simplices; +// std::vector dirs; + +// build_rips_zigzag_filtration(simplices, dirs, numberOfPoints, seed); + +// for (auto _ : state){ +// compute_with_gudhi(simplices, dirs); +// } +// } +// BENCHMARK(Compute_zigzag_with_gudhi_unordered_set)->RangeMultiplier(2)->Range(100, 1000)->Unit(benchmark::kMillisecond)->MinWarmUpTime(1); + +static void Compute_zigzag_with_gudhi_vector(benchmark::State& state) { + using ZP = Gudhi::zigzag_persistence::Zigzag_persistence>; + + unsigned int numberOfPoints = state.range(0); + int seed = numberOfPoints; + std::vector > simplices; + std::vector dirs; + + build_rips_zigzag_filtration(simplices, dirs, numberOfPoints, seed); + + for (auto _ : state){ + compute_with_gudhi(simplices, dirs); + } +} +BENCHMARK(Compute_zigzag_with_gudhi_vector)->RangeMultiplier(2)->Range(100, 1000)->Unit(benchmark::kMillisecond)->MinWarmUpTime(1); + +BENCHMARK_MAIN(); + diff --git a/src/Zigzag_persistence/benchmark/Zigzag_old_benchmark.cpp b/src/Zigzag_persistence/benchmark/Zigzag_old_benchmark.cpp new file mode 100644 index 0000000000..bb24be2942 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/Zigzag_old_benchmark.cpp @@ -0,0 +1,112 @@ +/* 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): Hannah Schreiber + * + * Copyright (C) 2022 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#include +#include +#include +#include +#include // for pair +#include +#include + +#include +#include + +#include +#include + +#include "rips-zigzag-dionysus.h" + +using ST = Gudhi::Simplex_tree; +using coltype = Gudhi::zigzag_persistence::Zigzag_persistence_collist; +using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; +// using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; +using Vertex_handle = ST::Vertex_handle; +using Filtration_value = ST::Filtration_value; +using interval_filtration = ZP::interval_filtration; + +std::vector< std::pair > print_indices(ZP& zp, unsigned int numberOfSimplices){ + std::set essentials; + std::vector< std::pair > res; + + for (unsigned int i = 0; i < numberOfSimplices; ++i){ + essentials.insert(essentials.end(), i); + } + + for (auto& bar : zp.index_persistence_diagram()){ + res.emplace_back(bar.birth(), bar.death()); + essentials.erase(bar.birth()); + essentials.erase(bar.death()); + } + + for (unsigned int v : essentials){ + res.emplace_back(v, numberOfSimplices); + } + + return res; +} + +std::vector< std::pair > compute_with_gudhi( + const std::vector >& simplices, + const std::vector& dirs) +{ + ZP zp(simplices.size()); + + // std::cout << "====================== Gudhi =====================\n"; + + std::vector filValues(simplices.size(), 1.0); + + auto start = simplices.begin(); + auto filIt = filValues.begin(); + unsigned int i = 0; + + while (start != simplices.end()){ + unsigned int c = 1; + auto end = start + 1; + ++i; + while (end != simplices.end() && dirs[i - 1] == dirs[i]) { + ++end; + ++i; + ++c; + } + + if (dirs[i - 1]){ + zp.insert_simplices_contiguously( + start, end, filIt); + } else { + zp.remove_simplices_contiguously( + start, end, filIt); + } + + start = end; + filIt += c; + } + + // std::cout << "==================================================\n"; + + return print_indices(zp, i); +} + +static void Compute_zigzag_with_gudhi(benchmark::State& state) { + unsigned int numberOfPoints = state.range(0);; + int seed = numberOfPoints; + std::vector > simplices; + std::vector dirs; + + build_rips_zigzag_filtration(simplices, dirs, numberOfPoints, seed); + + for (auto _ : state){ + compute_with_gudhi(simplices, dirs); + } +} +BENCHMARK(Compute_zigzag_with_gudhi)->RangeMultiplier(2)->Range(100, 1000)->Unit(benchmark::kMillisecond)->MinWarmUpTime(1); + +BENCHMARK_MAIN(); + diff --git a/src/Zigzag_persistence/benchmark/Zigzag_other_benchmark.cpp b/src/Zigzag_persistence/benchmark/Zigzag_other_benchmark.cpp new file mode 100644 index 0000000000..6e51854338 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/Zigzag_other_benchmark.cpp @@ -0,0 +1,144 @@ +/* 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): Hannah Schreiber + * + * Copyright (C) 2022 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#include +#include +#include +#include +#include // for pair +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "rips-zigzag-dionysus.h" + +using DField = dionysus::Z2Field; +using Simplex = dionysus::Simplex<>; +using DFiltration = dionysus::Filtration; +using DZZ = dionysus::ZigzagPersistence; +using DIndex = typename DZZ::Index; +using DChain = dionysus::ChainEntry; +using DIChain = dionysus::ChainEntry; +using Vertex_handle = int; + +std::vector< std::pair > compute_with_dionysus( + const std::vector >& simplices, + const std::vector& dirs) +{ + DField k; + std::unordered_map indices; + DZZ persistence(k); + std::vector< std::pair > res; + + // std::cout << "==================== Dionysus ====================\n"; + + std::set essentials; + + unsigned int op = 0; + unsigned int idx = 0; + + for (const std::vector& simplex : simplices){ + Simplex c(simplex); + DIndex pair; + if (dirs[op]) { + indices.try_emplace(c, idx++); + pair = persistence.add(c.boundary(persistence.field()) | + boost::adaptors::transformed([&indices](const DChain& e) { + return DIChain(e.element(), indices.find(e.index())->second); + })); + } else { + auto idxIt = indices.find(c); + pair = persistence.remove(idxIt->second); + indices.erase(idxIt); + } + + if (pair != DZZ::unpaired()) { + res.emplace_back(pair, op); + essentials.erase(pair); + } else { + essentials.insert(essentials.end(), op); + } + op++; + } + + for (unsigned int v : essentials){ + res.emplace_back(v, op); + } + + // std::cout << "==================================================\n"; + + return res; +} + +std::vector< std::tuple > compute_with_fzz( + const std::vector >& simplices, + const std::vector& dirs) +{ + std::vector< std::tuple > persistence; + FZZ::FastZigzag fzz; + + // std::cout << "======================= FZZ ======================\n"; + + fzz.compute(simplices, dirs, &persistence); + + std::sort(persistence.begin(), persistence.end(), + [](const std::tuple& p1, const std::tuple& p2){ + if (std::get<1>(p1) == std::get<1>(p2)){ + return std::get<0>(p1) < std::get<0>(p2); + } + + return std::get<1>(p1) < std::get<1>(p2); + }); + + // std::cout << "==================================================\n"; + + return persistence; +} + +static void Compute_zigzag_with_dionysus(benchmark::State& state) { + unsigned int numberOfPoints = state.range(0);; + int seed = numberOfPoints; + std::vector > simplices; + std::vector dirs; + + build_rips_zigzag_filtration(simplices, dirs, numberOfPoints, seed); + + for (auto _ : state){ + compute_with_dionysus(simplices, dirs); + } +} +BENCHMARK(Compute_zigzag_with_dionysus)->RangeMultiplier(2)->Range(100, 1000)->Unit(benchmark::kMillisecond)->MinWarmUpTime(1); + +static void Compute_zigzag_with_fzz(benchmark::State& state) { + unsigned int numberOfPoints = state.range(0);; + int seed = numberOfPoints; + std::vector > simplices; + std::vector dirs; + + build_rips_zigzag_filtration(simplices, dirs, numberOfPoints, seed); + + for (auto _ : state){ + compute_with_fzz(simplices, dirs); + } +} +BENCHMARK(Compute_zigzag_with_fzz)->RangeMultiplier(2)->Range(100, 1000)->Unit(benchmark::kMillisecond)->MinWarmUpTime(1); + +BENCHMARK_MAIN(); + diff --git a/src/Zigzag_persistence/benchmark/rips-zigzag-dionysus.h b/src/Zigzag_persistence/benchmark/rips-zigzag-dionysus.h new file mode 100644 index 0000000000..5cd2c585c4 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/rips-zigzag-dionysus.h @@ -0,0 +1,211 @@ +#include +#include +#include +#include + +#include +namespace ba = boost::adaptors; + +#include +#include +#include +namespace d = dionysus; + +#include + +typedef std::vector Point; +typedef std::vector PointContainer; + +typedef d::PairwiseDistances> PairDistances; +typedef PairDistances::DistanceType DistanceType; +typedef PairDistances::IndexType Vertex; + +typedef d::Rips Generator; +typedef Generator::Simplex Simplex; +typedef std::set SimplexSet; + +typedef std::vector VertexVector; +typedef std::vector EpsilonVector; +typedef std::tuple Edge; +typedef std::vector EdgeVector; + +inline PointContainer compute_points(unsigned int numberOfPoints, int seed = -1) +{ + PointContainer finalPoints; + std::set points; + std::random_device dev; + std::mt19937 rng(dev()); + if (seed > -1) rng.seed(seed); + std::uniform_real_distribution dist(0,10); + + for (unsigned int i = 0; i < numberOfPoints; ++i){ + auto res = points.insert({dist(rng), dist(rng)}); + while(!res.second){ + res = points.insert({dist(rng), dist(rng)}); + } + finalPoints.push_back(*res.first); + } + + return finalPoints; +} + +inline void compute_vertices_and_epsilons(const PairDistances& distances, + VertexVector& vertices, + EpsilonVector& epsilons) +{ + DistanceType inf = std::numeric_limits::infinity(); + EpsilonVector dist(distances.size(), inf); + + vertices.push_back(distances.begin()); + // epsilons.push_back(inf); + while (vertices.size() < distances.size()) { + for (Vertex v = distances.begin(); v != distances.end(); ++v) + dist[v] = std::min(dist[v], distances(v, vertices.back())); + auto max = std::max_element(dist.begin(), dist.end()); + vertices.push_back(max - dist.begin()); + epsilons.push_back(*max); + } + epsilons.push_back(0); +} + +inline void compute_edges(const PairDistances& distances, + const VertexVector& vertices, + const EpsilonVector& epsilons, + const DistanceType& multiplier, + EdgeVector& edges) +{ + for (unsigned i = 0; i != vertices.size(); ++i) + for (unsigned j = i + 1; j != vertices.size(); ++j) { + Vertex u = vertices[i]; + Vertex v = vertices[j]; + if (distances(u, v) <= multiplier * epsilons[j - 1]) edges.emplace_back(u, v); + } + std::sort(edges.begin(), edges.end(), + [&distances](const Edge& e1, const Edge& e2) { + return distances(std::get<0>(e1), std::get<1>(e1)) < + distances(std::get<0>(e2), std::get<1>(e2)); + }); +} + +inline void compute_positive_cofaces( + const PairDistances& distances, + const VertexVector& vertices, + const EdgeVector& edges, + const EpsilonVector& epsilons, + const DistanceType& multiplier, + Generator& rips, + short unsigned& skeleton, + unsigned& ce, + unsigned& i, + SimplexSet& cofaces) +{ + cofaces.clear(); + + // Add anything else that needs to be inserted into the complex + while (ce < edges.size()) { + Vertex u, v; + std::tie(u, v) = edges[ce]; + if (distances(u, v) <= multiplier * epsilons[i - 1]) + ++ce; + else + break; + // std::cout << "Adding cofaces of " << u << ' ' << v << std::endl; + rips.edge_cofaces( + u, v, + skeleton, + multiplier * epsilons[i - 1], + [&cofaces](Simplex&& s) { cofaces.insert(s); }, + vertices.begin(), + vertices.begin() + i + 1); + } +} + +inline void compute_negative_cofaces( + const VertexVector& vertices, + const EpsilonVector& epsilons, + const DistanceType& multiplier, + Generator& rips, + short unsigned& skeleton, + unsigned& i, + SimplexSet& cofaces) +{ + cofaces.clear(); + rips.vertex_cofaces( + vertices[i], + skeleton, + multiplier * epsilons[i - 1], + [&cofaces](Simplex&& s) { cofaces.insert(s); }, + vertices.begin(), + vertices.begin() + i + 1); + // std::cout << "Total cofaces: " << cofaces.size() << std::endl; +} + +inline unsigned int build_rips_zigzag_filtration(std::vector > &simpls, + std::vector& dirs, + unsigned int numberOfPoints, + int seed = -1, + short unsigned skeleton = 2, + DistanceType multiplier = 6) +{ + // std::cout << "Building filtration" << std::endl; + unsigned int numberOfSimplices = 0; + + PointContainer points = compute_points(numberOfPoints, seed); + + // Construct distances and Rips generator + PairDistances distances(points); + Generator rips(distances); + + // Order vertices and epsilons (in maxmin fashion) + VertexVector vertices; + EpsilonVector epsilons; + EdgeVector edges; + + compute_vertices_and_epsilons(distances, vertices, epsilons); + + // Generate and sort all the edges + compute_edges(distances, vertices, epsilons, multiplier, edges); + + // Insert vertices + for (auto v : vertices) { + // Add a vertex + simpls.push_back({static_cast(v)}); + dirs.push_back(true); + ++numberOfSimplices; + } + + // Process vertices + // dlog::progress progress(vertices.size()); + unsigned ce = 0; // index of the current one past last edge in the complex + SimplexSet cofaces; // record the cofaces of all the simplices that need to be removed and reinserted + + for (unsigned stage = 0; stage != vertices.size() - 1; ++stage) { + unsigned i = vertices.size() - 1 - stage; + + /* Increase epsilon */ + compute_positive_cofaces(distances, vertices, edges, epsilons, multiplier, rips, skeleton, ce, i, cofaces); + + for (auto& s : cofaces) { + // std::cout << "Inserting: " << s << std::endl; + simpls.emplace_back(s.begin(), s.end()); + dirs.push_back(true); + ++numberOfSimplices; + } + + /* Remove the vertex */ + // std::cout << "Removing vertex: " << vertices[i] << std::endl; + compute_negative_cofaces(vertices, epsilons, multiplier, rips, skeleton, i, cofaces); + + for (auto& s : cofaces | ba::reversed) { + // std::cout << "Removing: " << s << std::endl; + simpls.emplace_back(s.begin(), s.end()); + dirs.push_back(false); + } + + // ++progress; + } + + return numberOfSimplices; + // std::cout << "Finished" << std::endl; +} diff --git a/src/Zigzag_persistence/example/CMakeLists.txt b/src/Zigzag_persistence/example/CMakeLists.txt new file mode 100644 index 0000000000..1a981ce998 --- /dev/null +++ b/src/Zigzag_persistence/example/CMakeLists.txt @@ -0,0 +1,29 @@ +project(Zigzag_persistence_examples) + +add_executable ( Zigzag_persistence_example_simple_zigzag_filtration example_simple_zigzag_filtration.cpp ) +if(TARGET TBB::tbb) + target_link_libraries(Zigzag_persistence_example_simple_zigzag_filtration TBB::tbb) +endif() +add_test(NAME Zigzag_persistence_example_simple_zigzag_filtration COMMAND $) + +add_executable ( comp comparison_for_tests.cpp ./ext_zz/fzz/fzz.cpp ) +target_include_directories(comp PUBLIC ./ext_zz) +target_compile_options(comp PUBLIC "-fopenmp") +target_link_options(comp PUBLIC "-fopenmp") + +# add_executable ( Zigzag_persistence_example_rips_zigzag_filtration example_rips_zigzag_filtration.cpp ) +# target_include_directories(Zigzag_persistence_example_rips_zigzag_filtration PUBLIC ./ext_zz) +# target_compile_options(Zigzag_persistence_example_rips_zigzag_filtration PUBLIC "-fopenmp") +# target_link_options(Zigzag_persistence_example_rips_zigzag_filtration PUBLIC "-fopenmp") + +add_executable ( rips example_rips_zigzag_filtration.cpp ) +target_include_directories(rips PUBLIC ./ext_zz) +target_compile_options(rips PUBLIC "-fopenmp") +target_link_options(rips PUBLIC "-fopenmp") + +# add_executable ( rips_old example_rips_zigzag_filtration.cpp ) +# target_include_directories(rips_old PUBLIC ./ext_zz) +# target_compile_options(rips_old PUBLIC "-fopenmp") +# target_link_options(rips_old PUBLIC "-fopenmp") + + diff --git a/src/Zigzag_persistence/example/comparison_for_tests.cpp b/src/Zigzag_persistence/example/comparison_for_tests.cpp new file mode 100644 index 0000000000..2beded4910 --- /dev/null +++ b/src/Zigzag_persistence/example/comparison_for_tests.cpp @@ -0,0 +1,440 @@ +/* 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): Hannah Schreiber + * + * Copyright (C) 2014 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#include +#include +#include +#include +#include // for pair +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rips-zigzag-dionysus.h" + +using ST = Gudhi::Simplex_tree; +using Gudhi::persistence_matrix::Zigzag_options; +using CT = Gudhi::persistence_matrix::Column_types; +using ZP = Gudhi::zigzag_persistence::Zigzag_persistence >; +using Vertex_handle = ST::Vertex_handle; +using Filtration_value = ST::Filtration_value; +using interval_filtration = ZP::interval_filtration; + +using DField = dionysus::Z2Field; +using Simplex = dionysus::Simplex<>; +using DFiltration = dionysus::Filtration; +using DZZ = dionysus::ZigzagPersistence; +using DIndex = typename DZZ::Index; +using DChain = dionysus::ChainEntry; +using DIChain = dionysus::ChainEntry; + +void print_complex(ZP& zp){ + std::clog << std::endl << "Current complex:" << std::endl; + zp.print_current_complex(); +} + +// void print_barcode(ZP& zp){ +// std::clog << std::endl << "Current barcode:" << std::endl; +// for (auto& bar : zp.persistence_diagram()){ +// std::clog << std::floor(bar.birth()) << " - "; +// if (bar.death() == std::numeric_limits::infinity()){ +// std::clog << "inf"; +// } else { +// std::clog << std::floor(bar.death()); +// } +// std::clog << " (" << bar.dim() << ")\n"; +// } +// } + +std::vector< std::pair > print_indices(ZP& zp, unsigned int numberOfSimplices){ + std::set essentials; + std::vector< std::pair > res; + + for (unsigned int i = 0; i < numberOfSimplices; ++i){ + essentials.insert(essentials.end(), i); + } + + for (auto& bar : zp.index_persistence_diagram()){ + // std::clog << bar.birth() << " - "; + // std::clog << bar.death(); + // std::clog << " (" << bar.dim() << ")\n"; + res.emplace_back(bar.birth(), bar.death()); + essentials.erase(bar.birth()); + essentials.erase(bar.death()); + } + + for (unsigned int v : essentials){ + // std::clog << v << " - "; + // std::clog << "inf\n"; + res.emplace_back(v, numberOfSimplices); + } + + return res; +} + +// std::vector > get_simplices() +// { +// return { +// {0}, +// {1}, +// {2}, +// {0,1}, +// {0,2}, +// {3}, +// {1,2}, +// {4}, +// {3,4}, +// {5}, +// {0,1,2}, +// {4,5}, +// {3,5}, +// {3,4,5}, +// {0,1,2}, //r +// {3,4,5}, //r +// {1,4}, +// {0,1,2}, +// {2,4}, +// {3,4,5}, +// {0,4}, +// {0,2,4}, +// {1,2,4}, +// {0,1,4}, +// {3,4,5}, //r +// {3,4}, //r +// {3,5}, //r +// {0,1,2,4}}; +// } + +// std::vector get_filtration_values() +// { +// return { +// 0, 0, 0, +// 1, 1, 1, +// 2, 2, 2, +// 3, 3, 3, 3, +// 4, +// 5, +// 6, 6, 6, +// 7, 7, 7, 7, 7, 7, +// 8, +// 9, 9, 9 +// }; +// } + +// std::vector get_directions() +// { +// return { +// true, +// true, +// true, +// true, +// true, +// true, +// true, +// true, +// true, +// true, +// true, +// true, +// true, +// true, +// false, +// false, +// true, +// true, +// true, +// true, +// true, +// true, +// true, +// true, +// false, +// false, +// false, +// true}; +// } + +std::vector< std::pair > compute_with_gudhi( + const std::vector >& simplices, + const std::vector& dirs) +{ + ZP zp(simplices.size()); + + std::cout << "====================== Gudhi =====================\n"; + + // std::vector filValues = get_filtration_values(); + std::vector filValues(simplices.size(), 1.0); + + auto start = simplices.begin(); + auto filIt = filValues.begin(); + unsigned int i = 0; + + while (start != simplices.end()){ + unsigned int c = 1; + auto end = start + 1; + ++i; + while (end != simplices.end() && dirs[i - 1] == dirs[i]) { + ++end; + ++i; + ++c; + } + + if (dirs[i - 1]){ + zp.insert_simplices_contiguously( + start, end, filIt); + } else { + zp.remove_simplices_contiguously( + start, end, filIt); + } + + start = end; + filIt += c; + // print_complex(zp); + } + + std::cout << "==================================================\n"; + + // print_complex(zp); + // print_barcode(zp); + return print_indices(zp, i); +} + +std::vector< std::pair > compute_with_dionysus( + const std::vector >& simplices, + const std::vector& dirs) +{ + DField k; + std::unordered_map indices; + DZZ persistence(k); + std::vector< std::pair > res; + + std::cout << "==================== Dionysus ====================\n"; + + std::set essentials; + + unsigned int op = 0; + unsigned int idx = 0; + + for (const std::vector& simplex : simplices){ + Simplex c(simplex); + DIndex pair; + if (dirs[op]) { + indices.try_emplace(c, idx++); + // int dim = boost::distance(c.boundary(persistence.field())); + // dim = dim == 0 ? 0 : dim -1; + // fmt::print("[{}] Adding: {} : {}\n", op, c, dim); + pair = persistence.add(c.boundary(persistence.field()) | + boost::adaptors::transformed([&indices](const DChain& e) { + return DIChain(e.element(), indices.find(e.index())->second); + })); + } else { + // fmt::print("[{}] Removing: {} : {}\n", op, c, boost::distance(c.boundary(persistence.field())) - 1); + auto idxIt = indices.find(c); + pair = persistence.remove(idxIt->second); + indices.erase(idxIt); + } + + if (pair != DZZ::unpaired()) { + // fmt::print("{} - {}\n", pair, op); + res.emplace_back(pair, op); + essentials.erase(pair); + } else { + essentials.insert(essentials.end(), op); + } + op++; + } + + for (unsigned int v : essentials){ + // fmt::print("{} - inf\n", v); + res.emplace_back(v, op); + } + + std::cout << "==================================================\n"; + + return res; +} + +std::vector< std::tuple > compute_with_fzz( + const std::vector >& simplices, + const std::vector& dirs) +{ + std::vector< std::tuple > persistence; + // std::vector< std::pair > res; + FZZ::FastZigzag fzz; + + std::cout << "======================= FZZ ======================\n"; + + fzz.compute(simplices, dirs, &persistence); + + std::sort(persistence.begin(), persistence.end(), + [](const std::tuple& p1, const std::tuple& p2){ + if (std::get<1>(p1) == std::get<1>(p2)){ + return std::get<0>(p1) < std::get<0>(p2); + } + + return std::get<1>(p1) < std::get<1>(p2); + }); + + // for (auto& t : persistence) + // res.emplace_back(std::get<0>(t), std::get<1>(t)); + + // for (const auto& e : persistence) { + // std::cout << (std::get<0>(e) - 1) << " - "; + // if (static_cast(std::get<1>(e)) == simplices.size()) std::cout << "inf"; + // else std::cout << std::get<1>(e); + // std::cout << " (" << std::get<2>(e) << ")" << std::endl; + // } + + std::cout << "==================================================\n"; + + return persistence; +} + +bool are_equal(const std::vector >& gudhiRes, + const std::vector >& dioRes) +{ + if (gudhiRes.size() != dioRes.size()) return false; + + for (unsigned int i = 0; i < gudhiRes.size(); ++i){ + if (gudhiRes[i].first != dioRes[i].first || gudhiRes[i].second != dioRes[i].second) + return false; + } + + return true; +} + +bool are_equal(const std::vector >& gudhiRes, + const std::vector >& fzzRes) +{ + if (gudhiRes.size() != fzzRes.size()) return false; + + for (unsigned int i = 0; i < gudhiRes.size(); ++i){ + if (static_cast(gudhiRes[i].first) != std::get<0>(fzzRes[i]) - 1 || static_cast(gudhiRes[i].second) != std::get<1>(fzzRes[i])) + return false; + } + + return true; +} + +void print(const std::vector >& res, unsigned int infValue){ + for (const auto& p : res) { + std::cout << p.first << " - "; + if (p.second == infValue) std::cout << "inf"; + else std::cout << p.second; + std::cout << std::endl; + } +} + +void print(const std::vector >& res, int infValue){ + for (const auto& e : res) { + std::cout << (std::get<0>(e) - 1) << " - "; + if (std::get<1>(e) == infValue) std::cout << "inf"; + else std::cout << std::get<1>(e); + std::cout << std::endl; + } +} + +void print_differences(const std::vector >& gudhiRes, + const std::vector >& dioRes, + unsigned int infValue) +{ + for (unsigned int i = 0; i < gudhiRes.size(); ++i){ + if (gudhiRes[i].first != dioRes[i].first || gudhiRes[i].second != dioRes[i].second){ + std::string dg = gudhiRes[i].second == infValue ? "inf" : std::to_string(gudhiRes[i].second); + std::string dd = dioRes[i].second == infValue ? "inf" : std::to_string(dioRes[i].second); + std::cout << "[" << i << "] " + << gudhiRes[i].first << " - " << dg + << " / " + << dioRes[i].first << " - " << dd + << "\n"; + } + } +} + +void print_differences(const std::vector >& gudhiRes, + const std::vector >& fzzRes, + int infValue) +{ + for (unsigned int i = 0; i < gudhiRes.size(); ++i){ + if (static_cast(gudhiRes[i].first) != std::get<0>(fzzRes[i]) || static_cast(gudhiRes[i].second) != std::get<1>(fzzRes[i])){ + std::string dg = static_cast(gudhiRes[i].second) == infValue ? "inf" : std::to_string(gudhiRes[i].second); + std::string dd = std::get<1>(fzzRes[i]) == infValue ? "inf" : std::to_string(std::get<1>(fzzRes[i])); + std::cout << "[" << i << "] " + << gudhiRes[i].first << " - " << dg + << " / " + << std::get<0>(fzzRes[i]) << " - " << dd + << "\n"; + } + } +} + + + +int main(int argc, char* const argv[]) { + if (argc < 2 || argc > 3) { + std::cout << "Wrong arguments.\n"; + return 0; + } + + // std::vector > simplices = get_simplices(); + // std::vector dirs = get_directions(); + std::vector > simplices; + std::vector dirs; + + unsigned int numberOfPoints = std::stoi(argv[1]); + int seed = -1; + + if (argc == 3) + seed = std::stoi(argv[2]); + + unsigned int numberOfSimplices = build_rips_zigzag_filtration(simplices, dirs, numberOfPoints, seed); + std::cout << "\n" << "numberOfSimplices: " << numberOfSimplices << "\n"; + + auto gudhiRes = compute_with_gudhi(simplices, dirs); + auto dioRes = compute_with_dionysus(simplices, dirs); + auto fzzRes = compute_with_fzz(simplices, dirs); + + std::cout << "Res sizes: " << gudhiRes.size() << ", " << dioRes.size() << ", " << fzzRes.size() << "\n"; + + bool firstRes = are_equal(gudhiRes, dioRes); + if (!firstRes){ + std::cout << "------------------------ Gudhi and Dionysus results are not equal!\n"; + // print(gudhiRes, numberOfSimplices); + // std::cout << "------------------------\n"; + // print(dioRes, numberOfSimplices); + print_differences(gudhiRes, dioRes, numberOfSimplices); + } else { + std::cout << "+++++++++++++++++++++++++ Gudhi and Dionysus results are equal.\n"; + } + + if (!are_equal(gudhiRes, fzzRes)){ + std::cout << "------------------------ Gudhi and FZZ results are not equal!\n"; + // if (firstRes) { + // print(gudhiRes, numberOfSimplices); + // std::cout << "------------------------\n"; + // } + // print(fzzRes, numberOfSimplices); + } else { + std::cout << "+++++++++++++++++++++++++ Gudhi and FZZ results are equal.\n"; + } + + return 0; +} diff --git a/src/Zigzag_persistence/example/example_rips_zigzag_filtration.cpp b/src/Zigzag_persistence/example/example_rips_zigzag_filtration.cpp new file mode 100644 index 0000000000..ed021faecc --- /dev/null +++ b/src/Zigzag_persistence/example/example_rips_zigzag_filtration.cpp @@ -0,0 +1,158 @@ +/* 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): Hannah Schreiber + * + * Copyright (C) 2014 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#include // atoi, getenv +#include // size_t +#include // printf +#include // strcmp +#include // read, write +#include +#include + +#include +#include +#include + +#include +#include // for pair +#include + +#include "rips-zigzag-dionysus.h" + +using ST = Gudhi::Simplex_tree; +using Gudhi::persistence_matrix::Zigzag_options; +using CT = Gudhi::persistence_matrix::Column_types; +using ZP = Gudhi::zigzag_persistence::Zigzag_persistence >; +// using coltype = Gudhi::zigzag_persistence::Zigzag_persistence_collist; +// using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; +using Vertex_handle = ST::Vertex_handle; +using Filtration_value = ST::Filtration_value; +using interval_filtration = ZP::interval_filtration; + +std::vector< std::pair > print_indices(ZP& zp, unsigned int numberOfSimplices){ + std::set essentials; + std::vector< std::pair > res; + + for (unsigned int i = 0; i < numberOfSimplices; ++i){ + essentials.insert(essentials.end(), i); + } + + for (auto& bar : zp.index_persistence_diagram()){ + res.emplace_back(bar.birth(), bar.death()); + essentials.erase(bar.birth()); + essentials.erase(bar.death()); + } + + for (unsigned int v : essentials){ + res.emplace_back(v, numberOfSimplices); + } + + return res; +} + +std::vector< std::pair > compute_with_gudhi( + const std::vector >& simplices, + const std::vector& dirs) +{ + ZP zp(simplices.size()); + + // std::cout << "====================== Gudhi =====================\n"; + + std::vector filValues(simplices.size(), 1.0); + + auto start = simplices.begin(); + auto filIt = filValues.begin(); + unsigned int i = 0; + + while (start != simplices.end()){ + unsigned int c = 1; + auto end = start + 1; + ++i; + while (end != simplices.end() && dirs[i - 1] == dirs[i]) { + ++end; + ++i; + ++c; + } + + if (dirs[i - 1]){ + zp.insert_simplices_contiguously( + start, end, filIt); + } else { + zp.remove_simplices_contiguously( + start, end, filIt); + } + + start = end; + filIt += c; + } + + // std::cout << "==================================================\n"; + + return print_indices(zp, i); +} + +int main(int argc, char* const argv[]) { + if (argc < 2 || argc > 3) { + std::cout << "Wrong arguments.\n"; + return 0; + } + + int perf_ctl_fd = open("/tmp/perf_ctl.fifo",O_WRONLY); + int perf_ctl_ack_fd = open("/tmp/perf_ctl_ack.fifo",O_RDONLY); + char ack[5]; + std::cout << "perf_ctl_fd: " << perf_ctl_fd << "\n"; + std::cout << "perf_ctl_ack_fd: " << perf_ctl_ack_fd << "\n"; + + unsigned int numberOfPoints = std::stoi(argv[1]); + int seed = -1; + if (argc == 3) + seed = std::stoi(argv[2]); + + std::vector > simplices; + std::vector dirs; + + unsigned int numberOfSimplices = build_rips_zigzag_filtration(simplices, dirs, numberOfPoints, seed); + std::cout << "Filtration size: " << simplices.size() << "\n"; + std::cout << "Number of simplices: " << numberOfSimplices << "\n"; + + // Start the performance counter and read the ack + if (perf_ctl_fd != -1){ + write(perf_ctl_fd, "enable\n", 8); + read(perf_ctl_ack_fd, ack, 5); + if(std::strcmp(ack, "ack\n") != 0){ + std::cout << "No acknowledgment\n"; + return 1; + } + } + + Gudhi::Clock time("Zigzag Rips"); + /* auto res = */compute_with_gudhi(simplices, dirs); + time.end(); + std::cout << time; + + // Stop the performance counter and read the ack + if (perf_ctl_fd != -1){ + write(perf_ctl_fd, "disable\n", 9); + read(perf_ctl_ack_fd, ack, 5); + if(std::strcmp(ack, "ack\n") != 0){ + std::cout << "No acknowledgment\n"; + return 1; + } + } + + // for (const auto& p : res) { + // std::cout << p.first << " - "; + // if (p.second == numberOfSimplices) std::cout << "inf"; + // else std::cout << p.second; + // std::cout << std::endl; + // } + + return 0; +} diff --git a/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp b/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp new file mode 100644 index 0000000000..4750eb5358 --- /dev/null +++ b/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp @@ -0,0 +1,118 @@ +/* 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): Hannah Schreiber + * + * Copyright (C) 2014 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#include +#include + +#include +#include // for pair +#include + +using ST = Gudhi::Simplex_tree; +using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; +using Vertex_handle = ST::Vertex_handle; +using Filtration_value = ST::Filtration_value; +using interval_filtration = ZP::interval_filtration; + +void print_complex(ZP& zp){ + std::clog << std::endl << "Current complex:" << std::endl; + zp.print_current_complex(); +} + +void print_barcode(ZP& zp){ + std::clog << std::endl << "Current barcode:" << std::endl; + for (auto& bar : zp.persistence_diagram()){ + std::clog << std::floor(bar.birth()) << " - "; + if (bar.death() == std::numeric_limits::infinity()){ + std::clog << "inf"; + } else { + std::clog << std::floor(bar.death()); + } + std::clog << " (" << bar.dim() << ")\n"; + } +} + +void print_indices(ZP& zp){ + std::clog << std::endl << "Current pairs:" << std::endl; + for (auto& bar : zp.index_persistence_diagram()){ + std::clog << bar.birth() << " - "; + std::clog << bar.death(); + std::clog << " (" << bar.dim() << ")\n"; + } +} + +int main(int argc, char* const argv[]) { + ZP zp; + + std::vector > simplices{ + {0},{1},{2}, + {0,1},{0,2},{3}, + {1,2},{4},{3,4}, + {5},{0,1,2},{4,5},{3,5}}; + std::vector fils{0,0,0,1,1,1,2,2,2,3,3,3,3}; + zp.insert_simplices_contiguously(simplices, fils); + + print_complex(zp); + print_barcode(zp); + print_indices(zp); + + std::vector simplex{3,4,5}; + zp.insert_simplex(simplex, 4); + + print_complex(zp); + print_barcode(zp); + print_indices(zp); + + simplex[0] = 0; + simplex[1] = 1; + simplex[2] = 2; + zp.remove_simplex(simplex, 5); + + print_complex(zp); + print_barcode(zp); + print_indices(zp); + + simplex[0] = 3; + simplex[1] = 4; + simplex[2] = 5; + zp.remove_simplex(simplex, 6); + + print_complex(zp); + print_barcode(zp); + print_indices(zp); + + simplices = {{1,4},{0,1,2},{2,4},{3,4,5},{0,4},{0,2,4},{1,2,4},{0,1,4}}; + fils = {6,6,7,7,7,7,7,7}; + zp.insert_simplices_contiguously(simplices, fils); + + print_complex(zp); + print_barcode(zp); + print_indices(zp); + + simplices = {{3,4,5},{3,4},{3,5}}; + fils = {8,9,9}; + zp.remove_simplices_contiguously(simplices, fils); + + print_complex(zp); + print_barcode(zp); + print_indices(zp); + + simplex[0] = 0; + simplex[1] = 1; + simplex[2] = 2; + simplex.push_back(4); + zp.insert_simplex(simplex, 9); + + print_complex(zp); + print_barcode(zp); + print_indices(zp); + + return 0; +} diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/chain.h b/src/Zigzag_persistence/example/ext_zz/dionysus/chain.h new file mode 100644 index 0000000000..00c983623a --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/chain.h @@ -0,0 +1,153 @@ +#ifndef DIONYSUS_CHAIN_H +#define DIONYSUS_CHAIN_H + +#include +#include +#include + +#include "fields/z2.h" + +namespace dionysus +{ + +template +struct FieldElement +{ + typedef typename Field::Element Element; + FieldElement(Element e_): + e(e_) {} + Element element() const { return e; } + void set_element(Element e_) { e = e_; } + Element e; +}; + +template<> +struct FieldElement +{ + typedef Z2Field::Element Element; + FieldElement(Element) {} + Element element() const { return Z2Field::id(); } + void set_element(Element) {} +}; + +template +struct ChainEntry: public FieldElement, public Extra... +{ + typedef Field_ Field; + typedef Index_ Index; + + typedef FieldElement Parent; + typedef typename Parent::Element Element; + + ChainEntry(): Parent(Element()), i(Index()) {} // need for serialization + + ChainEntry(ChainEntry&& other) = default; + ChainEntry(const ChainEntry& other) = default; + ChainEntry& operator=(ChainEntry&& other) = default; + + ChainEntry(Element e_, const Index& i_): + Parent(e_), i(i_) {} + + ChainEntry(Element e_, Index&& i_): + Parent(e_), i(std::move(i_)) {} + + const Index& index() const { return i; } + Index& index() { return i; } + + // debug + bool operator==(const ChainEntry& other) const { return i == other.i; } + + Index i; +}; + +template +struct Chain +{ + struct Visitor + { + template + void first(Iter it) const {} + + template + void second(Iter it) const {} + + template + void equal_keep(Iter it) const {} + + template + void equal_drop(Iter it) const {} + }; + + // x += a*y + template + static void addto(C1& x, typename Field::Element a, const C2& y, const Field& field, const Cmp& cmp, const Visitor_& = Visitor_()); +}; + +template +struct Chain> +{ + struct Visitor + { + template + void first(Iter it) const {} + + template + void second(Iter it) const {} + + template + void equal_keep(Iter it) const {} + + template + void equal_drop(Iter it) const {} + }; + + // x += a*y + template + static void addto(std::list& x, typename Field::Element a, const C2& y, + const Field& field, const Cmp& cmp, const Visitor_& visitor = Visitor_()); +}; + + +template +struct Chain> +{ + struct Visitor + { + template + void first(Iter it) const {} + + template + void second(Iter it) const {} + + template + void equal_keep(Iter it) const {} + + template + void equal_drop(Iter it) const {} + }; + + // x += a*y + template + static void addto(std::set& x, typename Field::Element a, const C2& y, + const Field& field, const Cmp& cmp, const Visitor_& = Visitor_()); + + template + static void addto(std::set& x, typename Field::Element a, T&& y, + const Field& field, const Cmp& cmp, const Visitor_& = Visitor_()); +}; + +} + +//namespace std +//{ +// template +// void swap(::dionysus::ChainEntry& x, ::dionysus::ChainEntry& y) +// { +// std::swap(x.e, y.e); +// std::swap(x.i, y.i); +// } +//} + +#include "chain.hpp" + +#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/chain.hpp b/src/Zigzag_persistence/example/ext_zz/dionysus/chain.hpp new file mode 100644 index 0000000000..4da9f44615 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/chain.hpp @@ -0,0 +1,188 @@ +template +template +void +dionysus::Chain>:: +addto(std::list& x, typename Field::Element a, const C2& y, const Field& field, const Cmp& cmp, const Visitor_& visitor) +{ + typedef typename Field::Element Element; + + auto cur_x = std::begin(x), + end_x = std::end(x); + auto cur_y = std::begin(y), + end_y = std::end(y); + + while (cur_x != end_x && cur_y != end_y) + { + if (cmp(cur_x->index(), cur_y->index())) + { + visitor.first(cur_x++); + } else if (cmp(cur_y->index(), cur_x->index())) + { + // multiply and add + Element ay = field.mul(a, cur_y->element()); + auto nw_x = x.insert(cur_x, *cur_y); + nw_x->set_element(ay); + ++cur_y; + visitor.second(nw_x); + } else + { + Element ay = field.mul(a, cur_y->element()); + Element r = field.add(cur_x->element(), ay); + if (field.is_zero(r)) + { + visitor.equal_drop(cur_x); + x.erase(cur_x++); + } + else + { + cur_x->set_element(r); + visitor.equal_keep(cur_x); + ++cur_x; + } + ++cur_y; + } + } + + for (auto it = cur_y; it != end_y; ++it) + { + Element ay = field.mul(a, it->element()); + x.push_back(*it); + x.back().set_element(ay); + visitor.second(--x.end()); + } +} + +template +template +void +dionysus::Chain>:: +addto(std::set& x, typename Field::Element a, const C2& y, const Field& field, const Cmp&, const Visitor_& visitor) +{ + typedef typename Field::Element Element; + + auto cur_y = std::begin(y), + end_y = std::end(y); + + while (cur_y != end_y) + { + auto cur_x = x.find(*cur_y); + if (cur_x == x.end()) + { + auto nw = x.insert(*cur_y).first; + Element ay = field.mul(a, nw->element()); + const_cast(*nw).set_element(ay); + visitor.second(nw); + } else + { + Element ay = field.mul(a, cur_y->element()); + Element r = field.add(cur_x->element(), ay); + if (field.is_zero(r)) + { + visitor.equal_drop(cur_x); + x.erase(cur_x); + } + else + { + const_cast(*cur_x).set_element(r); + visitor.equal_keep(cur_x); + } + } + ++cur_y; + } +} + +template +template +void +dionysus::Chain>:: +addto(std::set& x, typename Field::Element a, T&& y, const Field& field, const Cmp&, const Visitor_& visitor) +{ + typedef typename Field::Element Element; + + auto cur_x = x.find(y); + if (cur_x == x.end()) + { + auto nw = x.insert(std::move(y)).first; + Element ay = field.mul(a, nw->element()); + const_cast(*nw).set_element(ay); + visitor.second(nw); + } else + { + Element ay = field.mul(a, y.element()); + Element r = field.add(cur_x->element(), ay); + if (field.is_zero(r)) + { + visitor.equal_drop(cur_x); + x.erase(cur_x); + } + else + { + const_cast(*cur_x).set_element(r); + visitor.equal_keep(cur_x); + } + } +} + +template +template +void +dionysus::Chain:: +addto(C1& x, typename Field::Element a, const C2& y, const Field& field, const Cmp& cmp, const Visitor_& visitor) +{ + typedef typename Field::Element Element; + + C1 res; + + auto cur_x = std::begin(x), + end_x = std::end(x); + auto cur_y = std::begin(y), + end_y = std::end(y); + + while (cur_x != end_x && cur_y != end_y) + { + if (cmp(*cur_x, *cur_y)) + { + res.emplace_back(std::move(*cur_x)); + visitor.first(--res.end()); + ++cur_x; + } else if (cmp(*cur_y, *cur_x)) + { + // multiply and add + Element ay = field.mul(a, cur_y->element()); + res.emplace_back(ay, cur_y->index()); + visitor.second(--res.end()); + ++cur_y; + } else + { + Element ay = field.mul(a, cur_y->element()); + Element r = field.add(cur_x->element(), ay); + if (field.is_zero(r)) + visitor.equal_drop(cur_x); + else + { + res.emplace_back(std::move(*cur_x)); + res.back().set_element(r); + visitor.equal_keep(--res.end()); + } + ++cur_x; + ++cur_y; + } + } + + while (cur_y != end_y) + { + Element ay = field.mul(a, cur_y->element()); + res.emplace_back(ay, cur_y->index()); + visitor.second(--res.end()); + ++cur_y; + } + + while (cur_x != end_x) + { + res.emplace_back(std::move(*cur_x)); + visitor.first(--res.end()); + ++cur_x; + } + + x.swap(res); +} diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/clearing-reduction.h b/src/Zigzag_persistence/example/ext_zz/dionysus/clearing-reduction.h new file mode 100644 index 0000000000..8651e9a69a --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/clearing-reduction.h @@ -0,0 +1,45 @@ +#ifndef DIONYSUS_CLEARING_REDUCTION_H +#define DIONYSUS_CLEARING_REDUCTION_H + +namespace dionysus +{ + +// Mid-level interface +template +class ClearingReduction +{ + public: + using Persistence = Persistence_; + using Field = typename Persistence::Field; + using Index = typename Persistence::Index; + + public: + ClearingReduction(Persistence& persistence): + persistence_(persistence) {} + + template + void operator()(const Filtration& f, const Relative& relative, const ReportPair& report_pair, const Progress& progress); + + template + void operator()(const Filtration& f, const ReportPair& report_pair); + + template + void operator()(const Filtration& f) { return (*this)(f, &no_report_pair); } + + static void no_report_pair(int, Index, Index) {} + static void no_progress() {} + + const Persistence& + persistence() const { return persistence_; } + Persistence& persistence() { return persistence_; } + + private: + Persistence& persistence_; +}; + +} + +#include "clearing-reduction.hpp" + +#endif + diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/clearing-reduction.hpp b/src/Zigzag_persistence/example/ext_zz/dionysus/clearing-reduction.hpp new file mode 100644 index 0000000000..ceac11879d --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/clearing-reduction.hpp @@ -0,0 +1,60 @@ +#include +#include + +#include +namespace ba = boost::adaptors; + +template +template +void +dionysus::ClearingReduction

:: +operator()(const Filtration& filtration, const ReportPair& report_pair) +{ + using Cell = typename Filtration::Cell; + (*this)(filtration, [](const Cell&) { return false; }, report_pair, &no_progress); +} + +template +template +void +dionysus::ClearingReduction

:: +operator()(const Filtration& filtration, const Relative& relative, const ReportPair& report_pair, const Progress& progress) +{ + persistence_.resize(filtration.size()); + + // sort indices by decreasing dimension + std::vector indices(filtration.size()); + std::iota(indices.begin(), indices.end(), 0); + std::stable_sort(indices.begin(), indices.end(), + [&filtration](size_t x, size_t y) + { return filtration[x].dimension() > filtration[y].dimension(); }); + + typedef typename Filtration::Cell Cell; + typedef ChainEntry CellChainEntry; + typedef ChainEntry ChainEntry; + + for(size_t i : indices) + { + progress(); + const auto& c = filtration[i]; + + if (relative(c)) + { + persistence_.set_skip(i); + continue; + } + + if (persistence_.pair(i) != persistence_.unpaired()) + continue; + + persistence_.set(i, c.boundary(persistence_.field()) | + ba::filtered([relative](const CellChainEntry& e) { return !relative(e.index()); }) | + ba::transformed([this,&filtration](const CellChainEntry& e) + { return ChainEntry(e.element(), filtration.index(e.index())); })); + + Index pair = persistence_.reduce(i); + if (pair != persistence_.unpaired()) + report_pair(c.dimension(), pair, i); + } +} + diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/cnpy.h b/src/Zigzag_persistence/example/ext_zz/dionysus/cnpy.h new file mode 100644 index 0000000000..b11013b9d7 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/cnpy.h @@ -0,0 +1,241 @@ +//Copyright (C) 2011 Carl Rogers +//Released under MIT License +//license available in LICENSE file, or at http://www.opensource.org/licenses/mit-license.php + +#ifndef LIBCNPY_H_ +#define LIBCNPY_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cnpy { + + struct NpyArray { + char* data; + std::vector shape; + unsigned int word_size; + bool fortran_order; + void destruct() {delete[] data;} + }; + + struct npz_t : public std::map + { + void destruct() + { + npz_t::iterator it = this->begin(); + for(; it != this->end(); ++it) (*it).second.destruct(); + } + }; + + char BigEndianTest(); + char map_type(const std::type_info& t); + template std::vector create_npy_header(const T* data, const unsigned int* shape, const unsigned int ndims); + void parse_npy_header(FILE* fp,unsigned int& word_size, unsigned int*& shape, unsigned int& ndims, bool& fortran_order); + void parse_zip_footer(FILE* fp, unsigned short& nrecs, unsigned int& global_header_size, unsigned int& global_header_offset); + npz_t npz_load(std::string fname); + NpyArray npz_load(std::string fname, std::string varname); + NpyArray npy_load(std::string fname); + + template std::vector& operator+=(std::vector& lhs, const T rhs) { + //write in little endian + for(char byte = 0; byte < sizeof(T); byte++) { + char val = *((char*)&rhs+byte); + lhs.push_back(val); + } + return lhs; + } + + template<> std::vector& operator+=(std::vector& lhs, const std::string rhs); + template<> std::vector& operator+=(std::vector& lhs, const char* rhs); + + + template std::string tostring(T i, int pad = 0, char padval = ' ') { + std::stringstream s; + s << i; + return s.str(); + } + + template void npy_save(std::string fname, const T* data, const unsigned int* shape, const unsigned int ndims, std::string mode = "w") { + FILE* fp = NULL; + + if(mode == "a") fp = fopen(fname.c_str(),"r+b"); + + if(fp) { + //file exists. we need to append to it. read the header, modify the array size + unsigned int word_size, tmp_dims; + unsigned int* tmp_shape = 0; + bool fortran_order; + parse_npy_header(fp,word_size,tmp_shape,tmp_dims,fortran_order); + assert(!fortran_order); + + if(word_size != sizeof(T)) { + std::cout<<"libnpy error: "< header = create_npy_header(data,tmp_shape,ndims); + fwrite(&header[0],sizeof(char),header.size(),fp); + fseek(fp,0,SEEK_END); + + delete[] tmp_shape; + } + else { + fp = fopen(fname.c_str(),"wb"); + std::vector header = create_npy_header(data,shape,ndims); + fwrite(&header[0],sizeof(char),header.size(),fp); + } + + unsigned int nels = 1; + for(int i = 0;i < ndims;i++) nels *= shape[i]; + + fwrite(data,sizeof(T),nels,fp); + fclose(fp); + } + + template void npz_save(std::string zipname, std::string fname, const T* data, const unsigned int* shape, const unsigned int ndims, std::string mode = "w") + { + //first, append a .npy to the fname + fname += ".npy"; + + //now, on with the show + FILE* fp = NULL; + unsigned short nrecs = 0; + unsigned int global_header_offset = 0; + std::vector global_header; + + if(mode == "a") fp = fopen(zipname.c_str(),"r+b"); + + if(fp) { + //zip file exists. we need to add a new npy file to it. + //first read the footer. this gives us the offset and size of the global header + //then read and store the global header. + //below, we will write the the new data at the start of the global header then append the global header and footer below it + unsigned int global_header_size; + parse_zip_footer(fp,nrecs,global_header_size,global_header_offset); + fseek(fp,global_header_offset,SEEK_SET); + global_header.resize(global_header_size); + size_t res = fread(&global_header[0],sizeof(char),global_header_size,fp); + if(res != global_header_size){ + throw std::runtime_error("npz_save: header read error while adding to existing zip"); + } + fseek(fp,global_header_offset,SEEK_SET); + } + else { + fp = fopen(zipname.c_str(),"wb"); + } + + std::vector npy_header = create_npy_header(data,shape,ndims); + + unsigned long nels = 1; + for (int m=0; m local_header; + local_header += "PK"; //first part of sig + local_header += (unsigned short) 0x0403; //second part of sig + local_header += (unsigned short) 20; //min version to extract + local_header += (unsigned short) 0; //general purpose bit flag + local_header += (unsigned short) 0; //compression method + local_header += (unsigned short) 0; //file last mod time + local_header += (unsigned short) 0; //file last mod date + local_header += (unsigned int) crc; //crc + local_header += (unsigned int) nbytes; //compressed size + local_header += (unsigned int) nbytes; //uncompressed size + local_header += (unsigned short) fname.size(); //fname length + local_header += (unsigned short) 0; //extra field length + local_header += fname; + + //build global header + global_header += "PK"; //first part of sig + global_header += (unsigned short) 0x0201; //second part of sig + global_header += (unsigned short) 20; //version made by + global_header.insert(global_header.end(),local_header.begin()+4,local_header.begin()+30); + global_header += (unsigned short) 0; //file comment length + global_header += (unsigned short) 0; //disk number where file starts + global_header += (unsigned short) 0; //internal file attributes + global_header += (unsigned int) 0; //external file attributes + global_header += (unsigned int) global_header_offset; //relative offset of local file header, since it begins where the global header used to begin + global_header += fname; + + //build footer + std::vector footer; + footer += "PK"; //first part of sig + footer += (unsigned short) 0x0605; //second part of sig + footer += (unsigned short) 0; //number of this disk + footer += (unsigned short) 0; //disk where footer starts + footer += (unsigned short) (nrecs+1); //number of records on this disk + footer += (unsigned short) (nrecs+1); //total number of records + footer += (unsigned int) global_header.size(); //nbytes of global headers + footer += (unsigned int) (global_header_offset + nbytes + local_header.size()); //offset of start of global headers, since global header now starts after newly written array + footer += (unsigned short) 0; //zip file comment length + + //write everything + fwrite(&local_header[0],sizeof(char),local_header.size(),fp); + fwrite(&npy_header[0],sizeof(char),npy_header.size(),fp); + fwrite(data,sizeof(T),nels,fp); + fwrite(&global_header[0],sizeof(char),global_header.size(),fp); + fwrite(&footer[0],sizeof(char),footer.size(),fp); + fclose(fp); + } + + template std::vector create_npy_header(const T* data, const unsigned int* shape, const unsigned int ndims) { + + std::vector dict; + dict += "{'descr': '"; + dict += BigEndianTest(); + dict += map_type(typeid(T)); + dict += tostring(sizeof(T)); + dict += "', 'fortran_order': False, 'shape': ("; + dict += tostring(shape[0]); + for(int i = 1;i < ndims;i++) { + dict += ", "; + dict += tostring(shape[i]); + } + if(ndims == 1) dict += ","; + dict += "), }"; + //pad with spaces so that preamble+dict is modulo 16 bytes. preamble is 10 bytes. dict needs to end with \n + int remainder = 16 - (10 + dict.size()) % 16; + dict.insert(dict.end(),remainder,' '); + dict.back() = '\n'; + + std::vector header; + header += (char) 0x93; + header += "NUMPY"; + header += (char) 0x01; //major version of numpy format + header += (char) 0x00; //minor version of numpy format + header += (unsigned short) dict.size(); + header.insert(header.end(),dict.begin(),dict.end()); + + return header; + } + + +} + +#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/cohomology-persistence.h b/src/Zigzag_persistence/example/ext_zz/dionysus/cohomology-persistence.h new file mode 100644 index 0000000000..8d2019e89d --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/cohomology-persistence.h @@ -0,0 +1,116 @@ +#ifndef DIONYSUS_COHOMOLOGY_PERSISTENCE_H +#define DIONYSUS_COHOMOLOGY_PERSISTENCE_H + +#include +#include + +#include +namespace bi = boost::intrusive; + +#include "reduction.h" +#include "chain.h" + +namespace dionysus +{ + +template> +class CohomologyPersistence +{ + public: + typedef Field_ Field; + typedef Index_ Index; + typedef Comparison_ Comparison; + + typedef typename Field::Element FieldElement; + + typedef bi::list_base_hook> auto_unlink_hook; + struct Entry; + struct ColumnHead; + + typedef std::vector Column; + typedef bi::list> Row; + typedef std::list Columns; + typedef typename Columns::iterator ColumnsIterator; + typedef Column Chain; + + using IndexColumn = std::tuple; + + CohomologyPersistence(const Field& field, + const Comparison& cmp = Comparison()): + field_(field), cmp_(cmp) {} + + CohomologyPersistence(Field&& field, + const Comparison& cmp = Comparison()): + field_(std::move(field)), + cmp_(cmp) {} + + CohomologyPersistence(CohomologyPersistence&& other): + field_(std::move(other.field_)), + cmp_(std::move(other.cmp_)), + columns_(std::move(other.columns_)), + rows_(std::move(other.rows_)) {} + + template + Index add(const ChainRange& chain); + + template + IndexColumn add(const ChainRange& chain, bool keep_cocycle); + + // TODO: no skip support for now + bool skip(Index) const { return false; } + void add_skip() {} + void set_skip(Index, bool flag = true) {} + + const Field& field() const { return field_; } + const Columns& columns() const { return columns_; } + void reserve(size_t s) { rows_.reserve(s); } + + struct AddtoVisitor; + + static const Index unpaired() { return Reduction::unpaired; } + + private: + Field field_; + Comparison cmp_; + Columns columns_; + std::vector rows_; +}; + + +template +struct CohomologyPersistence::ColumnHead +{ + ColumnHead(Index i): index_(i) {} + + Index index() const { return index_; } + + Index index_; + Column chain; +}; + +template +struct CohomologyPersistence::Entry: + public ChainEntry +{ + typedef ChainEntry Parent; + + Entry(FieldElement e, const Index& i): // slightly dangerous + Parent(e,i) {} + + Entry(FieldElement e, const Index& i, ColumnsIterator it): + Parent(e,i), column(it) {} + + Entry(const Entry& other) = default; + Entry(Entry&& other) = default; + + void unlink() { auto_unlink_hook::unlink(); } + bool is_linked() const { return auto_unlink_hook::is_linked(); } + + ColumnsIterator column; // TODO: I really don't like this overhead +}; + +} + +#include "cohomology-persistence.hpp" + +#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/cohomology-persistence.hpp b/src/Zigzag_persistence/example/ext_zz/dionysus/cohomology-persistence.hpp new file mode 100644 index 0000000000..b2334f99e1 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/cohomology-persistence.hpp @@ -0,0 +1,61 @@ +template +template +typename dionysus::CohomologyPersistence::Index +dionysus::CohomologyPersistence:: +add(const ChainRange& chain) +{ + return std::get<0>(add(chain, false)); // return just the index +} + + +template +template +typename dionysus::CohomologyPersistence::IndexColumn +dionysus::CohomologyPersistence:: +add(const ChainRange& chain, bool keep_cocycle) +{ + auto entry_cmp = [this](const Entry& e1, const Entry& e2) { return this->cmp_(e1.index(), e2.index()); }; + std::set row_sum(entry_cmp); + for (auto it = std::begin(chain); it != std::end(chain); ++it) + for (auto& re : rows_[it->index()]) + dionysus::Chain::addto(row_sum, it->element(), Entry(re.element(), re.column->index(), re.column), field_, cmp_); + + if (row_sum.empty()) // Birth + { + columns_.emplace_back(rows_.size()); + auto before_end = columns_.end(); + --before_end; + columns_.back().chain.push_back(Entry(field_.id(), rows_.size(), before_end)); + rows_.emplace_back(); + rows_.back().push_back(columns_.back().chain.front()); + return std::make_tuple(unpaired(), Column()); + } else // Death + { + // Select front element in terms of comparison (rows are unsorted) + auto it = std::max_element(std::begin(row_sum), std::end(row_sum), entry_cmp); + + Entry first = std::move(*it); + row_sum.erase(it); + + for (auto& ce : row_sum) + { + FieldElement ay = field_.neg(field_.div(ce.element(), first.element())); + dionysus::Chain::addto(ce.column->chain, ay, first.column->chain, field_, + [this](const Entry& e1, const Entry& e2) + { return this->cmp_(e1.index(), e2.index()); }); + + for (auto& x : ce.column->chain) + { + x.column = ce.column; + rows_[x.index()].push_back(x); + } + } + Index pair = first.column->index(); + Column cocycle; + if (keep_cocycle) + cocycle = std::move(first.column->chain); + columns_.erase(first.column); + rows_.emplace_back(); // useless row; only present to make indices match + return std::make_tuple(pair, cocycle); + } +} diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/common.h b/src/Zigzag_persistence/example/ext_zz/dionysus/common.h new file mode 100644 index 0000000000..e012b10539 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/common.h @@ -0,0 +1,25 @@ +#ifndef DIONYSUS_EXAMPLES_COMMON_H +#define DIONYSUS_EXAMPLES_COMMON_H + +#include +#include + +template +void read_points(const std::string& infilename, PointContainer& points) +{ + typedef typename PointContainer::value_type Point; + + std::ifstream in(infilename.c_str()); + std::string line; + while(std::getline(in, line)) + { + if (line[0] == '#') continue; // comment line in the file + std::stringstream linestream(line); + double x; + points.push_back(Point()); + while (linestream >> x) + points.back().push_back(x); + } +} + +#endif // DIONYSUS_EXAMPLES_COMMON_H diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/diagram.h b/src/Zigzag_persistence/example/ext_zz/dionysus/diagram.h new file mode 100644 index 0000000000..04eb29a927 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/diagram.h @@ -0,0 +1,114 @@ +#ifndef DIONYSUS_DIAGRAM_H +#define DIONYSUS_DIAGRAM_H + +#include +#include +#include + +namespace dionysus +{ + +template +class Diagram +{ + public: + using Value = Value_; + using Data = Data_; + struct Point: public std::pair + { + using Parent = std::pair; + + Point(Value b, Value d, Data dd): + Parent(b,d), data(dd) {} + + Value birth() const { return Parent::first; } + Value death() const { return Parent::second; } + + // FIXME: temporary hack + Value operator[](size_t i) const { if (i == 0) return birth(); return death(); } + + Data data; + }; + + using Points = std::vector; + using iterator = typename Points::iterator; + using const_iterator = typename Points::const_iterator; + using value_type = Point; + + public: + const_iterator begin() const { return points.begin(); } + const_iterator end() const { return points.end(); } + iterator begin() { return points.begin(); } + iterator end() { return points.end(); } + + const Point& operator[](size_t i) const { return points[i]; } + + size_t size() const { return points.size(); } + void push_back(const Point& p) { points.push_back(p); } + template + void emplace_back(Args&&... args) { points.emplace_back(std::forward(args)...); } + + private: + std::vector points; +}; + +namespace detail +{ + template + struct Diagrams + { + using Value = decltype(std::declval()(std::declval())); + using Data = decltype(std::declval()(std::declval())); + using type = std::vector>; + }; +} + +template +typename detail::Diagrams::type +init_diagrams(const ReducedMatrix& m, const Filtration& f, const GetValue& get_value, const GetData& get_data) +{ + using Result = typename detail::Diagrams::type; + + Result diagrams; + for (typename ReducedMatrix::Index i = 0; i < m.size(); ++i) + { + if (m.skip(i)) + continue; + + auto s = f[i]; + auto d = s.dimension(); + + while (d + 1 > diagrams.size()) + diagrams.emplace_back(); + + auto pair = m.pair(i); + if (pair == m.unpaired()) + { + auto birth = get_value(s); + using Value = decltype(birth); + Value death = std::numeric_limits::infinity(); + diagrams[d].emplace_back(birth, death, get_data(i)); + } else if (pair > i) // positive + { + auto birth = get_value(s); + auto death = get_value(f[pair]); + + // hack to work with coboundaries + auto pd = f[pair].dimension(); + if (pd < d) + { + d = pd; + std::swap(birth, death); + } + + if (birth != death) // skip diagonal + diagrams[d].emplace_back(birth, death, get_data(i)); + } // else negative: do nothing + } + + return diagrams; +} + +} + +#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/distances.h b/src/Zigzag_persistence/example/ext_zz/dionysus/distances.h new file mode 100644 index 0000000000..29cac601af --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/distances.h @@ -0,0 +1,93 @@ +#ifndef DIONYSUS_DISTANCES_H +#define DIONYSUS_DISTANCES_H + +#include +#include + +namespace dionysus +{ + +/** + * Class: ExplicitDistances + * Stores the pairwise distances of Distances_ instance passed at construction. + * It's a protypical Distances template argument for the Rips complex. + */ +template +class ExplicitDistances +{ + public: + typedef Distances_ Distances; + typedef size_t IndexType; + typedef typename Distances::DistanceType DistanceType; + + ExplicitDistances(IndexType size): + size_(size), + distances_(size*(size + 1)/2 + size) {} + ExplicitDistances(const Distances& distances); + + DistanceType operator()(IndexType a, IndexType b) const; + DistanceType& operator()(IndexType a, IndexType b); + + size_t size() const { return size_; } + IndexType begin() const { return 0; } + IndexType end() const { return size(); } + + private: + std::vector distances_; + size_t size_; +}; + + +/** + * Class: PairwiseDistances + * Given a Container_ of points and a Distance_, it computes distances between elements + * in the container (given as instances of Index_ defaulted to unsigned) using the Distance_ functor. + * + * Container_ is assumed to be an std::vector. That simplifies a number of things. + */ +template +class PairwiseDistances +{ + public: + typedef Container_ Container; + typedef Distance_ Distance; + typedef Index_ IndexType; + typedef typename Distance::result_type DistanceType; + + + PairwiseDistances(const Container& container, + const Distance& distance = Distance()): + container_(container), distance_(distance) {} + + DistanceType operator()(IndexType a, IndexType b) const { return distance_(container_[a], container_[b]); } + + size_t size() const { return container_.size(); } + IndexType begin() const { return 0; } + IndexType end() const { return size(); } + + private: + const Container& container_; + Distance distance_; +}; + +template +struct L2Distance +{ + typedef Point_ Point; + typedef decltype(Point()[0] + 0) result_type; + + result_type operator()(const Point& p1, const Point& p2) const + { + result_type sum = 0; + for (size_t i = 0; i < p1.size(); ++i) + sum += (p1[i] - p2[i])*(p1[i] - p2[i]); + + return sqrt(sum); + } +}; + +} + +#include "distances.hpp" + +#endif // DIONYSUS_DISTANCES_H diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/distances.hpp b/src/Zigzag_persistence/example/ext_zz/dionysus/distances.hpp new file mode 100644 index 0000000000..9b1f20aa2b --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/distances.hpp @@ -0,0 +1,30 @@ +template +dionysus::ExplicitDistances:: +ExplicitDistances(const Distances& distances): + size_(distances.size()), distances_((distances.size() * (distances.size() + 1))/2) +{ + IndexType i = 0; + for (typename Distances::IndexType a = distances.begin(); a != distances.end(); ++a) + for (typename Distances::IndexType b = a; b != distances.end(); ++b) + { + distances_[i++] = distances(a,b); + } +} + +template +typename dionysus::ExplicitDistances::DistanceType +dionysus::ExplicitDistances:: +operator()(IndexType a, IndexType b) const +{ + if (a > b) std::swap(a,b); + return distances_[a*size_ - ((a*(a-1))/2) + (b-a)]; +} + +template +typename dionysus::ExplicitDistances::DistanceType& +dionysus::ExplicitDistances:: +operator()(IndexType a, IndexType b) +{ + if (a > b) std::swap(a,b); + return distances_[a*size_ - ((a*(a-1))/2) + (b-a)]; +} diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/dlog/progress.h b/src/Zigzag_persistence/example/ext_zz/dionysus/dlog/progress.h new file mode 100644 index 0000000000..12bf86a4a2 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/dlog/progress.h @@ -0,0 +1,57 @@ +#ifndef DLOG_PROGRESS_H +#define DLOG_PROGRESS_H + +#include +#include +#include +#include + +namespace dlog +{ + +struct progress +{ + progress(size_t total): + current_(0), total_(total) { show_progress(); } + + progress& operator++() { current_++; if (current_ * 100 / total_ > (current_ - 1) * 100 / total_) show_progress(); check_done(); return *this; } + progress& operator=(size_t cur) { current_ = cur; show_progress(); check_done(); return *this; } + progress& operator()(const std::string& s) { message_ = s; show_progress(); check_done(); return *this; } + template + progress& operator()(const T& x) { std::ostringstream oss; oss << x; return (*this)(oss.str()); } + + inline void show_progress() const; + void check_done() const { if (current_ >= total_) std::cout << "\n" << std::flush; } + + private: + size_t current_, total_; + std::string message_; +}; + +} + +void +dlog::progress:: +show_progress() const +{ + int barWidth = 70; + + std::cout << "["; + int pos = barWidth * current_ / total_; + for (int i = 0; i < barWidth; ++i) + { + if (i < pos) + std::cout << "="; + else if (i == pos) + std::cout << ">"; + else + std::cout << " "; + } + std::cout << "] " << std::setw(3) << current_ * 100 / total_ << "%"; + if (!message_.empty()) + std::cout << " (" << message_ << ")"; + std::cout << "\r"; + std::cout.flush(); +} + +#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/fields/q.h b/src/Zigzag_persistence/example/ext_zz/dionysus/fields/q.h new file mode 100644 index 0000000000..8972ae2b5a --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/fields/q.h @@ -0,0 +1,63 @@ +#ifndef DIONYSUS_Q_H +#define DIONYSUS_Q_H + +#include + +// TODO: eventually need to be able to adaptively switch to arbitrary precision arithmetic + +namespace dionysus +{ + +template +class Q +{ + public: + using BaseElement = Element_; + struct Element + { + BaseElement numerator, denominator; + + bool operator==(Element o) const { return numerator == o.numerator && denominator == o.denominator; } + bool operator!=(Element o) const { return !((*this) == o); } + + friend + std::ostream& operator<<(std::ostream& out, Element e) { out << e.numerator << '/' << e.denominator; return out; } + }; + + Element id() const { return { 1,1 }; } + Element zero() const { return { 0,1 }; } + Element init(BaseElement a) const { return { a,1 }; } + + Element neg(Element a) const { return { -a.numerator, a.denominator }; } + Element add(Element a, Element b) const { Element x { a.numerator*b.denominator + b.numerator*a.denominator, a.denominator*b.denominator }; normalize(x); return x; } + + Element inv(Element a) const { return { a.denominator, a.numerator }; } + Element mul(Element a, Element b) const { Element x { a.numerator*b.numerator, a.denominator*b.denominator }; normalize(x); return x; } + Element div(Element a, Element b) const { return mul(a, inv(b)); } + + bool is_zero(Element a) const { return a.numerator == 0; } + + BaseElement numerator(const Element& x) const { return x.numerator; } + BaseElement denominator(const Element& x) const { return x.denominator; } + + static void normalize(Element& x) + { + BaseElement q = gcd(abs(x.numerator), abs(x.denominator)); + x.numerator /= q; + x.denominator /= q; + if (x.denominator < 0) + { + x.numerator = -x.numerator; + x.denominator = -x.denominator; + } + } + + static BaseElement abs(BaseElement x) { if (x < 0) return -x; return x; } + static BaseElement gcd(BaseElement a, BaseElement b) { if (b < a) return gcd(b,a); while (a != 0) { b %= a; std::swap(a,b); } return b; } + + static bool is_prime(BaseElement x) { return false; } // Ok, since is_prime is only used as a shortcut +}; + +} + +#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/fields/z2.h b/src/Zigzag_persistence/example/ext_zz/dionysus/fields/z2.h new file mode 100644 index 0000000000..6317ace6df --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/fields/z2.h @@ -0,0 +1,31 @@ +#ifndef DIONYSUS_Z2_H +#define DIONYSUS_Z2_H + +namespace dionysus +{ + +class Z2Field +{ + public: + typedef short Element; + + Z2Field() {} + + static Element id() { return 1; } + static Element zero() { return 0; } + static Element init(int a) { return (a % 2 + 2) % 2; } + + Element neg(Element a) const { return 2 - a; } + Element add(Element a, Element b) const { return (a+b) % 2; } + + Element inv(Element a) const { return a; } + Element mul(Element a, Element b) const { return a*b; } + Element div(Element a, Element b) const { return a; } + + bool is_zero(Element a) const { return a == 0; } +}; + +} + +#endif + diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/fields/zp.h b/src/Zigzag_persistence/example/ext_zz/dionysus/fields/zp.h new file mode 100644 index 0000000000..c70c61cc87 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/fields/zp.h @@ -0,0 +1,55 @@ +#ifndef DIONYSUS_ZP_H +#define DIONYSUS_ZP_H + +#include + +namespace dionysus +{ + +template +class ZpField +{ + public: + typedef Element_ Element; + + ZpField(Element p); + ZpField(const ZpField& other) = default; + ZpField(ZpField&& other) = default; + + Element id() const { return 1; } + Element zero() const { return 0; } + Element init(int a) const { return (a % p_ + p_) % p_; } + + Element neg(Element a) const { return p_ - a; } + Element add(Element a, Element b) const { return (a+b) % p_; } + + Element inv(Element a) const { while (a < 0) a += p_; return inverses_[a]; } + Element mul(Element a, Element b) const { return (a*b) % p_; } + Element div(Element a, Element b) const { return mul(a, inv(b)); } + + bool is_zero(Element a) const { return (a % p_) == 0; } + + Element prime() const { return p_; } + + private: + Element p_; + std::vector inverses_; +}; + +template +ZpField:: +ZpField(Element p): + p_(p), inverses_(p_) +{ + for (Element i = 1; i < p_; ++i) + for (Element j = 1; j < p_; ++j) + if (mul(i,j) == 1) + { + inverses_[i] = j; + break; + } +} + +} + +#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/filtration.h b/src/Zigzag_persistence/example/ext_zz/dionysus/filtration.h new file mode 100644 index 0000000000..caf871cdfb --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/filtration.h @@ -0,0 +1,124 @@ +#ifndef DIONYSUS_FILTRATION_H +#define DIONYSUS_FILTRATION_H + +#include +#include + +#include +#include +#include +#include + +namespace b = boost; +namespace bmi = boost::multi_index; + +namespace dionysus +{ + +// Filtration stores a filtered cell complex as boost::multi_index_container<...>. +// It allows for bidirectional translation between a cell and its index. +template>, + bool checked_index = false> +class Filtration +{ + public: + struct order {}; + + typedef Cell_ Cell; + typedef CellLookupIndex_ CellLookupIndex; + + typedef b::multi_index_container> + >> Container; + typedef typename Container::value_type value_type; + + typedef typename Container::template nth_index<0>::type Complex; + typedef typename Container::template nth_index<1>::type Order; + typedef typename Order::const_iterator OrderConstIterator; + typedef typename Order::iterator OrderIterator; + + + public: + Filtration() = default; + Filtration(Filtration&& other) = default; + Filtration& operator=(Filtration&& other) = default; + + Filtration(const std::initializer_list& cells): + Filtration(std::begin(cells), std::end(cells)) {} + + template + Filtration(Iterator bg, Iterator end): + cells_(bg, end) {} + + template + Filtration(const CellRange& cells): + Filtration(std::begin(cells), std::end(cells)) {} + + // Lookup + const Cell& operator[](size_t i) const { return cells_.template get()[i]; } + OrderConstIterator iterator(const Cell& s) const { return bmi::project(cells_, cells_.find(s)); } + size_t index(const Cell& s) const; + bool contains(const Cell& s) const { return cells_.find(s) != cells_.end(); } + + void push_back(const Cell& s) { cells_.template get().push_back(s); } + void push_back(Cell&& s) { cells_.template get().push_back(s); } + + void replace(size_t i, const Cell& s) { cells_.template get().replace(begin() + i, s); } + + // return index of the cell, adding it, if necessary + size_t add(const Cell& s) { size_t i = (iterator(s) - begin()); if (i == size()) emplace_back(s); return i; } + size_t add(Cell&& s) { size_t i = (iterator(s) - begin()); if (i == size()) emplace_back(std::move(s)); return i; } + + template + void emplace_back(Args&&... args) { cells_.template get().emplace_back(std::forward(args)...); } + + template> + void sort(const Cmp& cmp = Cmp()) { cells_.template get().sort(cmp); } + + void rearrange(const std::vector& indices); + + OrderConstIterator begin() const { return cells_.template get().begin(); } + OrderConstIterator end() const { return cells_.template get().end(); } + OrderIterator begin() { return cells_.template get().begin(); } + OrderIterator end() { return cells_.template get().end(); } + size_t size() const { return cells_.size(); } + void clear() { return Container().swap(cells_); } + + Cell& back() { return const_cast(cells_.template get().back()); } + const Cell& back() const { return cells_.template get().back(); } + + private: + Container cells_; +}; + +} + +template +size_t +dionysus::Filtration:: +index(const Cell& s) const +{ + auto it = iterator(s); + if (checked_index && it == end()) + { + std::ostringstream oss; + oss << "Trying to access non-existent cell: " << s; + throw std::runtime_error(oss.str()); + } + return it - begin(); +} + +template +void +dionysus::Filtration:: +rearrange(const std::vector& indices) +{ + std::vector> references; references.reserve(indices.size()); + for (size_t i : indices) + references.push_back(std::cref((*this)[i])); + cells_.template get().rearrange(references.begin()); +} + + +#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/format.h b/src/Zigzag_persistence/example/ext_zz/dionysus/format.h new file mode 100644 index 0000000000..7f7ba83318 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/format.h @@ -0,0 +1,8 @@ +#ifndef DIONYSUS_FORMAT_H +#define DIONYSUS_FORMAT_H + +#define FMT_HEADER_ONLY + +#include "format/format.h" + +#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/format/format.cc b/src/Zigzag_persistence/example/ext_zz/dionysus/format/format.cc new file mode 100644 index 0000000000..a01e272fd7 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/format/format.cc @@ -0,0 +1,1156 @@ +/* + Formatting library for C++ + + Copyright (c) 2012 - 2014, Victor Zverovich + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "format.h" + +#include + +#include +#include +#include +#include +#include + +#ifdef _WIN32 +# ifdef __MINGW32__ +# include +# endif +# include +#endif + +using fmt::internal::Arg; + +// Check if exceptions are disabled. +#if __GNUC__ && !__EXCEPTIONS +# define FMT_EXCEPTIONS 0 +#endif +#if _MSC_VER && !_HAS_EXCEPTIONS +# define FMT_EXCEPTIONS 0 +#endif +#ifndef FMT_EXCEPTIONS +# define FMT_EXCEPTIONS 1 +#endif + +#if FMT_EXCEPTIONS +# define FMT_TRY try +# define FMT_CATCH(x) catch (x) +#else +# define FMT_TRY if (true) +# define FMT_CATCH(x) if (false) +#endif + +#ifndef FMT_THROW +# if FMT_EXCEPTIONS +# define FMT_THROW(x) throw x +# define FMT_RETURN_AFTER_THROW(x) +# else +# define FMT_THROW(x) assert(false) +# define FMT_RETURN_AFTER_THROW(x) return x +# endif +#endif + +#ifdef FMT_HEADER_ONLY +# define FMT_FUNC inline +#else +# define FMT_FUNC +#endif + +#if _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4127) // conditional expression is constant +# pragma warning(disable: 4702) // unreachable code +#endif + +namespace { + +#ifndef _MSC_VER +# define FMT_SNPRINTF snprintf +#else // _MSC_VER +inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) { + va_list args; + va_start(args, format); + int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); + va_end(args); + return result; +} +# define FMT_SNPRINTF fmt_snprintf +#endif // _MSC_VER + +// Checks if a value fits in int - used to avoid warnings about comparing +// signed and unsigned integers. +template +struct IntChecker { + template + static bool fits_in_int(T value) { + unsigned max = INT_MAX; + return value <= max; + } +}; + +template <> +struct IntChecker { + template + static bool fits_in_int(T value) { + return value >= INT_MIN && value <= INT_MAX; + } +}; + +const char RESET_COLOR[] = "\x1b[0m"; + +typedef void (*FormatFunc)(fmt::Writer &, int, fmt::StringRef); + +// Portable thread-safe version of strerror. +// Sets buffer to point to a string describing the error code. +// This can be either a pointer to a string stored in buffer, +// or a pointer to some static immutable string. +// Returns one of the following values: +// 0 - success +// ERANGE - buffer is not large enough to store the error message +// other - failure +// Buffer should be at least of size 1. +int safe_strerror( + int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT { + assert(buffer != 0 && buffer_size != 0); + int result = 0; +#if ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !_GNU_SOURCE) || __ANDROID__ + // XSI-compliant version of strerror_r. + result = strerror_r(error_code, buffer, buffer_size); + if (result != 0) + result = errno; +#elif _GNU_SOURCE + // GNU-specific version of strerror_r. + char *message = strerror_r(error_code, buffer, buffer_size); + // If the buffer is full then the message is probably truncated. + if (message == buffer && strlen(buffer) == buffer_size - 1) + result = ERANGE; + buffer = message; +#elif __MINGW32__ + errno = 0; + (void)buffer_size; + buffer = strerror(error_code); + result = errno; +#elif _WIN32 + result = strerror_s(buffer, buffer_size, error_code); + // If the buffer is full then the message is probably truncated. + if (result == 0 && std::strlen(buffer) == buffer_size - 1) + result = ERANGE; +#else + result = strerror_r(error_code, buffer, buffer_size); + if (result == -1) + result = errno; // glibc versions before 2.13 return result in errno. +#endif + return result; +} + +void format_error_code(fmt::Writer &out, int error_code, + fmt::StringRef message) FMT_NOEXCEPT { + // Report error code making sure that the output fits into + // INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential + // bad_alloc. + out.clear(); + static const char SEP[] = ": "; + static const char ERR[] = "error "; + fmt::internal::IntTraits::MainType ec_value = error_code; + // Subtract 2 to account for terminating null characters in SEP and ERR. + std::size_t error_code_size = + sizeof(SEP) + sizeof(ERR) + fmt::internal::count_digits(ec_value) - 2; + if (message.size() <= fmt::internal::INLINE_BUFFER_SIZE - error_code_size) + out << message << SEP; + out << ERR << error_code; + assert(out.size() <= fmt::internal::INLINE_BUFFER_SIZE); +} + +void report_error(FormatFunc func, + int error_code, fmt::StringRef message) FMT_NOEXCEPT { + fmt::MemoryWriter full_message; + func(full_message, error_code, message); + // Use Writer::data instead of Writer::c_str to avoid potential memory + // allocation. + std::fwrite(full_message.data(), full_message.size(), 1, stderr); + std::fputc('\n', stderr); +} + +// IsZeroInt::visit(arg) returns true iff arg is a zero integer. +class IsZeroInt : public fmt::internal::ArgVisitor { + public: + template + bool visit_any_int(T value) { return value == 0; } +}; + +// Parses an unsigned integer advancing s to the end of the parsed input. +// This function assumes that the first character of s is a digit. +template +int parse_nonnegative_int(const Char *&s) { + assert('0' <= *s && *s <= '9'); + unsigned value = 0; + do { + unsigned new_value = value * 10 + (*s++ - '0'); + // Check if value wrapped around. + if (new_value < value) { + value = UINT_MAX; + break; + } + value = new_value; + } while ('0' <= *s && *s <= '9'); + if (value > INT_MAX) + FMT_THROW(fmt::FormatError("number is too big")); + return value; +} + +inline void require_numeric_argument(const Arg &arg, char spec) { + if (arg.type > Arg::LAST_NUMERIC_TYPE) { + std::string message = + fmt::format("format specifier '{}' requires numeric argument", spec); + FMT_THROW(fmt::FormatError(message)); + } +} + +template +void check_sign(const Char *&s, const Arg &arg) { + char sign = static_cast(*s); + require_numeric_argument(arg, sign); + if (arg.type == Arg::UINT || arg.type == Arg::ULONG_LONG) { + FMT_THROW(fmt::FormatError(fmt::format( + "format specifier '{}' requires signed argument", sign))); + } + ++s; +} + +// Checks if an argument is a valid printf width specifier and sets +// left alignment if it is negative. +class WidthHandler : public fmt::internal::ArgVisitor { + private: + fmt::FormatSpec &spec_; + + FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler); + + public: + explicit WidthHandler(fmt::FormatSpec &spec) : spec_(spec) {} + + unsigned visit_unhandled_arg() { + FMT_THROW(fmt::FormatError("width is not integer")); + FMT_RETURN_AFTER_THROW(0); + } + + template + unsigned visit_any_int(T value) { + typedef typename fmt::internal::IntTraits::MainType UnsignedType; + UnsignedType width = value; + if (fmt::internal::is_negative(value)) { + spec_.align_ = fmt::ALIGN_LEFT; + width = 0 - width; + } + if (width > INT_MAX) + FMT_THROW(fmt::FormatError("number is too big")); + return static_cast(width); + } +}; + +class PrecisionHandler : + public fmt::internal::ArgVisitor { + public: + unsigned visit_unhandled_arg() { + FMT_THROW(fmt::FormatError("precision is not integer")); + FMT_RETURN_AFTER_THROW(0); + } + + template + int visit_any_int(T value) { + if (!IntChecker::is_signed>::fits_in_int(value)) + FMT_THROW(fmt::FormatError("number is too big")); + return static_cast(value); + } +}; + +// Converts an integer argument to an integral type T for printf. +template +class ArgConverter : public fmt::internal::ArgVisitor, void> { + private: + fmt::internal::Arg &arg_; + wchar_t type_; + + FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter); + + public: + ArgConverter(fmt::internal::Arg &arg, wchar_t type) + : arg_(arg), type_(type) {} + + template + void visit_any_int(U value) { + bool is_signed = type_ == 'd' || type_ == 'i'; + using fmt::internal::Arg; + if (sizeof(T) <= sizeof(int)) { + // Extra casts are used to silence warnings. + if (is_signed) { + arg_.type = Arg::INT; + arg_.int_value = static_cast(static_cast(value)); + } else { + arg_.type = Arg::UINT; + arg_.uint_value = static_cast( + static_cast::Type>(value)); + } + } else { + if (is_signed) { + arg_.type = Arg::LONG_LONG; + arg_.long_long_value = + static_cast::Type>(value); + } else { + arg_.type = Arg::ULONG_LONG; + arg_.ulong_long_value = + static_cast::Type>(value); + } + } + } +}; + +// Converts an integer argument to char for printf. +class CharConverter : public fmt::internal::ArgVisitor { + private: + fmt::internal::Arg &arg_; + + FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter); + + public: + explicit CharConverter(fmt::internal::Arg &arg) : arg_(arg) {} + + template + void visit_any_int(T value) { + arg_.type = Arg::CHAR; + arg_.int_value = static_cast(value); + } +}; + +// This function template is used to prevent compile errors when handling +// incompatible string arguments, e.g. handling a wide string in a narrow +// string formatter. +template +Arg::StringValue ignore_incompatible_str(Arg::StringValue); + +template <> +inline Arg::StringValue ignore_incompatible_str( + Arg::StringValue) { return Arg::StringValue(); } + +template <> +inline Arg::StringValue ignore_incompatible_str( + Arg::StringValue s) { return s; } +} // namespace + +FMT_FUNC void fmt::SystemError::init( + int err_code, StringRef format_str, ArgList args) { + error_code_ = err_code; + MemoryWriter w; + internal::format_system_error(w, err_code, format(format_str, args)); + std::runtime_error &base = *this; + base = std::runtime_error(w.str()); +} + +template +int fmt::internal::CharTraits::format_float( + char *buffer, std::size_t size, const char *format, + unsigned width, int precision, T value) { + if (width == 0) { + return precision < 0 ? + FMT_SNPRINTF(buffer, size, format, value) : + FMT_SNPRINTF(buffer, size, format, precision, value); + } + return precision < 0 ? + FMT_SNPRINTF(buffer, size, format, width, value) : + FMT_SNPRINTF(buffer, size, format, width, precision, value); +} + +template +int fmt::internal::CharTraits::format_float( + wchar_t *buffer, std::size_t size, const wchar_t *format, + unsigned width, int precision, T value) { + if (width == 0) { + return precision < 0 ? + swprintf(buffer, size, format, value) : + swprintf(buffer, size, format, precision, value); + } + return precision < 0 ? + swprintf(buffer, size, format, width, value) : + swprintf(buffer, size, format, width, precision, value); +} + +template +const char fmt::internal::BasicData::DIGITS[] = + "0001020304050607080910111213141516171819" + "2021222324252627282930313233343536373839" + "4041424344454647484950515253545556575859" + "6061626364656667686970717273747576777879" + "8081828384858687888990919293949596979899"; + +#define FMT_POWERS_OF_10(factor) \ + factor * 10, \ + factor * 100, \ + factor * 1000, \ + factor * 10000, \ + factor * 100000, \ + factor * 1000000, \ + factor * 10000000, \ + factor * 100000000, \ + factor * 1000000000 + +template +const uint32_t fmt::internal::BasicData::POWERS_OF_10_32[] = { + 0, FMT_POWERS_OF_10(1) +}; + +template +const uint64_t fmt::internal::BasicData::POWERS_OF_10_64[] = { + 0, + FMT_POWERS_OF_10(1), + FMT_POWERS_OF_10(fmt::ULongLong(1000000000)), + // Multiply several constants instead of using a single long long constant + // to avoid warnings about C++98 not supporting long long. + fmt::ULongLong(1000000000) * fmt::ULongLong(1000000000) * 10 +}; + +FMT_FUNC void fmt::internal::report_unknown_type(char code, const char *type) { + if (std::isprint(static_cast(code))) { + FMT_THROW(fmt::FormatError( + fmt::format("unknown format code '{}' for {}", code, type))); + } + FMT_THROW(fmt::FormatError( + fmt::format("unknown format code '\\x{:02x}' for {}", + static_cast(code), type))); +} + +#ifdef _WIN32 + +FMT_FUNC fmt::internal::UTF8ToUTF16::UTF8ToUTF16(fmt::StringRef s) { + int length = MultiByteToWideChar( + CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, 0, 0); + static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16"; + if (length == 0) + FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); + buffer_.resize(length); + length = MultiByteToWideChar( + CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, &buffer_[0], length); + if (length == 0) + FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); +} + +FMT_FUNC fmt::internal::UTF16ToUTF8::UTF16ToUTF8(fmt::WStringRef s) { + if (int error_code = convert(s)) { + FMT_THROW(WindowsError(error_code, + "cannot convert string from UTF-16 to UTF-8")); + } +} + +FMT_FUNC int fmt::internal::UTF16ToUTF8::convert(fmt::WStringRef s) { + int length = WideCharToMultiByte(CP_UTF8, 0, s.c_str(), -1, 0, 0, 0, 0); + if (length == 0) + return GetLastError(); + buffer_.resize(length); + length = WideCharToMultiByte( + CP_UTF8, 0, s.c_str(), -1, &buffer_[0], length, 0, 0); + if (length == 0) + return GetLastError(); + return 0; +} + +FMT_FUNC void fmt::WindowsError::init( + int err_code, StringRef format_str, ArgList args) { + error_code_ = err_code; + MemoryWriter w; + internal::format_windows_error(w, err_code, format(format_str, args)); + std::runtime_error &base = *this; + base = std::runtime_error(w.str()); +} + +#endif + +FMT_FUNC void fmt::internal::format_system_error( + fmt::Writer &out, int error_code, + fmt::StringRef message) FMT_NOEXCEPT { + FMT_TRY { + MemoryBuffer buffer; + buffer.resize(INLINE_BUFFER_SIZE); + for (;;) { + char *system_message = &buffer[0]; + int result = safe_strerror(error_code, system_message, buffer.size()); + if (result == 0) { + out << message << ": " << system_message; + return; + } + if (result != ERANGE) + break; // Can't get error message, report error code instead. + buffer.resize(buffer.size() * 2); + } + } FMT_CATCH(...) {} + format_error_code(out, error_code, message); +} + +#ifdef _WIN32 +FMT_FUNC void fmt::internal::format_windows_error( + fmt::Writer &out, int error_code, + fmt::StringRef message) FMT_NOEXCEPT { + class String { + private: + LPWSTR str_; + + public: + String() : str_() {} + ~String() { LocalFree(str_); } + LPWSTR *ptr() { return &str_; } + LPCWSTR c_str() const { return str_; } + }; + FMT_TRY { + String system_message; + if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, + error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(system_message.ptr()), 0, 0)) { + UTF16ToUTF8 utf8_message; + if (utf8_message.convert(system_message.c_str()) == ERROR_SUCCESS) { + out << message << ": " << utf8_message; + return; + } + } + } FMT_CATCH(...) {} + format_error_code(out, error_code, message); +} +#endif + +// An argument formatter. +template +class fmt::internal::ArgFormatter : + public fmt::internal::ArgVisitor, void> { + private: + fmt::BasicFormatter &formatter_; + fmt::BasicWriter &writer_; + fmt::FormatSpec &spec_; + const Char *format_; + + FMT_DISALLOW_COPY_AND_ASSIGN(ArgFormatter); + + public: + ArgFormatter( + fmt::BasicFormatter &f,fmt::FormatSpec &s, const Char *fmt) + : formatter_(f), writer_(f.writer()), spec_(s), format_(fmt) {} + + template + void visit_any_int(T value) { writer_.write_int(value, spec_); } + + template + void visit_any_double(T value) { writer_.write_double(value, spec_); } + + void visit_char(int value) { + if (spec_.type_ && spec_.type_ != 'c') { + spec_.flags_ |= CHAR_FLAG; + writer_.write_int(value, spec_); + return; + } + if (spec_.align_ == ALIGN_NUMERIC || spec_.flags_ != 0) + FMT_THROW(FormatError("invalid format specifier for char")); + typedef typename fmt::BasicWriter::CharPtr CharPtr; + Char fill = static_cast(spec_.fill()); + if (spec_.precision_ == 0) { + std::fill_n(writer_.grow_buffer(spec_.width_), spec_.width_, fill); + return; + } + CharPtr out = CharPtr(); + if (spec_.width_ > 1) { + out = writer_.grow_buffer(spec_.width_); + if (spec_.align_ == fmt::ALIGN_RIGHT) { + std::fill_n(out, spec_.width_ - 1, fill); + out += spec_.width_ - 1; + } else if (spec_.align_ == fmt::ALIGN_CENTER) { + out = writer_.fill_padding(out, spec_.width_, 1, fill); + } else { + std::fill_n(out + 1, spec_.width_ - 1, fill); + } + } else { + out = writer_.grow_buffer(1); + } + *out = static_cast(value); + } + + void visit_string(Arg::StringValue value) { + writer_.write_str(value, spec_); + } + void visit_wstring(Arg::StringValue value) { + writer_.write_str(ignore_incompatible_str(value), spec_); + } + + void visit_pointer(const void *value) { + if (spec_.type_ && spec_.type_ != 'p') + fmt::internal::report_unknown_type(spec_.type_, "pointer"); + spec_.flags_ = fmt::HASH_FLAG; + spec_.type_ = 'x'; + writer_.write_int(reinterpret_cast(value), spec_); + } + + void visit_custom(Arg::CustomValue c) { + c.format(&formatter_, c.value, &format_); + } +}; + +template +template +void fmt::BasicWriter::write_str( + const Arg::StringValue &s, const FormatSpec &spec) { + // Check if StrChar is convertible to Char. + internal::CharTraits::convert(StrChar()); + if (spec.type_ && spec.type_ != 's') + internal::report_unknown_type(spec.type_, "string"); + const StrChar *str_value = s.value; + std::size_t str_size = s.size; + if (str_size == 0) { + if (!str_value) + FMT_THROW(FormatError("string pointer is null")); + if (*str_value) + str_size = std::char_traits::length(str_value); + } + std::size_t precision = spec.precision_; + if (spec.precision_ >= 0 && precision < str_size) + str_size = spec.precision_; + write_str(str_value, str_size, spec); +} + +template +inline Arg fmt::BasicFormatter::parse_arg_index(const Char *&s) { + const char *error = 0; + Arg arg = *s < '0' || *s > '9' ? + next_arg(error) : get_arg(parse_nonnegative_int(s), error); + if (error) { + FMT_THROW(FormatError( + *s != '}' && *s != ':' ? "invalid format string" : error)); + } + return arg; +} + +FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg( + unsigned arg_index, const char *&error) { + Arg arg = args_[arg_index]; + if (arg.type == Arg::NONE) + error = "argument index out of range"; + return arg; +} + +inline Arg fmt::internal::FormatterBase::next_arg(const char *&error) { + if (next_arg_index_ >= 0) + return do_get_arg(next_arg_index_++, error); + error = "cannot switch from manual to automatic argument indexing"; + return Arg(); +} + +inline Arg fmt::internal::FormatterBase::get_arg( + unsigned arg_index, const char *&error) { + if (next_arg_index_ <= 0) { + next_arg_index_ = -1; + return do_get_arg(arg_index, error); + } + error = "cannot switch from automatic to manual argument indexing"; + return Arg(); +} + +template +void fmt::internal::PrintfFormatter::parse_flags( + FormatSpec &spec, const Char *&s) { + for (;;) { + switch (*s++) { + case '-': + spec.align_ = ALIGN_LEFT; + break; + case '+': + spec.flags_ |= SIGN_FLAG | PLUS_FLAG; + break; + case '0': + spec.fill_ = '0'; + break; + case ' ': + spec.flags_ |= SIGN_FLAG; + break; + case '#': + spec.flags_ |= HASH_FLAG; + break; + default: + --s; + return; + } + } +} + +template +Arg fmt::internal::PrintfFormatter::get_arg( + const Char *s, unsigned arg_index) { + const char *error = 0; + Arg arg = arg_index == UINT_MAX ? + next_arg(error) : FormatterBase::get_arg(arg_index - 1, error); + if (error) + FMT_THROW(FormatError(!*s ? "invalid format string" : error)); + return arg; +} + +template +unsigned fmt::internal::PrintfFormatter::parse_header( + const Char *&s, FormatSpec &spec) { + unsigned arg_index = UINT_MAX; + Char c = *s; + if (c >= '0' && c <= '9') { + // Parse an argument index (if followed by '$') or a width possibly + // preceded with '0' flag(s). + unsigned value = parse_nonnegative_int(s); + if (*s == '$') { // value is an argument index + ++s; + arg_index = value; + } else { + if (c == '0') + spec.fill_ = '0'; + if (value != 0) { + // Nonzero value means that we parsed width and don't need to + // parse it or flags again, so return now. + spec.width_ = value; + return arg_index; + } + } + } + parse_flags(spec, s); + // Parse width. + if (*s >= '0' && *s <= '9') { + spec.width_ = parse_nonnegative_int(s); + } else if (*s == '*') { + ++s; + spec.width_ = WidthHandler(spec).visit(get_arg(s)); + } + return arg_index; +} + +template +void fmt::internal::PrintfFormatter::format( + BasicWriter &writer, BasicStringRef format_str, + const ArgList &args) { + const Char *start = format_str.c_str(); + set_args(args); + const Char *s = start; + while (*s) { + Char c = *s++; + if (c != '%') continue; + if (*s == c) { + write(writer, start, s); + start = ++s; + continue; + } + write(writer, start, s - 1); + + FormatSpec spec; + spec.align_ = ALIGN_RIGHT; + + // Parse argument index, flags and width. + unsigned arg_index = parse_header(s, spec); + + // Parse precision. + if (*s == '.') { + ++s; + if ('0' <= *s && *s <= '9') { + spec.precision_ = parse_nonnegative_int(s); + } else if (*s == '*') { + ++s; + spec.precision_ = PrecisionHandler().visit(get_arg(s)); + } + } + + Arg arg = get_arg(s, arg_index); + if (spec.flag(HASH_FLAG) && IsZeroInt().visit(arg)) + spec.flags_ &= ~HASH_FLAG; + if (spec.fill_ == '0') { + if (arg.type <= Arg::LAST_NUMERIC_TYPE) + spec.align_ = ALIGN_NUMERIC; + else + spec.fill_ = ' '; // Ignore '0' flag for non-numeric types. + } + + // Parse length and convert the argument to the required type. + switch (*s++) { + case 'h': + if (*s == 'h') + ArgConverter(arg, *++s).visit(arg); + else + ArgConverter(arg, *s).visit(arg); + break; + case 'l': + if (*s == 'l') + ArgConverter(arg, *++s).visit(arg); + else + ArgConverter(arg, *s).visit(arg); + break; + case 'j': + ArgConverter(arg, *s).visit(arg); + break; + case 'z': + ArgConverter(arg, *s).visit(arg); + break; + case 't': + ArgConverter(arg, *s).visit(arg); + break; + case 'L': + // printf produces garbage when 'L' is omitted for long double, no + // need to do the same. + break; + default: + --s; + ArgConverter(arg, *s).visit(arg); + } + + // Parse type. + if (!*s) + FMT_THROW(FormatError("invalid format string")); + spec.type_ = static_cast(*s++); + if (arg.type <= Arg::LAST_INTEGER_TYPE) { + // Normalize type. + switch (spec.type_) { + case 'i': case 'u': + spec.type_ = 'd'; + break; + case 'c': + // TODO: handle wchar_t + CharConverter(arg).visit(arg); + break; + } + } + + start = s; + + // Format argument. + switch (arg.type) { + case Arg::INT: + writer.write_int(arg.int_value, spec); + break; + case Arg::UINT: + writer.write_int(arg.uint_value, spec); + break; + case Arg::LONG_LONG: + writer.write_int(arg.long_long_value, spec); + break; + case Arg::ULONG_LONG: + writer.write_int(arg.ulong_long_value, spec); + break; + case Arg::CHAR: { + if (spec.type_ && spec.type_ != 'c') + writer.write_int(arg.int_value, spec); + typedef typename BasicWriter::CharPtr CharPtr; + CharPtr out = CharPtr(); + if (spec.width_ > 1) { + Char fill = ' '; + out = writer.grow_buffer(spec.width_); + if (spec.align_ != ALIGN_LEFT) { + std::fill_n(out, spec.width_ - 1, fill); + out += spec.width_ - 1; + } else { + std::fill_n(out + 1, spec.width_ - 1, fill); + } + } else { + out = writer.grow_buffer(1); + } + *out = static_cast(arg.int_value); + break; + } + case Arg::DOUBLE: + writer.write_double(arg.double_value, spec); + break; + case Arg::LONG_DOUBLE: + writer.write_double(arg.long_double_value, spec); + break; + case Arg::CSTRING: + arg.string.size = 0; + writer.write_str(arg.string, spec); + break; + case Arg::STRING: + writer.write_str(arg.string, spec); + break; + case Arg::WSTRING: + writer.write_str(ignore_incompatible_str(arg.wstring), spec); + break; + case Arg::POINTER: + if (spec.type_ && spec.type_ != 'p') + internal::report_unknown_type(spec.type_, "pointer"); + spec.flags_= HASH_FLAG; + spec.type_ = 'x'; + writer.write_int(reinterpret_cast(arg.pointer), spec); + break; + case Arg::CUSTOM: { + if (spec.type_) + internal::report_unknown_type(spec.type_, "object"); + const void *str_format = "s"; + arg.custom.format(&writer, arg.custom.value, &str_format); + break; + } + default: + assert(false); + break; + } + } + write(writer, start, s); +} + +template +const Char *fmt::BasicFormatter::format( + const Char *&format_str, const Arg &arg) { + const Char *s = format_str; + FormatSpec spec; + if (*s == ':') { + if (arg.type == Arg::CUSTOM) { + arg.custom.format(this, arg.custom.value, &s); + return s; + } + ++s; + // Parse fill and alignment. + if (Char c = *s) { + const Char *p = s + 1; + spec.align_ = ALIGN_DEFAULT; + do { + switch (*p) { + case '<': + spec.align_ = ALIGN_LEFT; + break; + case '>': + spec.align_ = ALIGN_RIGHT; + break; + case '=': + spec.align_ = ALIGN_NUMERIC; + break; + case '^': + spec.align_ = ALIGN_CENTER; + break; + } + if (spec.align_ != ALIGN_DEFAULT) { + if (p != s) { + if (c == '}') break; + if (c == '{') + FMT_THROW(FormatError("invalid fill character '{'")); + s += 2; + spec.fill_ = c; + } else ++s; + if (spec.align_ == ALIGN_NUMERIC) + require_numeric_argument(arg, '='); + break; + } + } while (--p >= s); + } + + // Parse sign. + switch (*s) { + case '+': + check_sign(s, arg); + spec.flags_ |= SIGN_FLAG | PLUS_FLAG; + break; + case '-': + check_sign(s, arg); + spec.flags_ |= MINUS_FLAG; + break; + case ' ': + check_sign(s, arg); + spec.flags_ |= SIGN_FLAG; + break; + } + + if (*s == '#') { + require_numeric_argument(arg, '#'); + spec.flags_ |= HASH_FLAG; + ++s; + } + + // Parse width and zero flag. + if ('0' <= *s && *s <= '9') { + if (*s == '0') { + require_numeric_argument(arg, '0'); + spec.align_ = ALIGN_NUMERIC; + spec.fill_ = '0'; + } + // Zero may be parsed again as a part of the width, but it is simpler + // and more efficient than checking if the next char is a digit. + spec.width_ = parse_nonnegative_int(s); + } + + // Parse precision. + if (*s == '.') { + ++s; + spec.precision_ = 0; + if ('0' <= *s && *s <= '9') { + spec.precision_ = parse_nonnegative_int(s); + } else if (*s == '{') { + ++s; + const Arg &precision_arg = parse_arg_index(s); + if (*s++ != '}') + FMT_THROW(FormatError("invalid format string")); + ULongLong value = 0; + switch (precision_arg.type) { + case Arg::INT: + if (precision_arg.int_value < 0) + FMT_THROW(FormatError("negative precision")); + value = precision_arg.int_value; + break; + case Arg::UINT: + value = precision_arg.uint_value; + break; + case Arg::LONG_LONG: + if (precision_arg.long_long_value < 0) + FMT_THROW(FormatError("negative precision")); + value = precision_arg.long_long_value; + break; + case Arg::ULONG_LONG: + value = precision_arg.ulong_long_value; + break; + default: + FMT_THROW(FormatError("precision is not integer")); + } + if (value > INT_MAX) + FMT_THROW(FormatError("number is too big")); + spec.precision_ = static_cast(value); + } else { + FMT_THROW(FormatError("missing precision specifier")); + } + if (arg.type < Arg::LAST_INTEGER_TYPE || arg.type == Arg::POINTER) { + FMT_THROW(FormatError( + fmt::format("precision not allowed in {} format specifier", + arg.type == Arg::POINTER ? "pointer" : "integer"))); + } + } + + // Parse type. + if (*s != '}' && *s) + spec.type_ = static_cast(*s++); + } + + if (*s++ != '}') + FMT_THROW(FormatError("missing '}' in format string")); + start_ = s; + + // Format argument. + internal::ArgFormatter(*this, spec, s - 1).visit(arg); + return s; +} + +template +void fmt::BasicFormatter::format( + BasicStringRef format_str, const ArgList &args) { + const Char *s = start_ = format_str.c_str(); + set_args(args); + while (*s) { + Char c = *s++; + if (c != '{' && c != '}') continue; + if (*s == c) { + write(writer_, start_, s); + start_ = ++s; + continue; + } + if (c == '}') + FMT_THROW(FormatError("unmatched '}' in format string")); + write(writer_, start_, s - 1); + Arg arg = parse_arg_index(s); + s = format(s, arg); + } + write(writer_, start_, s); +} + +FMT_FUNC void fmt::report_system_error( + int error_code, fmt::StringRef message) FMT_NOEXCEPT { + report_error(internal::format_system_error, error_code, message); +} + +#ifdef _WIN32 +FMT_FUNC void fmt::report_windows_error( + int error_code, fmt::StringRef message) FMT_NOEXCEPT { + report_error(internal::format_windows_error, error_code, message); +} +#endif + +FMT_FUNC void fmt::print(std::FILE *f, StringRef format_str, ArgList args) { + MemoryWriter w; + w.write(format_str, args); + std::fwrite(w.data(), 1, w.size(), f); +} + +FMT_FUNC void fmt::print(StringRef format_str, ArgList args) { + print(stdout, format_str, args); +} + +FMT_FUNC void fmt::print(std::ostream &os, StringRef format_str, ArgList args) { + MemoryWriter w; + w.write(format_str, args); + os.write(w.data(), w.size()); +} + +FMT_FUNC void fmt::print_colored(Color c, StringRef format, ArgList args) { + char escape[] = "\x1b[30m"; + escape[3] = '0' + static_cast(c); + std::fputs(escape, stdout); + print(format, args); + std::fputs(RESET_COLOR, stdout); +} + +FMT_FUNC int fmt::fprintf(std::FILE *f, StringRef format, ArgList args) { + MemoryWriter w; + printf(w, format, args); + std::size_t size = w.size(); + return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast(size); +} + +// Explicit instantiations for char. + +template const char *fmt::BasicFormatter::format( + const char *&format_str, const fmt::internal::Arg &arg); + +template void fmt::BasicFormatter::format( + BasicStringRef format, const ArgList &args); + +template void fmt::internal::PrintfFormatter::format( + BasicWriter &writer, BasicStringRef format, const ArgList &args); + +template int fmt::internal::CharTraits::format_float( + char *buffer, std::size_t size, const char *format, + unsigned width, int precision, double value); + +template int fmt::internal::CharTraits::format_float( + char *buffer, std::size_t size, const char *format, + unsigned width, int precision, long double value); + +// Explicit instantiations for wchar_t. + +template const wchar_t *fmt::BasicFormatter::format( + const wchar_t *&format_str, const fmt::internal::Arg &arg); + +template void fmt::BasicFormatter::format( + BasicStringRef format, const ArgList &args); + +template void fmt::internal::PrintfFormatter::format( + BasicWriter &writer, BasicStringRef format, + const ArgList &args); + +template int fmt::internal::CharTraits::format_float( + wchar_t *buffer, std::size_t size, const wchar_t *format, + unsigned width, int precision, double value); + +template int fmt::internal::CharTraits::format_float( + wchar_t *buffer, std::size_t size, const wchar_t *format, + unsigned width, int precision, long double value); + +#if _MSC_VER +# pragma warning(pop) +#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/format/format.h b/src/Zigzag_persistence/example/ext_zz/dionysus/format/format.h new file mode 100644 index 0000000000..03ed685383 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/format/format.h @@ -0,0 +1,2546 @@ +/* + Formatting library for C++ + + Copyright (c) 2012 - 2014, Victor Zverovich + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FMT_FORMAT_H_ +#define FMT_FORMAT_H_ + +#include + +#include +#include +#include // for std::ptrdiff_t +#include +#include +#include +#include +#include +#include + +#if _SECURE_SCL +# include +#endif + +#ifdef _MSC_VER +# include // _BitScanReverse, _BitScanReverse64 + +namespace fmt { +namespace internal { +# pragma intrinsic(_BitScanReverse) +inline uint32_t clz(uint32_t x) { + unsigned long r = 0; + _BitScanReverse(&r, x); + return 31 - r; +} +# define FMT_BUILTIN_CLZ(n) fmt::internal::clz(n) +inline uint32_t clzll(uint64_t x) { + unsigned long r = 0; +# ifdef _WIN64 +# pragma intrinsic(_BitScanReverse64) + _BitScanReverse64(&r, x); +# else + // Scan the high 32 bits. + if (_BitScanReverse(&r, static_cast(x >> 32))) + return 63 - (r + 32); + + // Scan the low 32 bits. + _BitScanReverse(&r, static_cast(x)); +# endif + return 63 - r; +} +# define FMT_BUILTIN_CLZLL(n) fmt::internal::clzll(n) +} +} +#endif + +#ifdef __GNUC__ +# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +# define FMT_GCC_EXTENSION __extension__ +# if FMT_GCC_VERSION >= 406 +# pragma GCC diagnostic push +// Disable the warning about "long long" which is sometimes reported even +// when using __extension__. +# pragma GCC diagnostic ignored "-Wlong-long" +// Disable the warning about declaration shadowing because it affects too +// many valid cases. +# pragma GCC diagnostic ignored "-Wshadow" +# endif +# if __cplusplus >= 201103L || defined __GXX_EXPERIMENTAL_CXX0X__ +# define FMT_HAS_GXX_CXX11 1 +# endif +#else +# define FMT_GCC_EXTENSION +#endif + +#ifdef __clang__ +# pragma clang diagnostic ignored "-Wdocumentation-unknown-command" +#endif + +#ifdef __GNUC_LIBSTD__ +# define FMT_GNUC_LIBSTD_VERSION (__GNUC_LIBSTD__ * 100 + __GNUC_LIBSTD_MINOR__) +#endif + +#ifdef __has_feature +# define FMT_HAS_FEATURE(x) __has_feature(x) +#else +# define FMT_HAS_FEATURE(x) 0 +#endif + +#ifdef __has_builtin +# define FMT_HAS_BUILTIN(x) __has_builtin(x) +#else +# define FMT_HAS_BUILTIN(x) 0 +#endif + +#ifdef __has_cpp_attribute +# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +# define FMT_HAS_CPP_ATTRIBUTE(x) 0 +#endif + +#ifndef FMT_USE_VARIADIC_TEMPLATES +// Variadic templates are available in GCC since version 4.4 +// (http://gcc.gnu.org/projects/cxx0x.html) and in Visual C++ +// since version 2013. +# define FMT_USE_VARIADIC_TEMPLATES \ + (FMT_HAS_FEATURE(cxx_variadic_templates) || \ + (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1800) +#endif + +#ifndef FMT_USE_RVALUE_REFERENCES +// Don't use rvalue references when compiling with clang and an old libstdc++ +// as the latter doesn't provide std::move. +# if defined(FMT_GNUC_LIBSTD_VERSION) && FMT_GNUC_LIBSTD_VERSION <= 402 +# define FMT_USE_RVALUE_REFERENCES 0 +# else +# define FMT_USE_RVALUE_REFERENCES \ + (FMT_HAS_FEATURE(cxx_rvalue_references) || \ + (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1600) +# endif +#endif + +#if FMT_USE_RVALUE_REFERENCES +# include // for std::move +#endif + +// Define FMT_USE_NOEXCEPT to make C++ Format use noexcept (C++11 feature). +#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ + (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) +# define FMT_NOEXCEPT noexcept +#else +# define FMT_NOEXCEPT throw() +#endif + +// A macro to disallow the copy constructor and operator= functions +// This should be used in the private: declarations for a class +#if FMT_USE_DELETED_FUNCTIONS || FMT_HAS_FEATURE(cxx_deleted_functions) || \ + (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1800 +# define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&) = delete; \ + TypeName& operator=(const TypeName&) = delete +#else +# define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + TypeName& operator=(const TypeName&) +#endif + +namespace fmt { + +// Fix the warning about long long on older versions of GCC +// that don't support the diagnostic pragma. +FMT_GCC_EXTENSION typedef long long LongLong; +FMT_GCC_EXTENSION typedef unsigned long long ULongLong; + +#if FMT_USE_RVALUE_REFERENCES +using std::move; +#endif + +template +class BasicWriter; + +typedef BasicWriter Writer; +typedef BasicWriter WWriter; + +template +class BasicFormatter; + +template +void format(BasicFormatter &f, const Char *&format_str, const T &value); + +/** + \rst + A string reference. It can be constructed from a C string or + ``std::string``. + + You can use one of the following typedefs for common character types: + + +------------+-------------------------+ + | Type | Definition | + +============+=========================+ + | StringRef | BasicStringRef | + +------------+-------------------------+ + | WStringRef | BasicStringRef | + +------------+-------------------------+ + + This class is most useful as a parameter type to allow passing + different types of strings to a function, for example:: + + template + std::string format(StringRef format_str, const Args & ... args); + + format("{}", 42); + format(std::string("{}"), 42); + \endrst + */ +template +class BasicStringRef { + private: + const Char *data_; + std::size_t size_; + + public: + /** + Constructs a string reference object from a C string and a size. + */ + BasicStringRef(const Char *s, std::size_t size) : data_(s), size_(size) {} + + /** + Constructs a string reference object from a C string computing + the size with ``std::char_traits::length``. + */ + BasicStringRef(const Char *s) + : data_(s), size_(std::char_traits::length(s)) {} + + /** + Constructs a string reference from an `std::string` object. + */ + BasicStringRef(const std::basic_string &s) + : data_(s.c_str()), size_(s.size()) {} + + /** + Converts a string reference to an `std::string` object. + */ + operator std::basic_string() const { + return std::basic_string(data_, size()); + } + + /** + Returns the pointer to a C string. + */ + const Char *c_str() const { return data_; } + + /** + Returns the string size. + */ + std::size_t size() const { return size_; } + + friend bool operator==(BasicStringRef lhs, BasicStringRef rhs) { + return lhs.data_ == rhs.data_; + } + friend bool operator!=(BasicStringRef lhs, BasicStringRef rhs) { + return lhs.data_ != rhs.data_; + } +}; + +typedef BasicStringRef StringRef; +typedef BasicStringRef WStringRef; + +/** + A formatting error such as invalid format string. +*/ +class FormatError : public std::runtime_error { +public: + explicit FormatError(StringRef message) + : std::runtime_error(message.c_str()) {} +}; + +namespace internal { + +// The number of characters to store in the MemoryBuffer object itself +// to avoid dynamic memory allocation. +enum { INLINE_BUFFER_SIZE = 500 }; + +#if _SECURE_SCL +// Use checked iterator to avoid warnings on MSVC. +template +inline stdext::checked_array_iterator make_ptr(T *ptr, std::size_t size) { + return stdext::checked_array_iterator(ptr, size); +} +#else +template +inline T *make_ptr(T *ptr, std::size_t) { return ptr; } +#endif + +// A buffer for POD types. It supports a subset of std::vector's operations. +template +class Buffer { + private: + FMT_DISALLOW_COPY_AND_ASSIGN(Buffer); + + protected: + T *ptr_; + std::size_t size_; + std::size_t capacity_; + + Buffer(T *ptr = 0, std::size_t capacity = 0) + : ptr_(ptr), size_(0), capacity_(capacity) {} + + virtual void grow(std::size_t size) = 0; + + public: + virtual ~Buffer() {} + + // Returns the size of this buffer. + std::size_t size() const { return size_; } + + // Returns the capacity of this buffer. + std::size_t capacity() const { return capacity_; } + + // Resizes the buffer. If T is a POD type new elements are not initialized. + void resize(std::size_t new_size) { + if (new_size > capacity_) + grow(new_size); + size_ = new_size; + } + + // Reserves space to store at least capacity elements. + void reserve(std::size_t capacity) { + if (capacity > capacity_) + grow(capacity); + } + + void clear() FMT_NOEXCEPT { size_ = 0; } + + void push_back(const T &value) { + if (size_ == capacity_) + grow(size_ + 1); + ptr_[size_++] = value; + } + + // Appends data to the end of the buffer. + void append(const T *begin, const T *end); + + T &operator[](std::size_t index) { return ptr_[index]; } + const T &operator[](std::size_t index) const { return ptr_[index]; } +}; + +template +void Buffer::append(const T *begin, const T *end) { + std::ptrdiff_t num_elements = end - begin; + if (size_ + num_elements > capacity_) + grow(size_ + num_elements); + std::copy(begin, end, make_ptr(ptr_, capacity_) + size_); + size_ += num_elements; +} + +// A memory buffer for POD types with the first SIZE elements stored in +// the object itself. +template > +class MemoryBuffer : private Allocator, public Buffer { + private: + T data_[SIZE]; + + // Free memory allocated by the buffer. + void free() { + if (this->ptr_ != data_) this->deallocate(this->ptr_, this->capacity_); + } + + protected: + void grow(std::size_t size); + + public: + explicit MemoryBuffer(const Allocator &alloc = Allocator()) + : Allocator(alloc), Buffer(data_, SIZE) {} + ~MemoryBuffer() { free(); } + +#if FMT_USE_RVALUE_REFERENCES + private: + // Move data from other to this buffer. + void move(MemoryBuffer &other) { + Allocator &this_alloc = *this, &other_alloc = other; + this_alloc = std::move(other_alloc); + this->size_ = other.size_; + this->capacity_ = other.capacity_; + if (other.ptr_ == other.data_) { + this->ptr_ = data_; + std::copy(other.data_, + other.data_ + this->size_, make_ptr(data_, this->capacity_)); + } else { + this->ptr_ = other.ptr_; + // Set pointer to the inline array so that delete is not called + // when freeing. + other.ptr_ = other.data_; + } + } + + public: + MemoryBuffer(MemoryBuffer &&other) { + move(other); + } + + MemoryBuffer &operator=(MemoryBuffer &&other) { + assert(this != &other); + free(); + move(other); + return *this; + } +#endif + + // Returns a copy of the allocator associated with this buffer. + Allocator get_allocator() const { return *this; } +}; + +template +void MemoryBuffer::grow(std::size_t size) { + std::size_t new_capacity = + (std::max)(size, this->capacity_ + this->capacity_ / 2); + T *new_ptr = this->allocate(new_capacity); + // The following code doesn't throw, so the raw pointer above doesn't leak. + std::copy(this->ptr_, + this->ptr_ + this->size_, make_ptr(new_ptr, new_capacity)); + std::size_t old_capacity = this->capacity_; + T *old_ptr = this->ptr_; + this->capacity_ = new_capacity; + this->ptr_ = new_ptr; + // deallocate may throw (at least in principle), but it doesn't matter since + // the buffer already uses the new storage and will deallocate it in case + // of exception. + if (old_ptr != data_) + this->deallocate(old_ptr, old_capacity); +} + +#ifndef _MSC_VER +// Portable version of signbit. +inline int getsign(double x) { + // When compiled in C++11 mode signbit is no longer a macro but a function + // defined in namespace std and the macro is undefined. +# ifdef signbit + return signbit(x); +# else + return std::signbit(x); +# endif +} + +// Portable version of isinf. +# ifdef isinf +inline int isinfinity(double x) { return isinf(x); } +inline int isinfinity(long double x) { return isinf(x); } +# else +inline int isinfinity(double x) { return std::isinf(x); } +inline int isinfinity(long double x) { return std::isinf(x); } +# endif +#else +inline int getsign(double value) { + if (value < 0) return 1; + if (value == value) return 0; + int dec = 0, sign = 0; + char buffer[2]; // The buffer size must be >= 2 or _ecvt_s will fail. + _ecvt_s(buffer, sizeof(buffer), value, 0, &dec, &sign); + return sign; +} +inline int isinfinity(double x) { return !_finite(x); } +inline int isinfinity(long double x) { return !_finite(static_cast(x)); } +#endif + +template +class BasicCharTraits { + public: +#if _SECURE_SCL + typedef stdext::checked_array_iterator CharPtr; +#else + typedef Char *CharPtr; +#endif +}; + +template +class CharTraits; + +template <> +class CharTraits : public BasicCharTraits { + private: + // Conversion from wchar_t to char is not allowed. + static char convert(wchar_t); + +public: + typedef const wchar_t *UnsupportedStrType; + + static char convert(char value) { return value; } + + // Formats a floating-point number. + template + static int format_float(char *buffer, std::size_t size, + const char *format, unsigned width, int precision, T value); +}; + +template <> +class CharTraits : public BasicCharTraits { + public: + typedef const char *UnsupportedStrType; + + static wchar_t convert(char value) { return value; } + static wchar_t convert(wchar_t value) { return value; } + + template + static int format_float(wchar_t *buffer, std::size_t size, + const wchar_t *format, unsigned width, int precision, T value); +}; + +// Checks if a number is negative - used to avoid warnings. +template +struct SignChecker { + template + static bool is_negative(T value) { return value < 0; } +}; + +template <> +struct SignChecker { + template + static bool is_negative(T) { return false; } +}; + +// Returns true if value is negative, false otherwise. +// Same as (value < 0) but doesn't produce warnings if T is an unsigned type. +template +inline bool is_negative(T value) { + return SignChecker::is_signed>::is_negative(value); +} + +// Selects uint32_t if FitsIn32Bits is true, uint64_t otherwise. +template +struct TypeSelector { typedef uint32_t Type; }; + +template <> +struct TypeSelector { typedef uint64_t Type; }; + +template +struct IntTraits { + // Smallest of uint32_t and uint64_t that is large enough to represent + // all values of T. + typedef typename + TypeSelector::digits <= 32>::Type MainType; +}; + +// MakeUnsigned::Type gives an unsigned type corresponding to integer type T. +template +struct MakeUnsigned { typedef T Type; }; + +#define FMT_SPECIALIZE_MAKE_UNSIGNED(T, U) \ + template <> \ + struct MakeUnsigned { typedef U Type; } + +FMT_SPECIALIZE_MAKE_UNSIGNED(char, unsigned char); +FMT_SPECIALIZE_MAKE_UNSIGNED(signed char, unsigned char); +FMT_SPECIALIZE_MAKE_UNSIGNED(short, unsigned short); +FMT_SPECIALIZE_MAKE_UNSIGNED(int, unsigned); +FMT_SPECIALIZE_MAKE_UNSIGNED(long, unsigned long); +FMT_SPECIALIZE_MAKE_UNSIGNED(LongLong, ULongLong); + +void report_unknown_type(char code, const char *type); + +// Static data is placed in this class template to allow header-only +// configuration. +template +struct BasicData { + static const uint32_t POWERS_OF_10_32[]; + static const uint64_t POWERS_OF_10_64[]; + static const char DIGITS[]; +}; + +typedef BasicData<> Data; + +#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clz) +# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) +#endif + +#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clzll) +# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) +#endif + +#ifdef FMT_BUILTIN_CLZLL +// Returns the number of decimal digits in n. Leading zeros are not counted +// except for n == 0 in which case count_digits returns 1. +inline unsigned count_digits(uint64_t n) { + // Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 + // and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits. + unsigned t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12; + return t - (n < Data::POWERS_OF_10_64[t]) + 1; +} +#else +// Fallback version of count_digits used when __builtin_clz is not available. +inline unsigned count_digits(uint64_t n) { + unsigned count = 1; + for (;;) { + // Integer division is slow so do it for a group of four digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + if (n < 10) return count; + if (n < 100) return count + 1; + if (n < 1000) return count + 2; + if (n < 10000) return count + 3; + n /= 10000u; + count += 4; + } +} +#endif + +#ifdef FMT_BUILTIN_CLZ +// Optional version of count_digits for better performance on 32-bit platforms. +inline unsigned count_digits(uint32_t n) { + uint32_t t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12; + return t - (n < Data::POWERS_OF_10_32[t]) + 1; +} +#endif + +// Formats a decimal unsigned integer value writing into buffer. +template +inline void format_decimal(Char *buffer, UInt value, unsigned num_digits) { + --num_digits; + while (value >= 100) { + // Integer division is slow so do it for a group of two digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + unsigned index = (value % 100) * 2; + value /= 100; + buffer[num_digits] = Data::DIGITS[index + 1]; + buffer[num_digits - 1] = Data::DIGITS[index]; + num_digits -= 2; + } + if (value < 10) { + *buffer = static_cast('0' + value); + return; + } + unsigned index = static_cast(value * 2); + buffer[1] = Data::DIGITS[index + 1]; + buffer[0] = Data::DIGITS[index]; +} + +#ifdef _WIN32 +// A converter from UTF-8 to UTF-16. +// It is only provided for Windows since other systems support UTF-8 natively. +class UTF8ToUTF16 { + private: + MemoryBuffer buffer_; + + public: + explicit UTF8ToUTF16(StringRef s); + operator WStringRef() const { return WStringRef(&buffer_[0], size()); } + size_t size() const { return buffer_.size() - 1; } + const wchar_t *c_str() const { return &buffer_[0]; } + std::wstring str() const { return std::wstring(&buffer_[0], size()); } +}; + +// A converter from UTF-16 to UTF-8. +// It is only provided for Windows since other systems support UTF-8 natively. +class UTF16ToUTF8 { + private: + MemoryBuffer buffer_; + + public: + UTF16ToUTF8() {} + explicit UTF16ToUTF8(WStringRef s); + operator StringRef() const { return StringRef(&buffer_[0], size()); } + size_t size() const { return buffer_.size() - 1; } + const char *c_str() const { return &buffer_[0]; } + std::string str() const { return std::string(&buffer_[0], size()); } + + // Performs conversion returning a system error code instead of + // throwing exception on conversion error. This method may still throw + // in case of memory allocation error. + int convert(WStringRef s); +}; +#endif + +void format_system_error(fmt::Writer &out, int error_code, + fmt::StringRef message) FMT_NOEXCEPT; + +#ifdef _WIN32 +void format_windows_error(fmt::Writer &out, int error_code, + fmt::StringRef message) FMT_NOEXCEPT; +#endif + +// Computes max(Arg, 1) at compile time. It is used to avoid errors about +// allocating an array of 0 size. +template +struct NonZero { + enum { VALUE = Arg }; +}; + +template <> +struct NonZero<0> { + enum { VALUE = 1 }; +}; + +// The value of a formatting argument. It is a POD type to allow storage in +// internal::MemoryBuffer. +struct Value { + template + struct StringValue { + const Char *value; + std::size_t size; + }; + + typedef void (*FormatFunc)( + void *formatter, const void *arg, void *format_str_ptr); + + struct CustomValue { + const void *value; + FormatFunc format; + }; + + union { + int int_value; + unsigned uint_value; + LongLong long_long_value; + ULongLong ulong_long_value; + double double_value; + long double long_double_value; + const void *pointer; + StringValue string; + StringValue sstring; + StringValue ustring; + StringValue wstring; + CustomValue custom; + }; +}; + +struct Arg : Value { + enum Type { + NONE, + // Integer types should go first, + INT, UINT, LONG_LONG, ULONG_LONG, CHAR, LAST_INTEGER_TYPE = CHAR, + // followed by floating-point types. + DOUBLE, LONG_DOUBLE, LAST_NUMERIC_TYPE = LONG_DOUBLE, + CSTRING, STRING, WSTRING, POINTER, CUSTOM + }; + Type type; +}; + +// Makes a Value object from any type. +template +class MakeValue : public Value { + private: + // The following two methods are private to disallow formatting of + // arbitrary pointers. If you want to output a pointer cast it to + // "void *" or "const void *". In particular, this forbids formatting + // of "[const] volatile char *" which is printed as bool by iostreams. + // Do not implement! + template + MakeValue(const T *value); + template + MakeValue(T *value); + + void set_string(StringRef str) { + string.value = str.c_str(); + string.size = str.size(); + } + + void set_string(WStringRef str) { + CharTraits::convert(wchar_t()); + wstring.value = str.c_str(); + wstring.size = str.size(); + } + + // Formats an argument of a custom type, such as a user-defined class. + template + static void format_custom_arg( + void *formatter, const void *arg, void *format_str_ptr) { + format(*static_cast*>(formatter), + *static_cast(format_str_ptr), + *static_cast(arg)); + } + +public: + MakeValue() {} + +#define FMT_MAKE_VALUE(Type, field, TYPE) \ + MakeValue(Type value) { field = value; } \ + static uint64_t type(Type) { return Arg::TYPE; } + + FMT_MAKE_VALUE(bool, int_value, INT) + FMT_MAKE_VALUE(short, int_value, INT) + FMT_MAKE_VALUE(unsigned short, uint_value, UINT) + FMT_MAKE_VALUE(int, int_value, INT) + FMT_MAKE_VALUE(unsigned, uint_value, UINT) + + MakeValue(long value) { + // To minimize the number of types we need to deal with, long is + // translated either to int or to long long depending on its size. + if (sizeof(long) == sizeof(int)) + int_value = static_cast(value); + else + long_long_value = value; + } + static uint64_t type(long) { + return sizeof(long) == sizeof(int) ? Arg::INT : Arg::LONG_LONG; + } + + MakeValue(unsigned long value) { + if (sizeof(unsigned long) == sizeof(unsigned)) + uint_value = static_cast(value); + else + ulong_long_value = value; + } + static uint64_t type(unsigned long) { + return sizeof(unsigned long) == sizeof(unsigned) ? + Arg::UINT : Arg::ULONG_LONG; + } + + FMT_MAKE_VALUE(LongLong, long_long_value, LONG_LONG) + FMT_MAKE_VALUE(ULongLong, ulong_long_value, ULONG_LONG) + FMT_MAKE_VALUE(float, double_value, DOUBLE) + FMT_MAKE_VALUE(double, double_value, DOUBLE) + FMT_MAKE_VALUE(long double, long_double_value, LONG_DOUBLE) + FMT_MAKE_VALUE(signed char, int_value, CHAR) + FMT_MAKE_VALUE(unsigned char, int_value, CHAR) + FMT_MAKE_VALUE(char, int_value, CHAR) + + MakeValue(wchar_t value) { + int_value = internal::CharTraits::convert(value); + } + static uint64_t type(wchar_t) { return Arg::CHAR; } + +#define FMT_MAKE_STR_VALUE(Type, TYPE) \ + MakeValue(Type value) { set_string(value); } \ + static uint64_t type(Type) { return Arg::TYPE; } + + FMT_MAKE_VALUE(char *, string.value, CSTRING) + FMT_MAKE_VALUE(const char *, string.value, CSTRING) + FMT_MAKE_VALUE(const signed char *, sstring.value, CSTRING) + FMT_MAKE_VALUE(const unsigned char *, ustring.value, CSTRING) + FMT_MAKE_STR_VALUE(const std::string &, STRING) + FMT_MAKE_STR_VALUE(StringRef, STRING) + + FMT_MAKE_STR_VALUE(wchar_t *, WSTRING) + FMT_MAKE_STR_VALUE(const wchar_t *, WSTRING) + FMT_MAKE_STR_VALUE(const std::wstring &, WSTRING) + FMT_MAKE_STR_VALUE(WStringRef, WSTRING) + + FMT_MAKE_VALUE(void *, pointer, POINTER) + FMT_MAKE_VALUE(const void *, pointer, POINTER) + + template + MakeValue(const T &value) { + custom.value = &value; + custom.format = &format_custom_arg; + } + template + static uint64_t type(const T &) { return Arg::CUSTOM; } +}; + +#define FMT_DISPATCH(call) static_cast(this)->call + +// An argument visitor. +// To use ArgVisitor define a subclass that implements some or all of the +// visit methods with the same signatures as the methods in ArgVisitor, +// for example, visit_int(int). +// Specify the subclass name as the Impl template parameter. Then calling +// ArgVisitor::visit for some argument will dispatch to a visit method +// specific to the argument type. For example, if the argument type is +// double then visit_double(double) method of a subclass will be called. +// If the subclass doesn't contain a method with this signature, then +// a corresponding method of ArgVisitor will be called. +// +// Example: +// class MyArgVisitor : public ArgVisitor { +// public: +// void visit_int(int value) { print("{}", value); } +// void visit_double(double value) { print("{}", value ); } +// }; +// +// ArgVisitor uses the curiously recurring template pattern: +// http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern +template +class ArgVisitor { + public: + Result visit_unhandled_arg() { return Result(); } + + Result visit_int(int value) { + return FMT_DISPATCH(visit_any_int(value)); + } + Result visit_long_long(LongLong value) { + return FMT_DISPATCH(visit_any_int(value)); + } + Result visit_uint(unsigned value) { + return FMT_DISPATCH(visit_any_int(value)); + } + Result visit_ulong_long(ULongLong value) { + return FMT_DISPATCH(visit_any_int(value)); + } + Result visit_char(int value) { + return FMT_DISPATCH(visit_any_int(value)); + } + template + Result visit_any_int(T) { + return FMT_DISPATCH(visit_unhandled_arg()); + } + + Result visit_double(double value) { + return FMT_DISPATCH(visit_any_double(value)); + } + Result visit_long_double(long double value) { + return FMT_DISPATCH(visit_any_double(value)); + } + template + Result visit_any_double(T) { + return FMT_DISPATCH(visit_unhandled_arg()); + } + + Result visit_string(Arg::StringValue) { + return FMT_DISPATCH(visit_unhandled_arg()); + } + Result visit_wstring(Arg::StringValue) { + return FMT_DISPATCH(visit_unhandled_arg()); + } + Result visit_pointer(const void *) { + return FMT_DISPATCH(visit_unhandled_arg()); + } + Result visit_custom(Arg::CustomValue) { + return FMT_DISPATCH(visit_unhandled_arg()); + } + + Result visit(const Arg &arg) { + switch (arg.type) { + default: + assert(false); + return Result(); + case Arg::INT: + return FMT_DISPATCH(visit_int(arg.int_value)); + case Arg::UINT: + return FMT_DISPATCH(visit_uint(arg.uint_value)); + case Arg::LONG_LONG: + return FMT_DISPATCH(visit_long_long(arg.long_long_value)); + case Arg::ULONG_LONG: + return FMT_DISPATCH(visit_ulong_long(arg.ulong_long_value)); + case Arg::DOUBLE: + return FMT_DISPATCH(visit_double(arg.double_value)); + case Arg::LONG_DOUBLE: + return FMT_DISPATCH(visit_long_double(arg.long_double_value)); + case Arg::CHAR: + return FMT_DISPATCH(visit_char(arg.int_value)); + case Arg::CSTRING: { + Value::StringValue str = arg.string; + str.size = 0; + return FMT_DISPATCH(visit_string(str)); + } + case Arg::STRING: + return FMT_DISPATCH(visit_string(arg.string)); + case Arg::WSTRING: + return FMT_DISPATCH(visit_wstring(arg.wstring)); + case Arg::POINTER: + return FMT_DISPATCH(visit_pointer(arg.pointer)); + case Arg::CUSTOM: + return FMT_DISPATCH(visit_custom(arg.custom)); + } + } +}; + +class RuntimeError : public std::runtime_error { + protected: + RuntimeError() : std::runtime_error("") {} +}; + +template +class ArgFormatter; +} // namespace internal + +/** + An argument list. + */ +class ArgList { + private: + uint64_t types_; + const internal::Value *values_; + + public: + // Maximum number of arguments that can be passed in ArgList. + enum { MAX_ARGS = 16 }; + + ArgList() : types_(0) {} + ArgList(ULongLong types, const internal::Value *values) + : types_(types), values_(values) {} + + /** + Returns the argument at specified index. + */ + internal::Arg operator[](unsigned index) const { + using internal::Arg; + Arg arg; + if (index >= MAX_ARGS) { + arg.type = Arg::NONE; + return arg; + } + unsigned shift = index * 4; + uint64_t mask = 0xf; + Arg::Type type = + static_cast((types_ & (mask << shift)) >> shift); + arg.type = type; + if (type != Arg::NONE) { + internal::Value &value = arg; + value = values_[index]; + } + return arg; + } +}; + +struct FormatSpec; + +namespace internal { + +class FormatterBase { + private: + ArgList args_; + int next_arg_index_; + + // Returns the argument with specified index. + Arg do_get_arg(unsigned arg_index, const char *&error); + + protected: + void set_args(const ArgList &args) { + args_ = args; + next_arg_index_ = 0; + } + + // Returns the next argument. + Arg next_arg(const char *&error); + + // Checks if manual indexing is used and returns the argument with + // specified index. + Arg get_arg(unsigned arg_index, const char *&error); + + template + void write(BasicWriter &w, const Char *start, const Char *end) { + if (start != end) + w << BasicStringRef(start, end - start); + } +}; + +// A printf formatter. +template +class PrintfFormatter : private FormatterBase { + private: + void parse_flags(FormatSpec &spec, const Char *&s); + + // Returns the argument with specified index or, if arg_index is equal + // to the maximum unsigned value, the next argument. + Arg get_arg(const Char *s, + unsigned arg_index = (std::numeric_limits::max)()); + + // Parses argument index, flags and width and returns the argument index. + unsigned parse_header(const Char *&s, FormatSpec &spec); + + public: + void format(BasicWriter &writer, + BasicStringRef format_str, const ArgList &args); +}; +} // namespace internal + +// A formatter. +template +class BasicFormatter : private internal::FormatterBase { + private: + BasicWriter &writer_; + const Char *start_; + + FMT_DISALLOW_COPY_AND_ASSIGN(BasicFormatter); + + // Parses argument index and returns corresponding argument. + internal::Arg parse_arg_index(const Char *&s); + + public: + explicit BasicFormatter(BasicWriter &w) : writer_(w) {} + + BasicWriter &writer() { return writer_; } + + void format(BasicStringRef format_str, const ArgList &args); + + const Char *format(const Char *&format_str, const internal::Arg &arg); +}; + +enum Alignment { + ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC +}; + +// Flags. +enum { + SIGN_FLAG = 1, PLUS_FLAG = 2, MINUS_FLAG = 4, HASH_FLAG = 8, + CHAR_FLAG = 0x10 // Argument has char type - used in error reporting. +}; + +// An empty format specifier. +struct EmptySpec {}; + +// A type specifier. +template +struct TypeSpec : EmptySpec { + Alignment align() const { return ALIGN_DEFAULT; } + unsigned width() const { return 0; } + int precision() const { return -1; } + bool flag(unsigned) const { return false; } + char type() const { return TYPE; } + char fill() const { return ' '; } +}; + +// A width specifier. +struct WidthSpec { + unsigned width_; + // Fill is always wchar_t and cast to char if necessary to avoid having + // two specialization of WidthSpec and its subclasses. + wchar_t fill_; + + WidthSpec(unsigned width, wchar_t fill) : width_(width), fill_(fill) {} + + unsigned width() const { return width_; } + wchar_t fill() const { return fill_; } +}; + +// An alignment specifier. +struct AlignSpec : WidthSpec { + Alignment align_; + + AlignSpec(unsigned width, wchar_t fill, Alignment align = ALIGN_DEFAULT) + : WidthSpec(width, fill), align_(align) {} + + Alignment align() const { return align_; } + + int precision() const { return -1; } +}; + +// An alignment and type specifier. +template +struct AlignTypeSpec : AlignSpec { + AlignTypeSpec(unsigned width, wchar_t fill) : AlignSpec(width, fill) {} + + bool flag(unsigned) const { return false; } + char type() const { return TYPE; } +}; + +// A full format specifier. +struct FormatSpec : AlignSpec { + unsigned flags_; + int precision_; + char type_; + + FormatSpec( + unsigned width = 0, char type = 0, wchar_t fill = ' ') + : AlignSpec(width, fill), flags_(0), precision_(-1), type_(type) {} + + bool flag(unsigned f) const { return (flags_ & f) != 0; } + int precision() const { return precision_; } + char type() const { return type_; } +}; + +// An integer format specifier. +template , typename Char = char> +class IntFormatSpec : public SpecT { + private: + T value_; + + public: + IntFormatSpec(T val, const SpecT &spec = SpecT()) + : SpecT(spec), value_(val) {} + + T value() const { return value_; } +}; + +// A string format specifier. +template +class StrFormatSpec : public AlignSpec { + private: + const T *str_; + + public: + StrFormatSpec(const T *str, unsigned width, wchar_t fill) + : AlignSpec(width, fill), str_(str) {} + + const T *str() const { return str_; } +}; + +/** + Returns an integer format specifier to format the value in base 2. + */ +IntFormatSpec > bin(int value); + +/** + Returns an integer format specifier to format the value in base 8. + */ +IntFormatSpec > oct(int value); + +/** + Returns an integer format specifier to format the value in base 16 using + lower-case letters for the digits above 9. + */ +IntFormatSpec > hex(int value); + +/** + Returns an integer formatter format specifier to format in base 16 using + upper-case letters for the digits above 9. + */ +IntFormatSpec > hexu(int value); + +/** + \rst + Returns an integer format specifier to pad the formatted argument with the + fill character to the specified width using the default (right) numeric + alignment. + + **Example**:: + + MemoryWriter out; + out << pad(hex(0xcafe), 8, '0'); + // out.str() == "0000cafe" + + \endrst + */ +template +IntFormatSpec, Char> pad( + int value, unsigned width, Char fill = ' '); + +#define FMT_DEFINE_INT_FORMATTERS(TYPE) \ +inline IntFormatSpec > bin(TYPE value) { \ + return IntFormatSpec >(value, TypeSpec<'b'>()); \ +} \ + \ +inline IntFormatSpec > oct(TYPE value) { \ + return IntFormatSpec >(value, TypeSpec<'o'>()); \ +} \ + \ +inline IntFormatSpec > hex(TYPE value) { \ + return IntFormatSpec >(value, TypeSpec<'x'>()); \ +} \ + \ +inline IntFormatSpec > hexu(TYPE value) { \ + return IntFormatSpec >(value, TypeSpec<'X'>()); \ +} \ + \ +template \ +inline IntFormatSpec > pad( \ + IntFormatSpec > f, unsigned width) { \ + return IntFormatSpec >( \ + f.value(), AlignTypeSpec(width, ' ')); \ +} \ + \ +/* For compatibility with older compilers we provide two overloads for pad, */ \ +/* one that takes a fill character and one that doesn't. In the future this */ \ +/* can be replaced with one overload making the template argument Char */ \ +/* default to char (C++11). */ \ +template \ +inline IntFormatSpec, Char> pad( \ + IntFormatSpec, Char> f, \ + unsigned width, Char fill) { \ + return IntFormatSpec, Char>( \ + f.value(), AlignTypeSpec(width, fill)); \ +} \ + \ +inline IntFormatSpec > pad( \ + TYPE value, unsigned width) { \ + return IntFormatSpec >( \ + value, AlignTypeSpec<0>(width, ' ')); \ +} \ + \ +template \ +inline IntFormatSpec, Char> pad( \ + TYPE value, unsigned width, Char fill) { \ + return IntFormatSpec, Char>( \ + value, AlignTypeSpec<0>(width, fill)); \ +} + +FMT_DEFINE_INT_FORMATTERS(int) +FMT_DEFINE_INT_FORMATTERS(long) +FMT_DEFINE_INT_FORMATTERS(unsigned) +FMT_DEFINE_INT_FORMATTERS(unsigned long) +FMT_DEFINE_INT_FORMATTERS(LongLong) +FMT_DEFINE_INT_FORMATTERS(ULongLong) + +/** + \rst + Returns a string formatter that pads the formatted argument with the fill + character to the specified width using the default (left) string alignment. + + **Example**:: + + std::string s = str(MemoryWriter() << pad("abc", 8)); + // s == "abc " + + \endrst + */ +template +inline StrFormatSpec pad( + const Char *str, unsigned width, Char fill = ' ') { + return StrFormatSpec(str, width, fill); +} + +inline StrFormatSpec pad( + const wchar_t *str, unsigned width, char fill = ' ') { + return StrFormatSpec(str, width, fill); +} + +// Generates a comma-separated list with results of applying f to +// numbers 0..n-1. +# define FMT_GEN(n, f) FMT_GEN##n(f) +# define FMT_GEN1(f) f(0) +# define FMT_GEN2(f) FMT_GEN1(f), f(1) +# define FMT_GEN3(f) FMT_GEN2(f), f(2) +# define FMT_GEN4(f) FMT_GEN3(f), f(3) +# define FMT_GEN5(f) FMT_GEN4(f), f(4) +# define FMT_GEN6(f) FMT_GEN5(f), f(5) +# define FMT_GEN7(f) FMT_GEN6(f), f(6) +# define FMT_GEN8(f) FMT_GEN7(f), f(7) +# define FMT_GEN9(f) FMT_GEN8(f), f(8) +# define FMT_GEN10(f) FMT_GEN9(f), f(9) +# define FMT_GEN11(f) FMT_GEN10(f), f(10) +# define FMT_GEN12(f) FMT_GEN11(f), f(11) +# define FMT_GEN13(f) FMT_GEN12(f), f(12) +# define FMT_GEN14(f) FMT_GEN13(f), f(13) +# define FMT_GEN15(f) FMT_GEN14(f), f(14) + +namespace internal { +inline uint64_t make_type() { return 0; } + +template +inline uint64_t make_type(const T &arg) { return MakeValue::type(arg); } + +#if FMT_USE_VARIADIC_TEMPLATES +template +inline uint64_t make_type(const Arg &first, const Args & ... tail) { + return make_type(first) | (make_type(tail...) << 4); +} +#else + +struct ArgType { + uint64_t type; + + ArgType() : type(0) {} + + template + ArgType(const T &arg) : type(make_type(arg)) {} +}; + +# define FMT_ARG_TYPE_DEFAULT(n) ArgType t##n = ArgType() + +inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) { + return t0.type | (t1.type << 4) | (t2.type << 8) | (t3.type << 12) | + (t4.type << 16) | (t5.type << 20) | (t6.type << 24) | (t7.type << 28) | + (t8.type << 32) | (t9.type << 36) | (t10.type << 40) | (t11.type << 44) | + (t12.type << 48) | (t13.type << 52) | (t14.type << 56); +} +#endif +} // namespace internal + +# define FMT_MAKE_TEMPLATE_ARG(n) typename T##n +# define FMT_MAKE_ARG_TYPE(n) T##n +# define FMT_MAKE_ARG(n) const T##n &v##n +# define FMT_MAKE_REF_char(n) fmt::internal::MakeValue(v##n) +# define FMT_MAKE_REF_wchar_t(n) fmt::internal::MakeValue(v##n) + +#if FMT_USE_VARIADIC_TEMPLATES +// Defines a variadic function returning void. +# define FMT_VARIADIC_VOID(func, arg_type) \ + template \ + void func(arg_type arg1, const Args & ... args) { \ + const fmt::internal::Value values[ \ + fmt::internal::NonZero::VALUE] = { \ + fmt::internal::MakeValue(args)... \ + }; \ + func(arg1, ArgList(fmt::internal::make_type(args...), values)); \ + } + +// Defines a variadic constructor. +# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ + template \ + ctor(arg0_type arg0, arg1_type arg1, const Args & ... args) { \ + using fmt::internal::MakeValue; \ + const fmt::internal::Value values[ \ + fmt::internal::NonZero::VALUE] = { \ + MakeValue(args)... \ + }; \ + func(arg0, arg1, ArgList(fmt::internal::make_type(args...), values)); \ + } + +#else + +# define FMT_MAKE_REF(n) fmt::internal::MakeValue(v##n) +# define FMT_MAKE_REF2(n) v##n + +// Defines a wrapper for a function taking one argument of type arg_type +// and n additional arguments of arbitrary types. +# define FMT_WRAP1(func, arg_type, n) \ + template \ + inline void func(arg_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ + const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF)}; \ + func(arg1, fmt::ArgList( \ + fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), vals)); \ + } + +// Emulates a variadic function returning void on a pre-C++11 compiler. +# define FMT_VARIADIC_VOID(func, arg_type) \ + inline void func(arg_type arg) { func(arg, fmt::ArgList()); } \ + FMT_WRAP1(func, arg_type, 1) FMT_WRAP1(func, arg_type, 2) \ + FMT_WRAP1(func, arg_type, 3) FMT_WRAP1(func, arg_type, 4) \ + FMT_WRAP1(func, arg_type, 5) FMT_WRAP1(func, arg_type, 6) \ + FMT_WRAP1(func, arg_type, 7) FMT_WRAP1(func, arg_type, 8) \ + FMT_WRAP1(func, arg_type, 9) FMT_WRAP1(func, arg_type, 10) + +# define FMT_CTOR(ctor, func, arg0_type, arg1_type, n) \ + template \ + ctor(arg0_type arg0, arg1_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ + const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF)}; \ + func(arg0, arg1, fmt::ArgList( \ + fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), vals)); \ + } + +// Emulates a variadic constructor on a pre-C++11 compiler. +# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 1) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 2) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 3) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 4) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 5) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 6) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 7) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 8) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 9) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 10) +#endif + +// Generates a comma-separated list with results of applying f to pairs +// (argument, index). +#define FMT_FOR_EACH1(f, x0) f(x0, 0) +#define FMT_FOR_EACH2(f, x0, x1) \ + FMT_FOR_EACH1(f, x0), f(x1, 1) +#define FMT_FOR_EACH3(f, x0, x1, x2) \ + FMT_FOR_EACH2(f, x0 ,x1), f(x2, 2) +#define FMT_FOR_EACH4(f, x0, x1, x2, x3) \ + FMT_FOR_EACH3(f, x0, x1, x2), f(x3, 3) +#define FMT_FOR_EACH5(f, x0, x1, x2, x3, x4) \ + FMT_FOR_EACH4(f, x0, x1, x2, x3), f(x4, 4) +#define FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5) \ + FMT_FOR_EACH5(f, x0, x1, x2, x3, x4), f(x5, 5) +#define FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6) \ + FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5), f(x6, 6) +#define FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7) \ + FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6), f(x7, 7) +#define FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8) \ + FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7), f(x8, 8) +#define FMT_FOR_EACH10(f, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) \ + FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8), f(x9, 9) + +/** + An error returned by an operating system or a language runtime, + for example a file opening error. +*/ +class SystemError : public internal::RuntimeError { + private: + void init(int err_code, StringRef format_str, ArgList args); + + protected: + int error_code_; + + typedef char Char; // For FMT_VARIADIC_CTOR. + + SystemError() {} + + public: + /** + \rst + Constructs a :class:`fmt::SystemError` object with the description + of the form + + .. parsed-literal:: + **: ** + + where ** is the formatted message and ** is + the system message corresponding to the error code. + *error_code* is a system error code as given by ``errno``. + If *error_code* is not a valid error code such as -1, the system message + may look like "Unknown error -1" and is platform-dependent. + + **Example**:: + + // This throws a SystemError with the description + // cannot open file 'madeup': No such file or directory + // or similar (system message may vary). + const char *filename = "madeup"; + std::FILE *file = std::fopen(filename, "r"); + if (!file) + throw fmt::SystemError(errno, "cannot open file '{}'", filename); + \endrst + */ + SystemError(int error_code, StringRef message) { + init(error_code, message, ArgList()); + } + FMT_VARIADIC_CTOR(SystemError, init, int, StringRef) + + int error_code() const { return error_code_; } +}; + +/** + \rst + This template provides operations for formatting and writing data into + a character stream. The output is stored in a buffer provided by a subclass + such as :class:`fmt::BasicMemoryWriter`. + + You can use one of the following typedefs for common character types: + + +---------+----------------------+ + | Type | Definition | + +=========+======================+ + | Writer | BasicWriter | + +---------+----------------------+ + | WWriter | BasicWriter | + +---------+----------------------+ + + \endrst + */ +template +class BasicWriter { + private: + // Output buffer. + internal::Buffer &buffer_; + + FMT_DISALLOW_COPY_AND_ASSIGN(BasicWriter); + + typedef typename internal::CharTraits::CharPtr CharPtr; + +#if _SECURE_SCL + // Returns pointer value. + static Char *get(CharPtr p) { return p.base(); } +#else + static Char *get(Char *p) { return p; } +#endif + + // Fills the padding around the content and returns the pointer to the + // content area. + static CharPtr fill_padding(CharPtr buffer, + unsigned total_size, std::size_t content_size, wchar_t fill); + + // Grows the buffer by n characters and returns a pointer to the newly + // allocated area. + CharPtr grow_buffer(std::size_t n) { + std::size_t size = buffer_.size(); + buffer_.resize(size + n); + return internal::make_ptr(&buffer_[size], n); + } + + // Prepare a buffer for integer formatting. + CharPtr prepare_int_buffer(unsigned num_digits, + const EmptySpec &, const char *prefix, unsigned prefix_size) { + unsigned size = prefix_size + num_digits; + CharPtr p = grow_buffer(size); + std::copy(prefix, prefix + prefix_size, p); + return p + size - 1; + } + + template + CharPtr prepare_int_buffer(unsigned num_digits, + const Spec &spec, const char *prefix, unsigned prefix_size); + + // Formats an integer. + template + void write_int(T value, Spec spec); + + // Formats a floating-point number (double or long double). + template + void write_double(T value, const FormatSpec &spec); + + // Writes a formatted string. + template + CharPtr write_str( + const StrChar *s, std::size_t size, const AlignSpec &spec); + + template + void write_str( + const internal::Arg::StringValue &str, const FormatSpec &spec); + + // This method is private to disallow writing a wide string to a + // char stream and vice versa. If you want to print a wide string + // as a pointer as std::ostream does, cast it to const void*. + // Do not implement! + void operator<<(typename internal::CharTraits::UnsupportedStrType); + + // Appends floating-point length specifier to the format string. + // The second argument is only used for overload resolution. + void append_float_length(Char *&format_ptr, long double) { + *format_ptr++ = 'L'; + } + + template + void append_float_length(Char *&, T) {} + + friend class internal::ArgFormatter; + friend class internal::PrintfFormatter; + + protected: + /** + Constructs a ``BasicWriter`` object. + */ + explicit BasicWriter(internal::Buffer &b) : buffer_(b) {} + + public: + /** + Destroys a ``BasicWriter`` object. + */ + virtual ~BasicWriter() {} + + /** + Returns the total number of characters written. + */ + std::size_t size() const { return buffer_.size(); } + + /** + Returns a pointer to the output buffer content. No terminating null + character is appended. + */ + const Char *data() const FMT_NOEXCEPT { return &buffer_[0]; } + + /** + Returns a pointer to the output buffer content with terminating null + character appended. + */ + const Char *c_str() const { + std::size_t size = buffer_.size(); + buffer_.reserve(size + 1); + buffer_[size] = '\0'; + return &buffer_[0]; + } + + /** + Returns the content of the output buffer as an `std::string`. + */ + std::basic_string str() const { + return std::basic_string(&buffer_[0], buffer_.size()); + } + + /** + \rst + Writes formatted data. + + *args* is an argument list representing arbitrary arguments. + + **Example**:: + + MemoryWriter out; + out.write("Current point:\n"); + out.write("({:+f}, {:+f})", -3.14, 3.14); + + This will write the following output to the ``out`` object: + + .. code-block:: none + + Current point: + (-3.140000, +3.140000) + + The output can be accessed using :func:`data()`, :func:`c_str` or + :func:`str` methods. + + See also :ref:`syntax`. + \endrst + */ + void write(BasicStringRef format, ArgList args) { + BasicFormatter(*this).format(format, args); + } + FMT_VARIADIC_VOID(write, BasicStringRef) + + BasicWriter &operator<<(int value) { + return *this << IntFormatSpec(value); + } + BasicWriter &operator<<(unsigned value) { + return *this << IntFormatSpec(value); + } + BasicWriter &operator<<(long value) { + return *this << IntFormatSpec(value); + } + BasicWriter &operator<<(unsigned long value) { + return *this << IntFormatSpec(value); + } + BasicWriter &operator<<(LongLong value) { + return *this << IntFormatSpec(value); + } + + /** + Formats *value* and writes it to the stream. + */ + BasicWriter &operator<<(ULongLong value) { + return *this << IntFormatSpec(value); + } + + BasicWriter &operator<<(double value) { + write_double(value, FormatSpec()); + return *this; + } + + /** + Formats *value* using the general format for floating-point numbers + (``'g'``) and writes it to the stream. + */ + BasicWriter &operator<<(long double value) { + write_double(value, FormatSpec()); + return *this; + } + + /** + Writes a character to the stream. + */ + BasicWriter &operator<<(char value) { + buffer_.push_back(value); + return *this; + } + + BasicWriter &operator<<(wchar_t value) { + buffer_.push_back(internal::CharTraits::convert(value)); + return *this; + } + + /** + Writes *value* to the stream. + */ + BasicWriter &operator<<(fmt::BasicStringRef value) { + const Char *str = value.c_str(); + buffer_.append(str, str + value.size()); + return *this; + } + + template + BasicWriter &operator<<(IntFormatSpec spec) { + internal::CharTraits::convert(FillChar()); + write_int(spec.value(), spec); + return *this; + } + + template + BasicWriter &operator<<(const StrFormatSpec &spec) { + const StrChar *s = spec.str(); + // TODO: error if fill is not convertible to Char + write_str(s, std::char_traits::length(s), spec); + return *this; + } + + void clear() FMT_NOEXCEPT { buffer_.clear(); } +}; + +template +template +typename BasicWriter::CharPtr BasicWriter::write_str( + const StrChar *s, std::size_t size, const AlignSpec &spec) { + CharPtr out = CharPtr(); + if (spec.width() > size) { + out = grow_buffer(spec.width()); + Char fill = static_cast(spec.fill()); + if (spec.align() == ALIGN_RIGHT) { + std::fill_n(out, spec.width() - size, fill); + out += spec.width() - size; + } else if (spec.align() == ALIGN_CENTER) { + out = fill_padding(out, spec.width(), size, fill); + } else { + std::fill_n(out + size, spec.width() - size, fill); + } + } else { + out = grow_buffer(size); + } + std::copy(s, s + size, out); + return out; +} + +template +typename BasicWriter::CharPtr + BasicWriter::fill_padding( + CharPtr buffer, unsigned total_size, + std::size_t content_size, wchar_t fill) { + std::size_t padding = total_size - content_size; + std::size_t left_padding = padding / 2; + Char fill_char = static_cast(fill); + std::fill_n(buffer, left_padding, fill_char); + buffer += left_padding; + CharPtr content = buffer; + std::fill_n(buffer + content_size, padding - left_padding, fill_char); + return content; +} + +template +template +typename BasicWriter::CharPtr + BasicWriter::prepare_int_buffer( + unsigned num_digits, const Spec &spec, + const char *prefix, unsigned prefix_size) { + unsigned width = spec.width(); + Alignment align = spec.align(); + Char fill = static_cast(spec.fill()); + if (spec.precision() > static_cast(num_digits)) { + // Octal prefix '0' is counted as a digit, so ignore it if precision + // is specified. + if (prefix_size > 0 && prefix[prefix_size - 1] == '0') + --prefix_size; + unsigned number_size = prefix_size + spec.precision(); + AlignSpec subspec(number_size, '0', ALIGN_NUMERIC); + if (number_size >= width) + return prepare_int_buffer(num_digits, subspec, prefix, prefix_size); + buffer_.reserve(width); + unsigned fill_size = width - number_size; + if (align != ALIGN_LEFT) { + CharPtr p = grow_buffer(fill_size); + std::fill(p, p + fill_size, fill); + } + CharPtr result = prepare_int_buffer( + num_digits, subspec, prefix, prefix_size); + if (align == ALIGN_LEFT) { + CharPtr p = grow_buffer(fill_size); + std::fill(p, p + fill_size, fill); + } + return result; + } + unsigned size = prefix_size + num_digits; + if (width <= size) { + CharPtr p = grow_buffer(size); + std::copy(prefix, prefix + prefix_size, p); + return p + size - 1; + } + CharPtr p = grow_buffer(width); + CharPtr end = p + width; + if (align == ALIGN_LEFT) { + std::copy(prefix, prefix + prefix_size, p); + p += size; + std::fill(p, end, fill); + } else if (align == ALIGN_CENTER) { + p = fill_padding(p, width, size, fill); + std::copy(prefix, prefix + prefix_size, p); + p += size; + } else { + if (align == ALIGN_NUMERIC) { + if (prefix_size != 0) { + p = std::copy(prefix, prefix + prefix_size, p); + size -= prefix_size; + } + } else { + std::copy(prefix, prefix + prefix_size, end - size); + } + std::fill(p, end - size, fill); + p = end; + } + return p - 1; +} + +template +template +void BasicWriter::write_int(T value, Spec spec) { + unsigned prefix_size = 0; + typedef typename internal::IntTraits::MainType UnsignedType; + UnsignedType abs_value = value; + char prefix[4] = ""; + if (internal::is_negative(value)) { + prefix[0] = '-'; + ++prefix_size; + abs_value = 0 - abs_value; + } else if (spec.flag(SIGN_FLAG)) { + prefix[0] = spec.flag(PLUS_FLAG) ? '+' : ' '; + ++prefix_size; + } + switch (spec.type()) { + case 0: case 'd': { + unsigned num_digits = internal::count_digits(abs_value); + CharPtr p = prepare_int_buffer( + num_digits, spec, prefix, prefix_size) + 1 - num_digits; + internal::format_decimal(get(p), abs_value, num_digits); + break; + } + case 'x': case 'X': { + UnsignedType n = abs_value; + if (spec.flag(HASH_FLAG)) { + prefix[prefix_size++] = '0'; + prefix[prefix_size++] = spec.type(); + } + unsigned num_digits = 0; + do { + ++num_digits; + } while ((n >>= 4) != 0); + Char *p = get(prepare_int_buffer( + num_digits, spec, prefix, prefix_size)); + n = abs_value; + const char *digits = spec.type() == 'x' ? + "0123456789abcdef" : "0123456789ABCDEF"; + do { + *p-- = digits[n & 0xf]; + } while ((n >>= 4) != 0); + break; + } + case 'b': case 'B': { + UnsignedType n = abs_value; + if (spec.flag(HASH_FLAG)) { + prefix[prefix_size++] = '0'; + prefix[prefix_size++] = spec.type(); + } + unsigned num_digits = 0; + do { + ++num_digits; + } while ((n >>= 1) != 0); + Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size)); + n = abs_value; + do { + *p-- = '0' + (n & 1); + } while ((n >>= 1) != 0); + break; + } + case 'o': { + UnsignedType n = abs_value; + if (spec.flag(HASH_FLAG)) + prefix[prefix_size++] = '0'; + unsigned num_digits = 0; + do { + ++num_digits; + } while ((n >>= 3) != 0); + Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size)); + n = abs_value; + do { + *p-- = '0' + (n & 7); + } while ((n >>= 3) != 0); + break; + } + default: + internal::report_unknown_type( + spec.type(), spec.flag(CHAR_FLAG) ? "char" : "integer"); + break; + } +} + +template +template +void BasicWriter::write_double( + T value, const FormatSpec &spec) { + // Check type. + char type = spec.type(); + bool upper = false; + switch (type) { + case 0: + type = 'g'; + break; + case 'e': case 'f': case 'g': case 'a': + break; + case 'F': +#ifdef _MSC_VER + // MSVC's printf doesn't support 'F'. + type = 'f'; +#endif + // Fall through. + case 'E': case 'G': case 'A': + upper = true; + break; + default: + internal::report_unknown_type(type, "double"); + break; + } + + char sign = 0; + // Use getsign instead of value < 0 because the latter is always + // false for NaN. + if (internal::getsign(static_cast(value))) { + sign = '-'; + value = -value; + } else if (spec.flag(SIGN_FLAG)) { + sign = spec.flag(PLUS_FLAG) ? '+' : ' '; + } + + if (value != value) { + // Format NaN ourselves because sprintf's output is not consistent + // across platforms. + std::size_t nan_size = 4; + const char *nan = upper ? " NAN" : " nan"; + if (!sign) { + --nan_size; + ++nan; + } + CharPtr out = write_str(nan, nan_size, spec); + if (sign) + *out = sign; + return; + } + + if (internal::isinfinity(value)) { + // Format infinity ourselves because sprintf's output is not consistent + // across platforms. + std::size_t inf_size = 4; + const char *inf = upper ? " INF" : " inf"; + if (!sign) { + --inf_size; + ++inf; + } + CharPtr out = write_str(inf, inf_size, spec); + if (sign) + *out = sign; + return; + } + + std::size_t offset = buffer_.size(); + unsigned width = spec.width(); + if (sign) { + buffer_.reserve(buffer_.size() + (std::max)(width, 1u)); + if (width > 0) + --width; + ++offset; + } + + // Build format string. + enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg + Char format[MAX_FORMAT_SIZE]; + Char *format_ptr = format; + *format_ptr++ = '%'; + unsigned width_for_sprintf = width; + if (spec.flag(HASH_FLAG)) + *format_ptr++ = '#'; + if (spec.align() == ALIGN_CENTER) { + width_for_sprintf = 0; + } else { + if (spec.align() == ALIGN_LEFT) + *format_ptr++ = '-'; + if (width != 0) + *format_ptr++ = '*'; + } + if (spec.precision() >= 0) { + *format_ptr++ = '.'; + *format_ptr++ = '*'; + } + + append_float_length(format_ptr, value); + *format_ptr++ = type; + *format_ptr = '\0'; + + // Format using snprintf. + Char fill = static_cast(spec.fill()); + for (;;) { + std::size_t buffer_size = buffer_.capacity() - offset; +#if _MSC_VER + // MSVC's vsnprintf_s doesn't work with zero size, so reserve + // space for at least one extra character to make the size non-zero. + // Note that the buffer's capacity will increase by more than 1. + if (buffer_size == 0) { + buffer_.reserve(offset + 1); + buffer_size = buffer_.capacity() - offset; + } +#endif + Char *start = &buffer_[offset]; + int n = internal::CharTraits::format_float( + start, buffer_size, format, width_for_sprintf, spec.precision(), value); + if (n >= 0 && offset + n < buffer_.capacity()) { + if (sign) { + if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) || + *start != ' ') { + *(start - 1) = sign; + sign = 0; + } else { + *(start - 1) = fill; + } + ++n; + } + if (spec.align() == ALIGN_CENTER && + spec.width() > static_cast(n)) { + width = spec.width(); + CharPtr p = grow_buffer(width); + std::copy(p, p + n, p + (width - n) / 2); + fill_padding(p, spec.width(), n, fill); + return; + } + if (spec.fill() != ' ' || sign) { + while (*start == ' ') + *start++ = fill; + if (sign) + *(start - 1) = sign; + } + grow_buffer(n); + return; + } + // If n is negative we ask to increase the capacity by at least 1, + // but as std::vector, the buffer grows exponentially. + buffer_.reserve(n >= 0 ? offset + n + 1 : buffer_.capacity() + 1); + } +} + +/** + \rst + This template provides operations for formatting and writing data into + a character stream. The output is stored in a memory buffer that grows + dynamically. + + You can use one of the following typedefs for common character types + and the standard allocator: + + +---------------+-----------------------------------------------+ + | Type | Definition | + +===============+===============================================+ + | MemoryWriter | BasicWriter> | + +---------------+-----------------------------------------------+ + | WMemoryWriter | BasicWriter> | + +---------------+-----------------------------------------------+ + + **Example**:: + + MemoryWriter out; + out << "The answer is " << 42 << "\n"; + out.write("({:+f}, {:+f})", -3.14, 3.14); + + This will write the following output to the ``out`` object: + + .. code-block:: none + + The answer is 42 + (-3.140000, +3.140000) + + The output can be converted to an ``std::string`` with ``out.str()`` or + accessed as a C string with ``out.c_str()``. + \endrst + */ +template > +class BasicMemoryWriter : public BasicWriter { + private: + internal::MemoryBuffer buffer_; + + public: + explicit BasicMemoryWriter(const Allocator& alloc = Allocator()) + : BasicWriter(buffer_), buffer_(alloc) {} + +#if FMT_USE_RVALUE_REFERENCES + /** + Constructs a :class:`fmt::BasicMemoryWriter` object moving the content + of the other object to it. + */ + BasicMemoryWriter(BasicMemoryWriter &&other) + : BasicWriter(buffer_), buffer_(std::move(other.buffer_)) { + } + + /** + Moves the content of the other ``BasicMemoryWriter`` object to this one. + */ + BasicMemoryWriter &operator=(BasicMemoryWriter &&other) { + buffer_ = std::move(other.buffer_); + return *this; + } +#endif +}; + +typedef BasicMemoryWriter MemoryWriter; +typedef BasicMemoryWriter WMemoryWriter; + +// Formats a value. +template +void format(BasicFormatter &f, const Char *&format_str, const T &value) { + std::basic_ostringstream os; + os << value; + internal::Arg arg; + internal::Value &arg_value = arg; + std::basic_string str = os.str(); + arg_value = internal::MakeValue(str); + arg.type = static_cast(internal::MakeValue::type(str)); + format_str = f.format(format_str, arg); +} + +// Reports a system error without throwing an exception. +// Can be used to report errors from destructors. +void report_system_error(int error_code, StringRef message) FMT_NOEXCEPT; + +#ifdef _WIN32 + +/** A Windows error. */ +class WindowsError : public SystemError { + private: + void init(int error_code, StringRef format_str, ArgList args); + + public: + /** + \rst + Constructs a :class:`fmt::WindowsError` object with the description + of the form + + .. parsed-literal:: + **: ** + + where ** is the formatted message and ** is the system + message corresponding to the error code. + *error_code* is a Windows error code as given by ``GetLastError``. + If *error_code* is not a valid error code such as -1, the system message + will look like "error -1". + + **Example**:: + + // This throws a WindowsError with the description + // cannot open file 'madeup': The system cannot find the file specified. + // or similar (system message may vary). + const char *filename = "madeup"; + LPOFSTRUCT of = LPOFSTRUCT(); + HFILE file = OpenFile(filename, &of, OF_READ); + if (file == HFILE_ERROR) + throw fmt::WindowsError(GetLastError(), "cannot open file '{}'", filename); + \endrst + */ + WindowsError(int error_code, StringRef message) { + init(error_code, message, ArgList()); + } + FMT_VARIADIC_CTOR(WindowsError, init, int, StringRef) +}; + +// Reports a Windows error without throwing an exception. +// Can be used to report errors from destructors. +void report_windows_error(int error_code, StringRef message) FMT_NOEXCEPT; + +#endif + +enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE }; + +/** + Formats a string and prints it to stdout using ANSI escape sequences + to specify color (experimental). + Example: + PrintColored(fmt::RED, "Elapsed time: {0:.2f} seconds") << 1.23; + */ +void print_colored(Color c, StringRef format, ArgList args); + +/** + \rst + Formats arguments and returns the result as a string. + + **Example**:: + + std::string message = format("The answer is {}", 42); + \endrst +*/ +inline std::string format(StringRef format_str, ArgList args) { + MemoryWriter w; + w.write(format_str, args); + return w.str(); +} + +inline std::wstring format(WStringRef format_str, ArgList args) { + WMemoryWriter w; + w.write(format_str, args); + return w.str(); +} + +/** + \rst + Prints formatted data to the file *f*. + + **Example**:: + + print(stderr, "Don't {}!", "panic"); + \endrst + */ +void print(std::FILE *f, StringRef format_str, ArgList args); + +/** + \rst + Prints formatted data to ``stdout``. + + **Example**:: + + print("Elapsed time: {0:.2f} seconds", 1.23); + \endrst + */ +void print(StringRef format_str, ArgList args); + +/** + \rst + Prints formatted data to the stream *os*. + + **Example**:: + + print(cerr, "Don't {}!", "panic"); + \endrst + */ +void print(std::ostream &os, StringRef format_str, ArgList args); + +template +void printf(BasicWriter &w, BasicStringRef format, ArgList args) { + internal::PrintfFormatter().format(w, format, args); +} + +/** + \rst + Formats arguments and returns the result as a string. + + **Example**:: + + std::string message = fmt::sprintf("The answer is %d", 42); + \endrst +*/ +inline std::string sprintf(StringRef format, ArgList args) { + MemoryWriter w; + printf(w, format, args); + return w.str(); +} + +/** + \rst + Prints formatted data to the file *f*. + + **Example**:: + + fmt::fprintf(stderr, "Don't %s!", "panic"); + \endrst + */ +int fprintf(std::FILE *f, StringRef format, ArgList args); + +/** + \rst + Prints formatted data to ``stdout``. + + **Example**:: + + fmt::printf("Elapsed time: %.2f seconds", 1.23); + \endrst + */ +inline int printf(StringRef format, ArgList args) { + return fprintf(stdout, format, args); +} + +/** + Fast integer formatter. + */ +class FormatInt { + private: + // Buffer should be large enough to hold all digits (digits10 + 1), + // a sign and a null character. + enum {BUFFER_SIZE = std::numeric_limits::digits10 + 3}; + mutable char buffer_[BUFFER_SIZE]; + char *str_; + + // Formats value in reverse and returns the number of digits. + char *format_decimal(ULongLong value) { + char *buffer_end = buffer_ + BUFFER_SIZE - 1; + while (value >= 100) { + // Integer division is slow so do it for a group of two digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + unsigned index = (value % 100) * 2; + value /= 100; + *--buffer_end = internal::Data::DIGITS[index + 1]; + *--buffer_end = internal::Data::DIGITS[index]; + } + if (value < 10) { + *--buffer_end = static_cast('0' + value); + return buffer_end; + } + unsigned index = static_cast(value * 2); + *--buffer_end = internal::Data::DIGITS[index + 1]; + *--buffer_end = internal::Data::DIGITS[index]; + return buffer_end; + } + + void FormatSigned(LongLong value) { + ULongLong abs_value = static_cast(value); + bool negative = value < 0; + if (negative) + abs_value = 0 - abs_value; + str_ = format_decimal(abs_value); + if (negative) + *--str_ = '-'; + } + + public: + explicit FormatInt(int value) { FormatSigned(value); } + explicit FormatInt(long value) { FormatSigned(value); } + explicit FormatInt(LongLong value) { FormatSigned(value); } + explicit FormatInt(unsigned value) : str_(format_decimal(value)) {} + explicit FormatInt(unsigned long value) : str_(format_decimal(value)) {} + explicit FormatInt(ULongLong value) : str_(format_decimal(value)) {} + + /** + Returns the number of characters written to the output buffer. + */ + std::size_t size() const { return buffer_ - str_ + BUFFER_SIZE - 1; } + + /** + Returns a pointer to the output buffer content. No terminating null + character is appended. + */ + const char *data() const { return str_; } + + /** + Returns a pointer to the output buffer content with terminating null + character appended. + */ + const char *c_str() const { + buffer_[BUFFER_SIZE - 1] = '\0'; + return str_; + } + + /** + Returns the content of the output buffer as an `std::string`. + */ + std::string str() const { return std::string(str_, size()); } +}; + +// Formats a decimal integer value writing into buffer and returns +// a pointer to the end of the formatted string. This function doesn't +// write a terminating null character. +template +inline void format_decimal(char *&buffer, T value) { + typename internal::IntTraits::MainType abs_value = value; + if (internal::is_negative(value)) { + *buffer++ = '-'; + abs_value = 0 - abs_value; + } + if (abs_value < 100) { + if (abs_value < 10) { + *buffer++ = static_cast('0' + abs_value); + return; + } + unsigned index = static_cast(abs_value * 2); + *buffer++ = internal::Data::DIGITS[index]; + *buffer++ = internal::Data::DIGITS[index + 1]; + return; + } + unsigned num_digits = internal::count_digits(abs_value); + internal::format_decimal(buffer, abs_value, num_digits); + buffer += num_digits; +} +} + +#if FMT_GCC_VERSION +// Use the system_header pragma to suppress warnings about variadic macros +// because suppressing -Wvariadic-macros with the diagnostic pragma doesn't +// work. It is used at the end because we want to suppress as little warnings +// as possible. +# pragma GCC system_header +#endif + +// This is used to work around VC++ bugs in handling variadic macros. +#define FMT_EXPAND(args) args + +// Returns the number of arguments. +// Based on https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s. +#define FMT_NARG(...) FMT_NARG_(__VA_ARGS__, FMT_RSEQ_N()) +#define FMT_NARG_(...) FMT_EXPAND(FMT_ARG_N(__VA_ARGS__)) +#define FMT_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N +#define FMT_RSEQ_N() 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 + +#define FMT_CONCAT(a, b) a##b +#define FMT_FOR_EACH_(N, f, ...) \ + FMT_EXPAND(FMT_CONCAT(FMT_FOR_EACH, N)(f, __VA_ARGS__)) +#define FMT_FOR_EACH(f, ...) \ + FMT_EXPAND(FMT_FOR_EACH_(FMT_NARG(__VA_ARGS__), f, __VA_ARGS__)) + +#define FMT_ADD_ARG_NAME(type, index) type arg##index +#define FMT_GET_ARG_NAME(type, index) arg##index + +#if FMT_USE_VARIADIC_TEMPLATES +# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ + template \ + ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ + const Args & ... args) { \ + using fmt::internal::Value; \ + const Value values[fmt::internal::NonZero::VALUE] = { \ + fmt::internal::MakeValue(args)... \ + }; \ + call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \ + fmt::internal::make_type(args...), values)); \ + } +#else +// Defines a wrapper for a function taking __VA_ARGS__ arguments +// and n additional arguments of arbitrary types. +# define FMT_WRAP(Char, ReturnType, func, call, n, ...) \ + template \ + inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ + FMT_GEN(n, FMT_MAKE_ARG)) { \ + const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF_##Char)}; \ + call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \ + fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), vals)); \ + } + +# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ + inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__)) { \ + call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList()); \ + } \ + FMT_WRAP(Char, ReturnType, func, call, 1, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 2, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 3, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 4, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 5, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 6, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 7, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 8, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 9, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 10, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 11, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 12, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 13, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 14, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 15, __VA_ARGS__) +#endif // FMT_USE_VARIADIC_TEMPLATES + +/** + \rst + Defines a variadic function with the specified return type, function name + and argument types passed as variable arguments to this macro. + + **Example**:: + + void print_error(const char *file, int line, const char *format, + fmt::ArgList args) { + fmt::print("{}: {}: ", file, line); + fmt::print(format, args); + } + FMT_VARIADIC(void, print_error, const char *, int, const char *) + + ``FMT_VARIADIC`` is used for compatibility with legacy C++ compilers that + don't implement variadic templates. You don't have to use this macro if + you don't need legacy compiler support and can use variadic templates + directly:: + + template + void print_error(const char *file, int line, const char *format, + const Args & ... args) { + fmt::print("{}: {}: ", file, line); + fmt::print(format, args...); + } + \endrst + */ +#define FMT_VARIADIC(ReturnType, func, ...) \ + FMT_VARIADIC_(char, ReturnType, func, return func, __VA_ARGS__) + +#define FMT_VARIADIC_W(ReturnType, func, ...) \ + FMT_VARIADIC_(wchar_t, ReturnType, func, return func, __VA_ARGS__) + +namespace fmt { +FMT_VARIADIC(std::string, format, StringRef) +FMT_VARIADIC_W(std::wstring, format, WStringRef) +FMT_VARIADIC(void, print, StringRef) +FMT_VARIADIC(void, print, std::FILE *, StringRef) +FMT_VARIADIC(void, print, std::ostream &, StringRef) +FMT_VARIADIC(void, print_colored, Color, StringRef) +FMT_VARIADIC(std::string, sprintf, StringRef) +FMT_VARIADIC(int, printf, StringRef) +FMT_VARIADIC(int, fprintf, std::FILE *, StringRef) +} + +// Restore warnings. +#if FMT_GCC_VERSION >= 406 +# pragma GCC diagnostic pop +#endif + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + +#ifdef FMT_HEADER_ONLY +# include "format.cc" +#endif + +#endif // FMT_FORMAT_H_ diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/grid/box.h b/src/Zigzag_persistence/example/ext_zz/dionysus/grid/box.h new file mode 100644 index 0000000000..5602141cb8 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/grid/box.h @@ -0,0 +1,136 @@ +#ifndef BOX_H +#define BOX_H + +#include +#include + +#include "grid.h" +#include "vertices.h" + +namespace grid +{ + +namespace ba = boost::adaptors; + +template +class Box +{ + public: + typedef GridRef GridProxy; + typedef typename GridProxy::Vertex Position; + + struct InternalTest; + struct BoundaryTest; + struct BoundsTest; + struct PositionToVertex; + + class FreudenthalLinkIterator; + typedef boost::iterator_range FreudenthalLinkRange; + + typedef VerticesIterator VI; + typedef boost::transformed_range + > VertexRange; + + // Topology interface + typedef typename GridProxy::Index Vertex; + typedef boost::transformed_range + > Link; + + + Box(): g_(0, Position()) {} + Box(const Position& shape): + g_(0, shape), to_(shape - Position::one()) {} + Box(const Position& shape, + const Position& from, + const Position& to): + g_(0, shape), from_(from), to_(to) {} + + + const Position& from() const { return from_; } + const Position& to() const { return to_; } + Position& from() { return from_; } + Position& to() { return to_; } + Position shape() const { return to_ - from_ + Position::one(); } + const Position& grid_shape() const { return g_.shape(); } + static unsigned dimension() { return D; } + + size_t size() const { size_t c = 1; for (unsigned i = 0; i < D; ++i) c *= (to_[i] - from_[i] + 1); return c; } + + VertexRange vertices() const { return boost::iterator_range(VI::begin(from_, to_), VI::end(from_, to_)) + | ba::transformed(position_to_vertex()); } + Link link(const Position& p) const { return FreudenthalLinkRange(FreudenthalLinkIterator::begin(p), FreudenthalLinkIterator::end(p)) + | ba::filtered(bounds_test()) + | ba::transformed(position_to_vertex()); } + Link link(const Vertex& v) const { return link(position(v)); } + + Box intersect(const Box& other) const; + bool intersects(const Box& other) const; + void merge(const Box& other); + + bool contains(const Position& p) const; + bool contains(const Vertex& v) const { return contains(position(v)); } + + bool boundary(const Position& p, bool degenerate = false) const; + bool boundary(const Vertex& v, bool deg = false) const { return boundary(position(v), deg); } + Box side(unsigned axis, bool upper) const; + + InternalTest internal_test() const { return InternalTest(*this); } + BoundaryTest boundary_test() const { return BoundaryTest(*this); } + BoundsTest bounds_test() const { return BoundsTest(*this); } + PositionToVertex position_to_vertex() const { return PositionToVertex(*this); } + + + void swap(Box& other) { g_.swap(other.g_); std::swap(from_, other.from_); std::swap(to_, other.to_); } + + bool operator==(const Box& other) const { return from_ == other.from_ && to_ == other.to_; } + + template + friend std::basic_ostream& + operator<<(std::basic_ostream& out, const Box& b) { out << "Box: " << b.from_ << " - " << b.to_ << " inside " << b.g_.shape(); return out; } + + struct InternalTest + { + InternalTest(const Box& box): box_(box) {} + bool operator()(const Vertex& v) const { return !box_.boundary(v); } + const Box& box_; + }; + + struct BoundaryTest + { + BoundaryTest(const Box& box): box_(box) {} + bool operator()(const Vertex& v) const { return box_.boundary(v); } + const Box& box_; + }; + + struct BoundsTest + { + BoundsTest(const Box& box): box_(box) {} + bool operator()(const Position& p) const { return box_.contains(p); } + bool operator()(const Vertex& v) const { return box_.contains(v); } + const Box& box_; + }; + + struct PositionToVertex + { + typedef Vertex result_type; + PositionToVertex(const Box& box): box_(box) {} + Vertex operator()(Position p) const { for (unsigned i = 0; i < D; ++i) p[i] %= box_.grid_shape()[i]; return box_.g_.index(p); } + const Box& box_; + }; + + // computes position inside the box (adjusted for the wrap-around, if need be) + Position position(const Vertex& v) const { Position p = g_.vertex(v); for (unsigned i = 0; i < D; ++i) if (p[i] < from()[i]) p[i] += grid_shape()[i]; return p; } + + private: + GridProxy g_; + Position from_, to_; +}; + +} + +#include "box.hpp" + +#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/grid/box.hpp b/src/Zigzag_persistence/example/ext_zz/dionysus/grid/box.hpp new file mode 100644 index 0000000000..f3af50c047 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/grid/box.hpp @@ -0,0 +1,141 @@ +template +grid::Box +grid::Box:: +intersect(const Box& other) const +{ + Position from, to; + for (unsigned i = 0; i < D; ++i) + { + from[i] = std::max(from_[i], other.from_[i]); + to[i] = std::min(to_[i], other.to_[i]); + } + + return Box(g_, from, to); +} + +template +bool +grid::Box:: +intersects(const Box& other) const +{ + for (unsigned i = 0; i < D; ++i) + if (std::max(from_[i], other.from_[i]) > std::min(to_[i], other.to_[i])) + return false; + + return true; +} + +template +bool +grid::Box:: +contains(const Position& p) const +{ + for (unsigned i = 0; i < D; ++i) + if (p[i] > to_[i] || p[i] < from_[i]) + return false; + return true; +} + +template +bool +grid::Box:: +boundary(const Position& p, bool degenerate) const +{ + for (unsigned i = 0; i < D; ++i) + { + if (degenerate && from_[i] == to_[i]) continue; + if (p[i] == from_[i] || p[i] == to_[i]) + return true; + } + + return false; +} + +template +grid::Box +grid::Box:: +side(unsigned axis, bool upper) const +{ + Box res(*this); + + if (upper) + res.from()[axis] = res.to()[axis]; + else + res.to()[axis] = res.from()[axis]; + + return res; +} + +template +void +grid::Box:: +merge(const Box& other) +{ + for (unsigned i = 0; i < D; ++i) + { + from_[i] = std::min(from_[i], other.from_[i]); + to_[i] = std::max(to_[i], other.to_[i]); + } +} + +/* Box::FreudenthalLinkIterator */ +template +class grid::Box::FreudenthalLinkIterator: + public boost::iterator_facade +{ + typedef boost::iterator_facade Parent; + + + public: + typedef typename Parent::value_type value_type; + typedef typename Parent::difference_type difference_type; + typedef typename Parent::reference reference; + + FreudenthalLinkIterator(): loc_(0), dir_(0) {} + FreudenthalLinkIterator(const Position& p, int loc = 0, int dir = 1): + p_(p), v_(p), loc_(loc), dir_(dir) {} + + static FreudenthalLinkIterator + begin(const Position& p) { FreudenthalLinkIterator it(p); ++it; return it; } + static FreudenthalLinkIterator + end(const Position& p) { return FreudenthalLinkIterator(p, 0, -1); } + + private: + void increment(); + bool equal(const FreudenthalLinkIterator& other) const { return v_ == other.v_; } + reference dereference() const { return v_; } + + friend class ::boost::iterator_core_access; + + private: + Position p_, v_; + int loc_; + int dir_; +}; + +template +void +grid::Box::FreudenthalLinkIterator:: +increment() +{ + loc_ += dir_; + if (loc_ == (1 << D)) + { + dir_ = -1; + loc_ += dir_; + } + + for (unsigned i = 0; i < D; ++i) + if (loc_ & (1 << i)) + v_[i] = p_[i] + dir_; + else + v_[i] = p_[i]; +} diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/grid/grid.h b/src/Zigzag_persistence/example/ext_zz/dionysus/grid/grid.h new file mode 100644 index 0000000000..c63fc7c598 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/grid/grid.h @@ -0,0 +1,143 @@ +#ifndef GRID_H +#define GRID_H + +#include "point.h" + +namespace grid +{ + +template +struct Grid; + +template +struct GridRef +{ + public: + typedef C Value; + + typedef Point Vertex; + typedef size_t Index; + + public: + template + GridRef(C* data, const Point& shape, bool c_order = true): + data_(data), shape_(shape), c_order_(c_order) { set_stride(); } + + GridRef(Grid& g): + data_(g.data()), shape_(g.shape()), + c_order_(g.c_order()) { set_stride(); } + + template + C operator()(const Point& v) const { return data_[v*stride_]; } + + template + C& operator()(const Point& v) { return data_[v*stride_]; } + + C operator()(Index i) const { return data_[i]; } + C& operator()(Index i) { return data_[i]; } + + const Vertex& + shape() const { return shape_; } + + const C* + data() const { return data_; } + C* data() { return data_; } + + // Set every element to the given value + GridRef& operator=(C value) { Index s = size(); for (Index i = 0; i < s; ++i) data_[i] = value; return *this; } + GridRef& operator/=(C value) { Index s = size(); for (Index i = 0; i < s; ++i) data_[i] /= value; return *this; } + + Vertex vertex(Index idx) const { Vertex v; for (unsigned i = 0; i < D; ++i) { v[i] = idx / stride_[i]; idx %= stride_[i]; } return v; } + Index index(const Vertex& v) const { return v*stride_; } + + Index size() const { return size(shape()); } + void swap(GridRef& other) { std::swap(data_, other.data_); std::swap(shape_, other.shape_); std::swap(stride_, other.stride_); } + + bool c_order() const { return c_order_; } + + protected: + static Index + size(const Vertex& v) { Index res = 1; for (unsigned i = 0; i < D; ++i) res *= v[i]; return res; } + + void set_stride() + { + Index cur = 1; + if (c_order_) + for (unsigned i = D; i > 0; --i) { stride_[i-1] = cur; cur *= shape_[i-1]; } + else + for (unsigned i = 0; i < D; ++i) { stride_[i] = cur; cur *= shape_[i]; } + + } + void set_shape(const Vertex& v) { shape_ = v; set_stride(); } + void set_data(C* data) { data_ = data; } + void set_c_order(bool order) { c_order_ = order; } + + private: + C* data_; + Vertex shape_; + Vertex stride_; + bool c_order_; +}; + + +template +struct Grid: public GridRef +{ + public: + typedef GridRef Parent; + typedef typename Parent::Value Value; + typedef typename Parent::Index Index; + typedef Parent Reference; + + template + struct rebind { typedef Grid type; }; + + public: + template + Grid(const Point& shape, bool c_order = true): + Parent(new C[size(shape)], shape, c_order) + {} + + Grid(const Parent& g): + Parent(new C[size(g.shape())], g.shape(), + g.c_order()) { copy_data(g.data()); } + + template + Grid(const OtherGrid& g): + Parent(new C[size(g.shape())], + g.shape(), + g.c_order()) { copy_data(g.data()); } + + ~Grid() { delete[] Parent::data(); } + + template + Grid& operator=(const GridRef& other) + { + delete[] Parent::data(); + Parent::set_c_order(other.c_order()); // NB: order needs to be set before the shape, to set the stride correctly + Parent::set_shape(other.shape()); + Index s = size(shape()); + Parent::set_data(new C[s]); + copy_data(other.data()); + return *this; + } + + using Parent::data; + using Parent::shape; + using Parent::operator(); + using Parent::operator=; + using Parent::size; + + private: + template + void copy_data(const OC* data) + { + Index s = size(shape()); + for (Index i = 0; i < s; ++i) + Parent::data()[i] = data[i]; + } +}; + +} + +#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/grid/point.h b/src/Zigzag_persistence/example/ext_zz/dionysus/grid/point.h new file mode 100644 index 0000000000..0e867c34aa --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/grid/point.h @@ -0,0 +1,132 @@ +#ifndef POINT_H +#define POINT_H + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace grid +{ + +template +class Point: public boost::array, + private boost::addable< Point // Point + Point + , boost::subtractable< Point // Point - Point + , boost::dividable2< Point, Coordinate_ // Point / Coordinate + , boost::multipliable2< Point, Coordinate_ // Point * Coordinate, Coordinate * Point + > > > > +{ + public: + typedef Coordinate_ Coordinate; + typedef boost::array ArrayParent; + + typedef Point LPoint; + typedef Point UPoint; + + template + struct rebind { typedef Point type; }; + + public: + Point() { for (unsigned i = 0; i < D; ++i) (*this)[i] = 0; } + Point(const ArrayParent& a): + ArrayParent(a) {} + template Point(const Point& p) { for (size_t i = 0; i < D; ++i) (*this)[i] = p[i]; } + template Point(const T* a) { for (unsigned i = 0; i < D; ++i) (*this)[i] = a[i]; } + template Point(const std::vector& a) { for (unsigned i = 0; i < D; ++i) (*this)[i] = a[i]; } + + static + unsigned dimension() { return D; } + + static Point zero() { return Point(); } + static Point one() { Point p; for (unsigned i = 0; i < D; ++i) p[i] = 1; return p; } + + LPoint drop(int dim) const { LPoint p; unsigned c = 0; for (unsigned i = 0; i < D; ++i) { if (i == dim) continue; p[c++] = (*this)[i]; } return p; } + UPoint lift(int dim, Coordinate x) const { UPoint p; for (unsigned i = 0; i < D+1; ++i) { if (i < dim) p[i] = (*this)[i]; else if (i == dim) p[i] = x; else if (i > dim) p[i] = (*this)[i-1]; } return p; } + + using ArrayParent::operator[]; + + Point& operator+=(const Point& y) { for (unsigned i = 0; i < D; ++i) (*this)[i] += y[i]; return *this; } + Point& operator-=(const Point& y) { for (unsigned i = 0; i < D; ++i) (*this)[i] -= y[i]; return *this; } + Point& operator*=(Coordinate a) { for (unsigned i = 0; i < D; ++i) (*this)[i] *= a; return *this; } + Point& operator/=(Coordinate a) { for (unsigned i = 0; i < D; ++i) (*this)[i] /= a; return *this; } + + Point operator-() const { Point res; for (unsigned i = 0; i < D; ++i) res[i] = -(*this)[i]; return res; } + + Coordinate norm() const { return (*this)*(*this); } + + std::ostream& operator<<(std::ostream& out) const { out << (*this)[0]; for (unsigned i = 1; i < D; ++i) out << " " << (*this)[i]; return out; } + std::istream& operator>>(std::istream& in); + + friend + Coordinate operator*(const Point& x, const Point& y) { Coordinate n = 0; for (size_t i = 0; i < D; ++i) n += x[i] * y[i]; return n; } + + template + friend + Coordinate operator*(const Point& x, const Point& y) { Coordinate n = 0; for (size_t i = 0; i < D; ++i) n += x[i] * y[i]; return n; } + + private: + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { ar & boost::serialization::base_object(*this); } +}; + +template +std::istream& +Point:: +operator>>(std::istream& in) +{ + std::string point_str; + in >> point_str; // read until ' ' + std::stringstream ps(point_str); + + char x; + for (unsigned i = 0; i < dimension(); ++i) + { + ps >> (*this)[i]; + ps >> x; + } + + return in; +} + + +template +Coordinate norm2(const Point& p) +{ Coordinate res = 0; for (unsigned i = 0; i < D; ++i) res += p[i]*p[i]; return res; } + +template +std::ostream& +operator<<(std::ostream& out, const Point& p) +{ return p.operator<<(out); } + +template +std::istream& +operator>>(std::istream& in, Point& p) +{ return p.operator>>(in); } + +} + +namespace opts +{ + template + struct Traits; + + template + struct Traits< grid::Point > + { + static + std::string type_string() { return "POINT"; } + }; +} + + +#endif // POINT_H diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/grid/vertices.h b/src/Zigzag_persistence/example/ext_zz/dionysus/grid/vertices.h new file mode 100644 index 0000000000..339782e8c4 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/grid/vertices.h @@ -0,0 +1,86 @@ +#ifndef VERTICES_H +#define VERTICES_H + +#include + +namespace grid +{ + +template +class VerticesIterator: + public boost::iterator_facade, + Vertex_, + boost::forward_traversal_tag, + Vertex_, + std::ptrdiff_t> +{ + typedef boost::iterator_facade Parent; + + + public: + typedef typename Parent::value_type value_type; + typedef typename Parent::difference_type difference_type; + typedef typename Parent::reference reference; + + typedef value_type Vertex; + typedef typename Vertex::Coordinate Coordinate; + + // upper bounds are non-inclusive + VerticesIterator(const Vertex& bounds): + to_(bounds - Vertex::one()) {} + + VerticesIterator(const Vertex& pos, + const Vertex& bounds): + pos_(pos), to_(bounds - Vertex::one()) {} + + VerticesIterator(const Vertex& pos, + const Vertex& from, + const Vertex& to): + pos_(pos), from_(from), + to_(to) {} + + + static VerticesIterator + begin(const Vertex& bounds) { return VerticesIterator(bounds); } + static VerticesIterator + end(const Vertex& bounds) { Vertex e; e[0] = bounds[0]; return VerticesIterator(e, bounds); } + + static VerticesIterator + begin(const Vertex& from, const Vertex& to) { return VerticesIterator(from, from, to); } + static VerticesIterator + end(const Vertex& from, const Vertex& to) { Vertex e = from; e[0] = to[0] + 1; return VerticesIterator(e, from, to); } + + private: + void increment(); + bool equal(const VerticesIterator& other) const { return pos_ == other.pos_; } + reference dereference() const { return pos_; } + + friend class ::boost::iterator_core_access; + + private: + Vertex pos_; + Vertex from_; + Vertex to_; +}; + +} + +template +void +grid::VerticesIterator:: +increment() +{ + unsigned j = Vertex::dimension() - 1; + while (j > 0 && pos_[j] == to_[j]) + { + pos_[j] = from_[j]; + --j; + } + ++pos_[j]; +} + +#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/matrix-filtration.h b/src/Zigzag_persistence/example/ext_zz/dionysus/matrix-filtration.h new file mode 100644 index 0000000000..516f27b2d1 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/matrix-filtration.h @@ -0,0 +1,120 @@ +#pragma once + +#include +#include +#include + +namespace dionysus +{ + + +template +class MatrixFiltrationCell; + + +// adapt Matrix as a Filtration to make it possible to feed into reduction algorithms +template +class MatrixFiltration +{ + public: + using Matrix = Matrix_; + using CellValue = CellValue_; + using Dimensions = std::vector; + using Values = std::vector; + using Cell = MatrixFiltrationCell; + + + public: + MatrixFiltration(Matrix m, Dimensions dimensions, Values values): + m_(std::move(m)), + dimensions_(dimensions), + values_(values) { assert(m_->size() == dimensions_.size()); assert(m_->size() == values_.size()); } + + Cell operator[](size_t i) const { return Cell(this, i); } + size_t size() const { return m_.size(); } + + size_t index(const Cell& c) const; + + Cell begin() const { return Cell(this, 0); } + Cell end() const { return Cell(this, size()); } + + const Dimensions& dimensions() const { return dimensions_; } + const Values& values() const { return values_; } + + private: + Matrix m_; + Dimensions dimensions_; + Values values_; + + friend class MatrixFiltrationCell; +}; + + +template +class MatrixFiltrationCell +{ + public: + using MatrixFiltration = MatrixFiltration_; + using Matrix = typename MatrixFiltration::Matrix; + using Data = typename MatrixFiltration::CellValue; + using Field = typename Matrix::Field; + + template + using Entry = ChainEntry; + + template + using BoundaryChain = std::vector>; + + public: + MatrixFiltrationCell(const MatrixFiltration* mf, size_t i): + mf_(mf), i_(i) {} + + short unsigned dimension() const { return mf_->dimensions_[i_]; } + const Data& data() const { return mf_->values_[i_]; } + + bool operator==(const MatrixFiltrationCell& other) const { return i_ == other.i_; } + bool operator!=(const MatrixFiltrationCell& other) const { return i_ != other.i_; } + + BoundaryChain<> boundary() const + { + BoundaryChain<> bdry; + for (auto& entry : (mf_->m_)[i_]) + bdry.emplace_back(Entry<> { entry.e, MatrixFiltrationCell(mf_, entry.i) }); + return bdry; + } + + template + BoundaryChain boundary(const Field_& field) const + { + BoundaryChain bdry; + for (auto& entry : (mf_->m_)[i_]) + bdry.emplace_back(Entry { field.init(entry.e), MatrixFiltrationCell(mf_, entry.i) }); + return bdry; + } + + // iterator interface + MatrixFiltrationCell operator++(int) { MatrixFiltrationCell copy = *this; i_++; return copy; } + MatrixFiltrationCell& operator++() { ++i_; return *this; } + + const MatrixFiltrationCell& operator*() const { return *this; } + MatrixFiltrationCell& operator*() { return *this; } + + size_t i() const { return i_; } + + friend + std::ostream& operator<<(std::ostream& out, const MatrixFiltrationCell& c) + { out << c.i_; return out; } + + private: + const MatrixFiltration* mf_ = nullptr; + size_t i_; +}; + +template +size_t +MatrixFiltration::index(const Cell& c) const +{ + return c.i(); +} + +} diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/omni-field-persistence.h b/src/Zigzag_persistence/example/ext_zz/dionysus/omni-field-persistence.h new file mode 100644 index 0000000000..3d83d4ae44 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/omni-field-persistence.h @@ -0,0 +1,145 @@ +#ifndef DIONYSUS_OMNI_FIELD_REDUCTION_H +#define DIONYSUS_OMNI_FIELD_REDUCTION_H + +#include +#include + +#include "reduction.h" // for unpaired +#include "fields/q.h" +#include "fields/zp.h" +#include "chain.h" + +namespace dionysus +{ + +template, class Q_ = ::dionysus::Q<>, class Zp_ = ::dionysus::ZpField> +class OmniFieldPersistence +{ + public: + using Index = Index_; + using Q = Q_; + using Field = Q; + using Comparison = Comparison_; + + using BaseElement = typename Q::BaseElement; + using Zp = Zp_; + using Zps = std::unordered_map; + + using QElement = typename Q::Element; + using QEntry = ChainEntry; + using QChain = std::vector; + + using ZpElement = typename Zp::Element; + using ZpEntry = ChainEntry; + using ZpChain = std::vector; + + using QChains = std::vector; + using ZpChains = std::unordered_map>; + + using QLows = std::unordered_map; + using ZpLows = std::unordered_map>; + + using QPairs = std::vector; + using ZpPairs = std::unordered_map>; + + using Factors = std::vector; + + using Specials = std::unordered_map>; + + const Field& field() const { return q_; } + + void sort(QChain& c) { std::sort(c.begin(), c.end(), + [this](const QEntry& e1, const QEntry& e2) + { return this->cmp_(e1.index(), e2.index()); }); } + + template + void add(const ChainRange& chain) { return add(QChain(std::begin(chain), std::end(chain))); } + void add(QChain&& chain); + + void reserve(size_t s) { q_chains_.reserve(s); q_pairs_.reserve(s); } + size_t size() const { return q_pairs_.size(); } + + void reduce(ZpChain& zp_chain, BaseElement p); + ZpChain convert(const QChain& c, const Zp& field) const; + bool special(Index i, BaseElement p) const { auto it = zp_chains_.find(i); if (it == zp_chains_.end()) return false; if (it->second.find(p) == it->second.end()) return false; return true; } + Specials specials() const + { + Specials specials; + for (auto& x : zp_chains_) + for (auto& y : x.second) + specials[x.first].push_back(y.first); + return specials; + } + + const Zp& zp(BaseElement p) const { auto it = zps_.find(p); if (it != zps_.end()) return it->second; return zps_.emplace(p, Zp(p)).first->second; } + + static Factors factor(BaseElement x); + + const QChains& q_chains() const { return q_chains_; } + const ZpChains& zp_chains() const { return zp_chains_; } + + // This is a bit of a hack; it takes advantage of the fact that zp(p) + // generates field on-demand and memoizes them. So there is an entry in + // zps_ only if something special happened over the prime. + Factors primes() const { Factors result; result.reserve(zps_.size()); for (auto& x : zps_) result.push_back(x.first); return result; } + + // TODO: no skip support for now + bool skip(Index) const { return false; } + void add_skip() {} + void set_skip(Index, bool flag = true) {} + + Index pair(Index i, BaseElement p) const; + void set_pair(Index i, Index j); + void set_pair(Index i, Index j, BaseElement p); + static const Index unpaired() { return Reduction::unpaired; } + + private: + QChains q_chains_; + ZpChains zp_chains_; + + QLows q_lows_; + ZpLows zp_lows_; + + QPairs q_pairs_; + ZpPairs zp_pairs_; + + Q q_; + mutable Zps zps_; + + Comparison cmp_; +}; + +// Make OmniFieldPersistence act like a ReducedMatrix (e.g., for the purpose of constructing a persistence diagram) +template +struct PrimeAdapter +{ + using Persistence = OmniFieldPersistence; + using Prime = typename Persistence::BaseElement; + using Index = typename Persistence::Index; + + PrimeAdapter(const Persistence& persistence, Prime p): + persistence_(persistence), p_(p) {} + + bool skip(Index i) const { return persistence_.skip(i); } + + size_t size() const { return persistence_.size(); } + Index pair(Index i) const { return persistence_.pair(i, p_); } + static const Index unpaired() { return Persistence::unpaired(); } + + const Persistence& persistence_; + Prime p_; +}; + +template +PrimeAdapter +prime_adapter(const OmniFieldPersistence& persistence, + typename PrimeAdapter::Prime p) +{ + return PrimeAdapter(persistence, p); +} + +} // dionysus + +#include "omni-field-persistence.hpp" + +#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/omni-field-persistence.hpp b/src/Zigzag_persistence/example/ext_zz/dionysus/omni-field-persistence.hpp new file mode 100644 index 0000000000..68d5fbede7 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/omni-field-persistence.hpp @@ -0,0 +1,250 @@ +template +void +dionysus::OmniFieldPersistence:: +add(QChain&& chain) +{ + sort(chain); + + q_chains_.emplace_back(std::move(chain)); + q_pairs_.emplace_back(unpaired()); + Index i = q_chains_.size() - 1; + + QChain& c = q_chains_.back(); + + auto reduce = [this,&c,i](BaseElement p) + { + auto zp_chain = convert(c, zp(p)); + + this->reduce(zp_chain, p); + + if (!zp_chain.empty()) + { + auto l = zp_chain.back().index(); + zp_lows_[l].emplace(p,i); + set_pair(l,i,p); + } + + zp_chains_[i].emplace(p, std::move(zp_chain)); // empty chain is still a valid indicator that we don't need to bother with this field + }; + + // reduce + auto entry_cmp = [this](const QEntry& e1, const QEntry& e2) { return this->cmp_(e1.index(), e2.index()); }; + while (!c.empty()) + { + auto& low = c.back(); + + auto e = low.element(); + auto l = low.index(); + assert(!q_.is_zero(e)); + if (e != q_.id()) + { + auto factors = factor(q_.numerator(e)); + for (auto p : factors) + { + if (!special(i, p)) // there is already a dedicated column over p + reduce(p); + } + } + + auto it_zp = zp_lows_.find(l); + if (it_zp != zp_lows_.end()) + for (auto& x : it_zp->second) + { + auto p = x.first; + if (!special(i,p)) + reduce(p); + } + + auto it_q = q_lows_.find(l); + if (it_q != q_lows_.end()) + { + Index j = it_q->second; + + // add the primes from j to i + auto it_zp = zp_chains_.find(j); + if (it_zp != zp_chains_.end()) + for (auto& x : it_zp->second) + { + auto p = x.first; + if (!special(i,p)) + reduce(p); + } + + // reduce over Q + auto j_chain = q_chains_[j]; + auto j_e = j_chain.back().element(); + + auto m = q_.neg(q_.div(e,j_e)); + Chain::addto(c, m, j_chain, q_, entry_cmp); + assert(c.empty() || !q_.is_zero(c.back().element())); + } else + { + q_lows_.emplace(l,i); + set_pair(l,i); + break; + } + } +} + +template +void +dionysus::OmniFieldPersistence:: +reduce(ZpChain& zp_chain, BaseElement p) +{ + auto& field = zp(p); + + auto entry_cmp = [this](const ZpEntry& e1, const ZpEntry& e2) { return this->cmp_(e1.index(), e2.index()); }; + + while (!zp_chain.empty()) + { + auto& low = zp_chain.back(); + auto j = low.index(); + + auto it = zp_lows_.find(j); + if (it != zp_lows_.end()) + { + auto it2 = it->second.find(p); + if (it2 != it->second.end()) + { + const ZpChain& co = zp_chains_[it2->second][p]; + + auto m = field.neg(field.div(low.element(), co.back().element())); + assert(m < p); + Chain::addto(zp_chain, m, co, field, entry_cmp); + continue; + } + } + + auto qit = q_lows_.find(j); + if (qit == q_lows_.end() || special(qit->second, p)) // no valid pivot over Q + return; + + // TODO: this could be optimized (add and convert on the fly) + auto& q_chain = q_chains_[qit->second]; + assert(q_chain.empty() || !q_.is_zero(q_chain.back().element())); + + auto co = convert(q_chain, field); + auto m = field.neg(field.div(low.element(), co.back().element())); + Chain::addto(zp_chain, m, co, field, entry_cmp); + + assert(!zp_chain.empty() || zp_chain.back().index() != j); + } +} + +template +typename dionysus::OmniFieldPersistence::ZpChain +dionysus::OmniFieldPersistence:: +convert(const QChain& c, const Zp& field) const +{ + ZpChain result; + result.reserve(c.size()); + auto p = field.prime(); + for (auto& x : c) + { + auto num = q_.numerator(x.element()) % p; + if (num != 0) + { + while (num < 0) num += p; + auto denom = q_.denominator(x.element()) % p; + while (denom < 0) denom += p; + assert(denom % p != 0); + result.emplace_back(field.div(num, denom), x.index()); + } + } + return result; +} + + +template +typename dionysus::OmniFieldPersistence::Factors +dionysus::OmniFieldPersistence:: +factor(BaseElement x) +{ + if (x < 0) + x = -x; + Factors result; + + if (Q::is_prime(x)) + { + result.push_back(x); + return result; + } + + BaseElement p { 2 }; + while (p*p <= x) + { + if (x % p == 0) + { + result.push_back(p); + do { x /= p; } while (x % p == 0); + if (Q::is_prime(x)) + { + result.push_back(x); + break; + } + } + ++p; + } + if (x > 1) + result.push_back(x); + + return result; +} + +template +typename dionysus::OmniFieldPersistence::Index +dionysus::OmniFieldPersistence:: +pair(Index i, BaseElement p) const +{ + if (p == 1) + return q_pairs_[i]; + else + { + auto it = zp_pairs_.find(p); + if (it == zp_pairs_.end()) + return q_pairs_[i]; + else + { + auto pit = it->second.find(i); + if (pit == it->second.end()) + return q_pairs_[i]; + else + return pit->second; + } + } +} + +template +void +dionysus::OmniFieldPersistence:: +set_pair(Index i, Index j, BaseElement p) +{ + auto& pairs = zp_pairs_[p]; + pairs[i] = j; + pairs[j] = i; +} + +template +void +dionysus::OmniFieldPersistence:: +set_pair(Index i, Index j) +{ + q_pairs_[i] = j; + q_pairs_[j] = i; + + auto it = zp_chains_.find(j); + if (it == zp_chains_.end()) + return; + + auto& chains = it->second; + for (auto& x : chains) + { + auto p = x.first; + auto& chain = x.second; + if (chain.empty()) + { + zp_pairs_[p][j] = unpaired(); + zp_pairs_[p][i] = unpaired(); + } + } +} diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/opts/opts.h b/src/Zigzag_persistence/example/ext_zz/dionysus/opts/opts.h new file mode 100644 index 0000000000..1a9bbf71bf --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/opts/opts.h @@ -0,0 +1,499 @@ +/** + * Author: Dmitriy Morozov + * The interface is heavily influenced by GetOptPP (https://code.google.com/p/getoptpp/). + * The parsing logic is from ProgramOptions.hxx (https://github.com/Fytch/ProgramOptions.hxx). + * + * History: + * - 2015-06-01: added Traits<...>::type_string() for long, unsigned long + * - ... + * - 2018-04-27: replace parsing logic with the one from ProgramOptions.hxx to + * make the parser compliant with [GNU Program Argument Syntax + * Conventions](https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html) + * - 2018-05-11: add dashed_non_option(), to accept arguments that are negative numbers + */ + +#ifndef OPTS_OPTS_H +#define OPTS_OPTS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace opts { + +// Converters +template +struct Converter +{ + Converter() {} + static + bool convert(const std::string& val, T& res) + { + std::istringstream iss(val); + iss >> res; + return !iss.fail() && iss.eof(); + } +}; + +// Type +template +struct Traits +{ + static std::string type_string() { return "UNKNOWN TYPE"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "INT"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "SHORT INT"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "LONG"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "UNSIGNED INT"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "SHORT UNSIGNED INT"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "UNSIGNED LONG"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "FLOAT"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "DOUBLE"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "STRING"; } +}; + + +struct BasicOption +{ + using IsShort = std::function; + + BasicOption(char s_, + std::string l_, + std::string default_, + std::string type_, + std::string help_): + s(s_), l(l_), d(default_), t(type_), help(help_) {} + virtual ~BasicOption() {} + + int long_size() const { return l.size() + 1 + t.size(); } + + void output(std::ostream& out, int max_long) const + { + out << " "; + if (s) + out << '-' << s << ", "; + else + out << " "; + + out << "--" << l << ' '; + + if (!t.empty()) + out << t; + + for (int i = long_size(); i < max_long; ++i) + out << ' '; + + out << " " << help; + + if (!d.empty()) + { + out << " [default: " << d << "]"; + } + out << '\n'; + } + + virtual bool flag() const { return false; } + virtual bool parse(int argc, char** argv, int& i, int j, IsShort is_short); + virtual bool set(std::string arg) =0; + + char s; + std::string l; + std::string d; + std::string t; + std::string help; +}; + +// Option +template +struct OptionContainer: public BasicOption +{ + OptionContainer(char s_, + const std::string& l_, + T& var_, + const std::string& help_, + const std::string& type_ = Traits::type_string()): + BasicOption(s_, l_, default_value(var_), type_, help_), + var(&var_) {} + + static + std::string default_value(const T& def) + { + std::ostringstream oss; + oss << def; + return oss.str(); + } + + bool set(std::string s) override { return Converter::convert(s, *var); } + + T* var; +}; + +template<> +struct OptionContainer: public BasicOption +{ + OptionContainer(char s_, + const std::string& l_, + bool& var_, + const std::string& help_): + BasicOption(s_, l_, "", "", help_), + var(&var_) { *var = false; } + + bool parse(int, char**, int&, int, IsShort) override { *var = true; return true; } + bool set(std::string) override { return true; } + bool flag() const override { return true; } + + bool* var; +}; + +template +struct OptionContainer< std::vector >: public BasicOption +{ + OptionContainer(char s_, + const std::string& l_, + std::vector& var_, + const std::string& help_, + const std::string& type_ = "SEQUENCE"): + BasicOption(s_, l_, default_value(var_), type_, help_), + var(&var_), first(true) { } + + static + std::string default_value(const std::vector& def) + { + std::ostringstream oss; + oss << "("; + if (def.size()) + oss << def[0]; + for (size_t i = 1; i < def.size(); ++i) + oss << ", " << def[i]; + oss << ")"; + return oss.str(); + } + + bool set(std::string s) override + { + if (first) + { + var->clear(); + first = false; + } + + T x; + bool result = Converter::convert(s,x); + var->emplace_back(std::move(x)); + return result; + } + + std::vector* var; + mutable bool first; +}; + + +template +std::unique_ptr +Option(char s, const std::string& l, T& var, const std::string& help) { return std::unique_ptr{new OptionContainer(s, l, var, help)}; } + +template +std::unique_ptr +Option(char s, const std::string& l, T& var, + const std::string& type, const std::string& help) { return std::unique_ptr{new OptionContainer(s, l, var, help, type)}; } + +template +std::unique_ptr +Option(const std::string& l, T& var, const std::string& help) { return std::unique_ptr{new OptionContainer(0, l, var, help)}; } + +template +std::unique_ptr +Option(const std::string& l, T& var, + const std::string& type, const std::string& help) { return std::unique_ptr{new OptionContainer(0, l, var, help, type)}; } + +// PosOption +template +struct PosOptionContainer +{ + PosOptionContainer(T& var_): + var(&var_) {} + + bool parse(std::list& args) const + { + if (args.empty()) + return false; + + bool result = Converter::convert(args.front(), *var); + if (!result) + std::cerr << "error: failed to parse " << args.front() << '\n'; + args.pop_front(); + return result; + } + + T* var; +}; + +template +PosOptionContainer +PosOption(T& var) { return PosOptionContainer(var); } + + +// Options +struct Options +{ + Options(): + failed(false) {} + + inline + Options& operator>>(std::unique_ptr opt); + template + Options& operator>>(const PosOptionContainer& poc); + + operator bool() { return !failed; } + + + friend + std::ostream& + operator<<(std::ostream& out, const Options& ops) + { + int max_long = 0; + for (auto& cur : ops.options) + { + int cur_long = cur->long_size(); + if (cur_long > max_long) + max_long = cur_long; + } + + out << "Options:\n"; + for (auto& cur : ops.options) + cur->output(out, max_long); + + return out; + } + + bool parse(int argc, char** argv); + + void unrecognized_option(std::string arg) const + { + std::cerr << "error: unrecognized option " << arg << '\n'; + } + + static bool dashed_non_option(char* arg, BasicOption::IsShort is_short) + { + return arg[ 0 ] == '-' + && (std::isdigit(arg[ 1 ]) || arg[ 1 ] == '.') + && !is_short(arg[ 1 ]); + } + + private: + std::list args; + std::list> options; + bool failed; +}; + +bool +BasicOption::parse(int argc, char** argv, int& i, int j, IsShort is_short) +{ + char* argument; + char* cur_arg = argv[i]; + // -v... + if (argv[i][j] == '\0') + { + // -v data + if (i + 1 < argc && (argv[i+1][0] != '-' || Options::dashed_non_option(argv[i+1], is_short))) + { + ++i; + argument = argv[i]; + } else + { + std::cerr << "error: cannot find the argument; ignoring " << argv[i] << '\n'; + return false; + } + } else if (argv[i][j] == '=') + { + // -v=data + argument = &argv[i][j+1]; + } else if( j == 2 ) { // only for short options + // -vdata + argument = &argv[i][j]; + } else + { + std::cerr << "error: unexpected character \'" << argv[i][j] << "\' ignoring " << argv[i] << '\n'; + return false; + } + bool result = set(argument); + if (!result) + std::cerr << "error: failed to parse " << argument << " in " << cur_arg << '\n'; + return result; +} + +bool +Options::parse(int argc, char** argv) +{ + std::map short_opts; + std::map long_opts; + + for (auto& opt : options) + { + if (opt->s) + short_opts[opt->s] = opt.get(); + + long_opts[opt->l] = opt.get(); + } + + auto is_short = [&short_opts](char c) -> bool { return short_opts.find(c) != short_opts.end(); }; + + for (int i = 1; i < argc; ++i) + { + if( argv[ i ][ 0 ] == '\0' ) + continue; + if( argv[ i ][ 0 ] != '-' || dashed_non_option(argv[i], is_short)) + args.push_back(argv[i]); + else + { + // -... + if( argv[ i ][ 1 ] == '\0' ) + { + // - + args.push_back(argv[i]); + } else if( argv[ i ][ 1 ] == '-' ) + { + if( argv[ i ][ 2 ] == '\0' ) + { + // -- + while( ++i < argc ) + args.push_back(argv[i]); + } else { + // --... + char* first = &argv[ i ][ 2 ]; + char* last = first; + for(; *last != '=' && *last != '\0'; ++last); + if (first == last) + { + failed = true; + unrecognized_option(argv[i]); + } else + { + auto opt_it = long_opts.find(std::string{first,last}); + if (opt_it == long_opts.end()) + { + failed = true; + unrecognized_option(argv[i]); + } else + { + failed |= !opt_it->second->parse(argc, argv, i, last - argv[i], is_short); + } + } + } + } else + { + // -f... + auto opt_it = short_opts.find(argv[i][1]); + if (opt_it == short_opts.end()) + { + failed = true; + unrecognized_option(argv[i]); + } else if (opt_it->second->flag()) + { + opt_it->second->parse(argc, argv, i, 0, is_short); // arguments are meaningless; just sets the flag + + // -fgh + char c; + for(int j = 1; (c = argv[i][j]) != '\0'; ++j) + { + if (!std::isprint(c) || c == '-') + { + failed = true; + std::cerr << "error: invalid character\'" << c << " ignoring " << &argv[i][j] << '\n'; + break; + } + opt_it = short_opts.find(c); + if (opt_it == short_opts.end()) + { + failed = true; + unrecognized_option("-" + std::string(1, c)); + continue; + } + if (!opt_it->second->flag()) + { + failed = true; + std::cerr << "error: non-void options not allowed in option packs; ignoring " << c << '\n'; + continue; + } + opt_it->second->parse(argc, argv, i, 0, is_short); // arguments are meaningless; just sets the flag + } + } else + { + failed |= !opt_it->second->parse(argc, argv, i, 2, is_short); + } + } + } + } + + return !failed; +} + +Options& +Options::operator>>(std::unique_ptr opt) +{ + options.emplace_back(std::move(opt)); + return *this; +} + +template +Options& +Options::operator>>(const PosOptionContainer& poc) +{ + if (!failed) + failed = !poc.parse(args); + return *this; +} + +} + +#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/ordinary-persistence.h b/src/Zigzag_persistence/example/ext_zz/dionysus/ordinary-persistence.h new file mode 100644 index 0000000000..5f26bd2a1b --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/ordinary-persistence.h @@ -0,0 +1,64 @@ +#ifndef DIONYSUS_ORDINARY_PERSISTENCE_H +#define DIONYSUS_ORDINARY_PERSISTENCE_H + +#include "reduced-matrix.h" + +namespace dionysus +{ + +/* Move this into a ReducedMatrix class */ + +// Ordinary D -> R reduction +template, + template class... Visitors> +using OrdinaryPersistence = ReducedMatrix; + +// No negative optimization +template> +struct NoNegative +{ + template + struct Visitor: public EmptyVisitor + { + template + void chain_initialized(Self* matrix, Chain& c) + { + for (auto cur = std::begin(c); cur != std::end(c); ++cur) + { + Index i = cur->index(); + Index p = matrix->pair(i); + if (!(p == Self::unpaired() || (*matrix)[i].empty())) + c.erase(cur--); + } + } + }; + + template + using V2 = EmptyVisitor; +}; + +template, + template class... Visitors> +using OrdinaryPersistenceNoNegative = ReducedMatrix::template Visitor, + Visitors...>; + +// TODO: add clearing optimization (possibly bake it into the code itself) + +template, + template class... Visitors> +using FastPersistence = ReducedMatrix::template Visitor, + //Clearing::template Visitor, // FIXME + Visitors...>; + + +} + +#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/pair-recorder.h b/src/Zigzag_persistence/example/ext_zz/dionysus/pair-recorder.h new file mode 100644 index 0000000000..81c066bda6 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/pair-recorder.h @@ -0,0 +1,78 @@ +#ifndef DIONYSUS_PAIR_RECORDER_H +#define DIONYSUS_PAIR_RECORDER_H + +namespace dionysus +{ + +template +struct PairRecorder: public Persistence_ +{ + typedef Persistence_ Persistence; + typedef typename Persistence::Index Index; + + + using Persistence::Persistence; + + template + Index add(const ChainRange& chain) + { + Index p = Persistence::add(chain); + pairs_.push_back(p); + if (p != unpaired()) + pairs_[p] = pairs_.size() - 1; + + return p; + } + + Index pair(Index i) const { return pairs_[i]; } + + void resize(size_t s) { Persistence::resize(s); pairs_.resize(s, unpaired()); } + size_t size() const { return pairs_.size(); } + static const Index unpaired() { return Reduction::unpaired; } + + std::vector pairs_; +}; + +template +struct PairChainRecorder: public PairRecorder +{ + using Persistence = Persistence_; + using Parent = PairRecorder; + using Index = typename Persistence_::Index; + using Chain = typename Persistence_::Chain; + + using Parent::Parent; + + template + Index add(const ChainRange& chain) + { + auto p_chain = Persistence::add(chain, keep_cocycles); + Index p = std::get<0>(p_chain); + + pairs_.push_back(p); + chains_.emplace_back(); + + if (p != unpaired()) + { + pairs_[p] = pairs_.size() - 1; + chains_[p] = std::move(std::get<1>(p_chain)); + } + + return p; + } + + using Parent::unpaired; + + Index pair(Index i) const { return pairs_[i]; } + const Chain& chain(Index i) const { return chains_[i]; } // chain that dies at i + void resize(size_t s) { Parent::resize(s); chains_.resize(s); } + + std::vector chains_; + using Parent::pairs_; + + bool keep_cocycles = true; +}; + +} + +#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/reduced-matrix.h b/src/Zigzag_persistence/example/ext_zz/dionysus/reduced-matrix.h new file mode 100644 index 0000000000..f7a04b130d --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/reduced-matrix.h @@ -0,0 +1,170 @@ +#ifndef DIONYSUS_REDUCED_MATRIX_H +#define DIONYSUS_REDUCED_MATRIX_H + +#include +#include + +#include "chain.h" +#include "reduction.h" + +namespace dionysus +{ + +template, template class... Visitors> +class ReducedMatrix +{ + public: + typedef ReducedMatrix Self; + + typedef Field_ Field; + typedef Index_ Index; + typedef Comparison_ Comparison; + + typedef std::tuple...> VisitorsTuple; + template + using Visitor = std::tuple_element; + + typedef typename Field::Element FieldElement; + typedef ChainEntry Entry; + typedef std::vector Chain; + + typedef std::vector Chains; + typedef std::vector Indices; + typedef std::vector SkipFlags; + + public: + ReducedMatrix(const Field& field): + field_(field) {} + + ReducedMatrix(const Field& field, + const Comparison& cmp, + const Visitors&... visitors): + field_(field), + cmp_(cmp), + visitors_(visitors...) {} + + ReducedMatrix(Field&& field, + Comparison&& cmp, + Visitors&&... visitors): + field_(std::move(field)), + cmp_(std::move(cmp)), + visitors_(visitors...) {} + + ReducedMatrix(Self&& m) = default; + ReducedMatrix(const Self& m) = default; + + template class... OtherVisitors> + ReducedMatrix(ReducedMatrix&& other): + field_(other.field_), + cmp_(other.cmp_), + reduced_(std::move(other.reduced_)), + pairs_(std::move(other.pairs_)), + skip_(std::move(other.skip_)) {} + + template + Index add(const ChainRange& chain) { return add(Chain(std::begin(chain), std::end(chain))); } + Index add(Chain&& chain); + + template + void set(Index i, const ChainRange& chain) { return set(i, Chain(std::begin(chain), std::end(chain))); } + void set(Index i, Chain&& chain); + + Index reduce(Index i); + Index reduce(Chain& c) { return reduce(c, reduced_, pairs_); } + template + Index reduce(Chain& c, const ChainsLookup& chains, const LowLookup& low); + + Index reduce_upto(Index i); // TODO + + size_t size() const { return pairs_.size(); } + void clear() { Chains().swap(reduced_); Indices().swap(pairs_); } + + void sort(Chain& c) { std::sort(c.begin(), c.end(), [this](const Entry& e1, const Entry& e2) { return this->cmp_(e1.index(), e2.index()); }); } + + const Chain& operator[](Index i) const { return reduced_[i]; } + Index pair(Index i) const { return pairs_[i]; } + void set_pair(Index i, Index j) { pairs_[i] = j; pairs_[j] = i; } + + Chain& column(Index i) { return reduced_[i]; } + + bool skip(Index i) const { return skip_[i]; } + void add_skip(); + void set_skip(Index i, bool flag = true) { skip_[i] = flag; } + + const Field& field() const { return field_; } + const Comparison& cmp() const { return cmp_; } + void reserve(size_t s) { reduced_.reserve(s); pairs_.reserve(s); } + void resize(size_t s); + + const Chains& columns() const { return reduced_; } + + template + Visitor& visitor() { return std::get(visitors_); } + + static const Index unpaired() { return Reduction::unpaired; } + + private: + template class... Vs> + friend class ReducedMatrix; // let's all be friends + + public: + // Visitors::chain_initialized(c) + template + typename std::enable_if::type + visitors_chain_initialized(Chain& c) {} + + template + typename std::enable_if::type + visitors_chain_initialized(Chain& c) { std::get(visitors_).chain_initialized(this, c); visitors_chain_initialized(c); } + + // Visitors::addto(m, cl) + template + typename std::enable_if::type + visitors_addto(FieldElement m, Index cl) {} + + template + typename std::enable_if::type + visitors_addto(FieldElement m, Index cl) { std::get(visitors_).addto(this, m, cl); visitors_addto(m, cl); } + + // Visitors::reduction_finished(m, cl) + template + typename std::enable_if::type + visitors_reduction_finished() {} + + template + typename std::enable_if::type + visitors_reduction_finished() { std::get(visitors_).reduction_finished(this); visitors_reduction_finished(); } + + private: + Field field_; + Comparison cmp_; + Chains reduced_; // matrix R + Indices pairs_; + SkipFlags skip_; // indicates whether the column should be skipped (e.g., for relative homology) + VisitorsTuple visitors_; +}; + +/* Visitors */ + +// The prototypical visitor. Others may (and probably should) inherit from it. +template +struct EmptyVisitor +{ + EmptyVisitor() = default; + + template + EmptyVisitor(const EmptyVisitor&) {} + + + template + void chain_initialized(Self*, Chain& c) {} + + void addto(Self*, typename Field::Element m, Index cl) {} + void reduction_finished(Self*) {} +}; + +} + +#include "reduced-matrix.hpp" + +#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/reduced-matrix.hpp b/src/Zigzag_persistence/example/ext_zz/dionysus/reduced-matrix.hpp new file mode 100644 index 0000000000..3e4aca8f29 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/reduced-matrix.hpp @@ -0,0 +1,78 @@ +template class... V> +void +dionysus::ReducedMatrix:: +resize(size_t s) +{ + reduced_.resize(s); + pairs_.resize(s, unpaired()); + skip_.resize(s, false); +} + +template class... V> +typename dionysus::ReducedMatrix::Index +dionysus::ReducedMatrix:: +add(Chain&& chain) +{ + // TODO: skip the computation entirely if we already know this is positive (in case of the clearing optimization) + Index i = pairs_.size(); + pairs_.emplace_back(unpaired()); + reduced_.emplace_back(); + skip_.push_back(false); + + set(i, std::move(chain)); + + return reduce(i); +} + +template class... V> +void +dionysus::ReducedMatrix:: +add_skip() +{ + pairs_.emplace_back(unpaired()); + reduced_.emplace_back(); + skip_.push_back(true); +} + +template class... V> +void +dionysus::ReducedMatrix:: +set(Index i, Chain&& c) +{ + sort(c); + visitors_chain_initialized(c); + reduced_[i] = std::move(c); +} + +template class... V> +typename dionysus::ReducedMatrix::Index +dionysus::ReducedMatrix:: +reduce(Index i) +{ + Chain& c = column(i); + Index pair = reduce(c); + + if (pair != unpaired()) + pairs_[pair] = i; + + pairs_[i] = pair; + visitors_reduction_finished<>(); + + return pair; +} + +template class... V> +template +typename dionysus::ReducedMatrix::Index +dionysus::ReducedMatrix:: +reduce( Chain& c, + const ChainsLookup& chains, + const LowLookup& lows) +{ + auto entry_cmp = [this](const Entry& e1, const Entry& e2) { return this->cmp_(e1.index(), e2.index()); }; + return Reduction::reduce(c, chains, lows, field_, + [this](FieldElement m, Index cl) + { this->visitors_addto<>(m, cl); }, + entry_cmp); +} diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/reduction.h b/src/Zigzag_persistence/example/ext_zz/dionysus/reduction.h new file mode 100644 index 0000000000..2afd333d41 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/reduction.h @@ -0,0 +1,109 @@ +#ifndef DIONYSUS_REDUCTION_H +#define DIONYSUS_REDUCTION_H + +#include +#include +#include +#include +#include "chain.h" + +namespace dionysus +{ + +namespace detail +{ + +template +struct Unpaired +{ static constexpr Index value() { return std::numeric_limits::max(); } }; + +} + +template +struct Reduction +{ + typedef Index_ Index; + + template + using AddtoVisitor = std::function; + + template + struct CallToSub; + + static const Index unpaired; + + template> + static + Index reduce(Chain1& c, + const ChainsLookup& chains, + const LowLookup& lows, + const Field& field, + const AddtoVisitor& visitor = [](typename Field::Element, Index) {}, + const Comparison& cmp = Comparison()) + { + typedef typename Field::Element FieldElement; + + while (!c.empty()) + { + //auto& low = c.back(); + auto& low = *(std::prev(c.end())); + Index l = low.index(); + Index cl = lows(l); + // std::cout << "idx: " << std::get<0>(cl) << ", " << std::get<1>(cl) << "\n"; + if (cl == unpaired) + return l; + else + { + // Reduce further + auto& co = chains(cl); + auto& co_low = co.back(); + FieldElement m = field.neg(field.div(low.element(), co_low.element())); + // c += m*co + Chain::addto(c, m, co, field, cmp); + visitor(m, cl); + } + } + return unpaired; + } + + template> + static + Index reduce(Chain1& c, + const std::vector& chains, + const std::vector& lows, + const Field& field, + const AddtoVisitor& visitor = [](typename Field::Element, Index) {}, + const Comparison& cmp = Comparison()) + { + return reduce(c, + CallToSub(chains), + CallToSub(lows), + field, visitor, cmp); + } + + // This is a work-around a bug in GCC (should really be a lambda function) + template + struct CallToSub + { + CallToSub(const std::vector& items_): + items(items_) {} + const Item& operator()(Index i) const { return items[i]; } + const std::vector& items; + }; +}; + + +template +const Index +Reduction::unpaired = detail::Unpaired::value(); + +} + +#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/relative-homology-zigzag.h b/src/Zigzag_persistence/example/ext_zz/dionysus/relative-homology-zigzag.h new file mode 100644 index 0000000000..167a32779d --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/relative-homology-zigzag.h @@ -0,0 +1,84 @@ +#ifndef RELATIVE_HOMOLOGY_ZIGZAG_H +#define RELATIVE_HOMOLOGY_ZIGZAG_H + +#include +#include + +#include "zigzag-persistence.h" + +namespace dionysus +{ + +namespace ba = boost::adaptors; + +template> +class RelativeHomologyZigzag +{ + public: + typedef Field_ Field; + typedef Index_ Index; + typedef Comparison_ Comparison; + + typedef ZigzagPersistence ZZP; + typedef typename ZZP::IndexChain IndexChain; + typedef typename ZZP::FieldElement FieldElement; + typedef typename IndexChain::value_type ChainEntry; + + + typedef Comparison Cmp; + + RelativeHomologyZigzag(const Field& field, + const Comparison& cmp = Comparison()): + zzp_(field, cmp) + { + zzp_.add( IndexChain() ); // vertex w + ++zzp_op_; + ++zzp_cell_; + } + + template + void add_both(const ChainRange& chain); + + void remove_both(Index cell); + + // index of the absolute cell; chain = its boundary + template + Index add(Index cell, const ChainRange& chain); // add to the relative part + + Index remove(Index cell); // remove from the relative part + + const Field& field() const { return zzp_.field(); } + const Cmp& cmp() const { return zzp_.cmp(); } + + size_t alive_size() const { return zzp_.alive_size() - 1; } // -1 for the cone vertex + + static + const Index unpaired() { return ZZP::unpaired(); } + + private: + template + IndexChain relative_chain(Index cell, const ChainRange& chain) const; + + template + IndexChain absolute_chain(const ChainRange& chain) const; + + Index abs_index(Index idx) const { return absolute_.left.find(idx)->second; } + Index rel_index(Index idx) const { return relative_.left.find(idx)->second; } + Index decode_pair(Index pair); + + private: + ZZP zzp_; // underlying (cone) implementation + boost::bimap absolute_; // bimap between our cells and zzp absolute cells + boost::bimap relative_; // bimap between our cells and zzp relative cells + std::unordered_map op_map_; // map from zzp_op to our op + Index op_ = 0, + zzp_op_ = 0, + cell_ = 0, + zzp_cell_ = 0; +}; + +} + +#include "relative-homology-zigzag.hpp" + +#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/relative-homology-zigzag.hpp b/src/Zigzag_persistence/example/ext_zz/dionysus/relative-homology-zigzag.hpp new file mode 100644 index 0000000000..499807106c --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/relative-homology-zigzag.hpp @@ -0,0 +1,122 @@ +template +template +void +dionysus::RelativeHomologyZigzag:: +add_both(const ChainRange& chain) +{ + zzp_.add(absolute_chain(chain)); + op_map_.insert( { zzp_op_++, op_ } ); + absolute_.left.insert( { cell_, zzp_cell_++ } ); + + zzp_.add(relative_chain(cell_, chain)); + op_map_.insert( { zzp_op_++, op_ } ); + relative_.left.insert( { cell_, zzp_cell_++ } ); + + cell_++; + op_++; +} + +template +void +dionysus::RelativeHomologyZigzag:: +remove_both(Index cell) +{ + Index abs_cell = absolute_.left.find(cell)->second; + Index rel_cell = relative_.left.find(cell)->second; + + zzp_.remove(rel_cell); + zzp_.remove(abs_cell); + + absolute_.left.erase(cell); + relative_.left.erase(cell); + + op_map_.insert( { zzp_op_++, op_ } ); + op_map_.insert( { zzp_op_++, op_ } ); + + op_++; +} + +template +template +typename dionysus::RelativeHomologyZigzag::Index +dionysus::RelativeHomologyZigzag:: +add(Index cell, const ChainRange& chain) +{ + Index pair = zzp_.add(relative_chain(cell, chain)); + op_map_.insert( { zzp_op_++, op_++ } ); + relative_.left.insert( { cell, zzp_cell_++ } ); + + return decode_pair(pair); +} + + +template +typename dionysus::RelativeHomologyZigzag::Index +dionysus::RelativeHomologyZigzag:: +decode_pair(Index pair) +{ + if (pair == unpaired()) + return pair; + + Index decoded = op_map_.find(pair)->second; + op_map_.erase(pair); + return decoded; +} + +template +template +typename dionysus::RelativeHomologyZigzag::IndexChain +dionysus::RelativeHomologyZigzag:: +absolute_chain(const ChainRange& chain) const +{ + IndexChain res; + for (const auto& e : chain) + res.push_back(ChainEntry(e.element(), abs_index(e.index()))); + return res; +} + +template +template +typename dionysus::RelativeHomologyZigzag::IndexChain +dionysus::RelativeHomologyZigzag:: +relative_chain(Index cell, const ChainRange& chain) const +{ + // NB: to compute the signs correctly, + // this assumes that the cone vertex w is the last vertex in some total order + + typedef typename IndexChain::value_type ChainEntry; + + IndexChain res; + if (!chain.empty()) + { + for (const auto& e : chain) + res.push_back(ChainEntry(e.element(), rel_index(e.index()))); + + FieldElement a = field().id(); + if (chain.size() % 2 == 0) // TODO: double-check + a = field().neg(a); + res.push_back(ChainEntry(a, abs_index(cell))); // add the base space cell + } else + { + res.reserve(2); + res.push_back(ChainEntry(field().id(), abs_index(cell))); + res.push_back(ChainEntry(field().neg(field().id()), 0)); + } + return res; +} + + +template +typename dionysus::RelativeHomologyZigzag::Index +dionysus::RelativeHomologyZigzag:: +remove(Index cell) +{ + Index rel_cell = rel_index(cell); + Index pair = zzp_.remove(rel_cell); + pair = decode_pair(pair); + + op_map_.insert( { zzp_op_++, op_++ } ); + relative_.left.erase(cell); + + return pair; +} diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/rips.h b/src/Zigzag_persistence/example/ext_zz/dionysus/rips.h new file mode 100644 index 0000000000..c7ccb1189e --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/rips.h @@ -0,0 +1,147 @@ +#ifndef DIONYSUS_RIPS_H +#define DIONYSUS_RIPS_H + +#include +#include + +#include + +#include "simplex.h" + +namespace dionysus +{ + +/** + * Rips class + * + * Class providing basic operations to work with Rips complexes. It implements Bron-Kerbosch algorithm, + * and provides simple wrappers for various functions. + * + * Distances_ is expected to define types IndexType and DistanceType as well as + * provide operator()(...) which given two IndexTypes should return + * the distance between them. There should be methods begin() and end() + * for iterating over IndexTypes as well as a method size(). + */ +template > +class Rips +{ + public: + typedef Distances_ Distances; + typedef typename Distances::IndexType IndexType; + typedef typename Distances::DistanceType DistanceType; + + typedef Simplex_ Simplex; + typedef typename Simplex::Vertex Vertex; // should be the same as IndexType + typedef std::vector VertexContainer; + + typedef short unsigned Dimension; + + class Evaluator; + class Comparison; + + public: + Rips(const Distances& distances): + distances_(distances) {} + + // Calls functor f on each simplex in the k-skeleton of the Rips complex + template + void generate(Dimension k, DistanceType max, const Functor& f, + Iterator candidates_begin, Iterator candidates_end) const; + + // Calls functor f on all the simplices of the Rips complex that contain the given vertex v + template + void vertex_cofaces(IndexType v, Dimension k, DistanceType max, const Functor& f, + Iterator candidates_begin, Iterator candidates_end) const; + + // Calls functor f on all the simplices of the Rips complex that contain the given edge [u,v] + template + void edge_cofaces(IndexType u, IndexType v, Dimension k, DistanceType max, const Functor& f, + Iterator candidates_begin, Iterator candidates_end) const; + + // Calls functor f on all the simplices of the Rips complex that contain the given Simplex s + // (unlike the previous methods it does not call the functor on the Simplex s itself) + template + void cofaces(const Simplex& s, Dimension k, DistanceType max, const Functor& f, + Iterator candidates_begin, Iterator candidates_end) const; + + + /* No Iterator argument means Iterator = IndexType and the range is [distances().begin(), distances().end()) */ + template + void generate(Dimension k, DistanceType max, const Functor& f) const + { generate(k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); } + + template + void vertex_cofaces(IndexType v, Dimension k, DistanceType max, const Functor& f) const + { vertex_cofaces(v, k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); } + + template + void edge_cofaces(IndexType u, IndexType v, Dimension k, DistanceType max, const Functor& f) const + { edge_cofaces(u, v, k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); } + + template + void cofaces(const Simplex& s, Dimension k, DistanceType max, const Functor& f) const + { cofaces(s, k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); } + + + const Distances& distances() const { return distances_; } + DistanceType max_distance() const; + + DistanceType distance(const Simplex& s1, const Simplex& s2) const; + + + template + static void bron_kerbosch(VertexContainer& current, + const VertexContainer& candidates, + typename VertexContainer::const_iterator excluded, + Dimension max_dim, + const NeighborTest& neighbor, + const Functor& functor, + bool check_initial = true); + + protected: + const Distances& distances_; +}; + +template +class Rips::Evaluator +{ + public: + typedef Simplex_ Simplex; + + Evaluator(const Distances& distances): + distances_(distances) {} + + DistanceType operator()(const Simplex& s) const; + + protected: + const Distances& distances_; +}; + +template +class Rips::Comparison +{ + public: + typedef Simplex_ Simplex; + + Comparison(const Distances& distances): + eval_(distances) {} + + bool operator()(const Simplex& s1, const Simplex& s2) const + { + DistanceType e1 = eval_(s1), + e2 = eval_(s2); + if (e1 == e2) + return s1.dimension() < s2.dimension(); + + return e1 < e2; + } + + protected: + Evaluator eval_; +}; + +} + +#include "rips.hpp" + +#endif // DIONYSUS_RIPS_H diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/rips.hpp b/src/Zigzag_persistence/example/ext_zz/dionysus/rips.hpp new file mode 100644 index 0000000000..2fdda34a7a --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/rips.hpp @@ -0,0 +1,162 @@ +#include +#include +#include +#include + +#include +#include + +template +template +void +dionysus::Rips:: +generate(Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const +{ + auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; }; + + // current = empty + // candidates = everything + VertexContainer current; + VertexContainer candidates(bg, end); + bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f); +} + +template +template +void +dionysus::Rips:: +vertex_cofaces(IndexType v, Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const +{ + auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; }; + + // current = [v] + // candidates = everything - [v] + VertexContainer current; current.push_back(v); + VertexContainer candidates; + for (Iterator cur = bg; cur != end; ++cur) + if (*cur != v && neighbor(v, *cur)) + candidates.push_back(*cur); + + bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f); +} + +template +template +void +dionysus::Rips:: +edge_cofaces(IndexType u, IndexType v, Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const +{ + auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; }; + + // current = [u,v] + // candidates = everything - [u,v] + VertexContainer current; current.push_back(u); current.push_back(v); + + VertexContainer candidates; + for (Iterator cur = bg; cur != end; ++cur) + if (*cur != u && *cur != v && neighbor(v,*cur) && neighbor(u,*cur)) + candidates.push_back(*cur); + + bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f); +} + +template +template +void +dionysus::Rips:: +cofaces(const Simplex& s, Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const +{ + namespace ba = boost::adaptors; + + auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; }; + + // current = s + VertexContainer current(s.begin(), s.end()); + + // candidates = everything - s that is a neighbor of every vertex in the simplex + VertexContainer candidates; + boost::set_difference(std::make_pair(bg, end) | + ba::filtered([this,&s,&neighbor](Vertex cur) + { for (auto& v : s) + if (!neighbor(v, cur)) + return false; + }), + s, + std::back_inserter(candidates)); + + bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f, false); +} + + +template +template +void +dionysus::Rips:: +bron_kerbosch(VertexContainer& current, + const VertexContainer& candidates, + typename VertexContainer::const_iterator excluded, + Dimension max_dim, + const NeighborTest& neighbor, + const Functor& functor, + bool check_initial) +{ + if (check_initial && !current.empty()) + functor(Simplex(current)); + + if (current.size() == static_cast(max_dim) + 1) + return; + + for (auto cur = std::next(excluded); cur != candidates.end(); ++cur) + { + current.push_back(*cur); + + VertexContainer new_candidates; + for (auto ccur = candidates.begin(); ccur != cur; ++ccur) + if (neighbor(*ccur, *cur)) + new_candidates.push_back(*ccur); + size_t ex = new_candidates.size(); + for (auto ccur = std::next(cur); ccur != candidates.end(); ++ccur) + if (neighbor(*ccur, *cur)) + new_candidates.push_back(*ccur); + excluded = new_candidates.begin() + (ex - 1); + + bron_kerbosch(current, new_candidates, excluded, max_dim, neighbor, functor); + current.pop_back(); + } +} + +template +typename dionysus::Rips::DistanceType +dionysus::Rips:: +distance(const Simplex& s1, const Simplex& s2) const +{ + DistanceType mx = 0; + for (auto a : s1) + for (auto b : s2) + mx = std::max(mx, distances_(a,b)); + return mx; +} + +template +typename dionysus::Rips::DistanceType +dionysus::Rips:: +max_distance() const +{ + DistanceType mx = 0; + for (IndexType a = distances_.begin(); a != distances_.end(); ++a) + for (IndexType b = std::next(a); b != distances_.end(); ++b) + mx = std::max(mx, distances_(a,b)); + return mx; +} + +template +typename dionysus::Rips::DistanceType +dionysus::Rips::Evaluator:: +operator()(const Simplex& s) const +{ + DistanceType mx = 0; + for (auto a = s.begin(); a != s.end(); ++a) + for (auto b = std::next(a); b != s.end(); ++b) + mx = std::max(mx, distances_(*a,*b)); + return mx; +} diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/row-reduction.h b/src/Zigzag_persistence/example/ext_zz/dionysus/row-reduction.h new file mode 100644 index 0000000000..e2481ce080 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/row-reduction.h @@ -0,0 +1,54 @@ +#ifndef DIONYSUS_ROW_REDUCTION_H +#define DIONYSUS_ROW_REDUCTION_H + +#include "reduced-matrix.h" + +namespace dionysus +{ + +// Mid-level interface +template, template class... Visitors> +class RowReduction +{ + public: + typedef Field_ Field; + typedef Index_ Index; + typedef Comparison_ Comparison; + + typedef ReducedMatrix Persistence; + + public: + RowReduction(const Field& field): + persistence_(field) {} + + RowReduction(const Field& field, + const Comparison& cmp, + const Visitors&... visitors): + persistence_(field, cmp, visitors...) {} + + template + void operator()(const Filtration& f, const Relative& relative, const ReportPair& report_pair, const Progress& progress); + + template + void operator()(const Filtration& f, const ReportPair& report_pair); + + template + void operator()(const Filtration& f) { return (*this)(f, &no_report_pair); } + + static void no_report_pair(int, Index, Index) {} + static void no_progress() {} + + const Persistence& + persistence() const { return persistence_; } + Persistence& persistence() { return persistence_; } + + private: + Persistence persistence_; +}; + +} + +#include "row-reduction.hpp" + +#endif + diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/row-reduction.hpp b/src/Zigzag_persistence/example/ext_zz/dionysus/row-reduction.hpp new file mode 100644 index 0000000000..edb1652872 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/row-reduction.hpp @@ -0,0 +1,103 @@ +#include +namespace ba = boost::adaptors; + +template class... V> +template +void +dionysus::RowReduction:: +operator()(const Filtration& filtration, const ReportPair& report_pair) +{ + using Cell = typename Filtration::Cell; + (*this)(filtration, [](const Cell&) { return false; }, report_pair, &no_progress); +} + +template class... V> +template +void +dionysus::RowReduction:: +operator()(const Filtration& filtration, const Relative& relative, const ReportPair& report_pair, const Progress& progress) +{ + persistence_.resize(filtration.size()); + + typedef typename Persistence::Index Index; + typedef typename Persistence::FieldElement Element; + typedef typename Persistence::Chain Chain; + typedef typename Filtration::Cell Cell; + typedef ChainEntry CellChainEntry; + typedef ChainEntry ChainEntry; + + std::vector rows(persistence_.size()); + + auto& field = persistence_.field(); + + // fill the matrix + Index i = 0; + for(auto& c : filtration) + { + progress(); + + if (relative(c)) + { + persistence_.set_skip(i); + ++i; + continue; + } + + persistence_.set(i, c.boundary(field) | + ba::filtered([relative](const CellChainEntry& e) { return !relative(e.index()); }) | + ba::transformed([this,&filtration](const CellChainEntry& e) + { return ChainEntry(e.element(), filtration.index(e.index())); })); + if (!persistence_[i].empty()) + { + auto& x = persistence_[i].back(); + rows[x.index()].emplace_back(x.element(),i); + } + ++i; + } + + auto entry_cmp = [this](const ChainEntry& e1, const ChainEntry& e2) { return this->persistence_.cmp()(e1.index(), e2.index()); }; + + // reduce the matrix from the bottom up + for (auto it = rows.rbegin(); it != rows.rend(); ++it) + { + auto& row = *it; + Index r = rows.rend() - it - 1; + + if (row.empty()) + continue; + + // add the first column to every other column + Index c = row.front().index(); + Element e = row.front().element(); + Chain& first = persistence_.column(c); + for (size_t i = 1; i < row.size(); ++i) + { + Index cur_idx = row[i].index(); + Element cur_elem = row[i].element(); + Chain& cur = persistence_.column(cur_idx); + if (cur.empty()) // zeroed out by the clearing optimization + continue; + + Element m = field.neg(field.div(cur_elem, e)); + // cur += m*first + ::dionysus::Chain::addto(cur, m, first, field, entry_cmp); + + // update row + if (!cur.empty()) + { + ChainEntry ce = cur.back(); + auto& new_row = rows[ce.index()]; + new_row.emplace_back(ce.element(), cur_idx); + if (entry_cmp(new_row.back(), new_row.front())) + std::swap(new_row.back(), new_row.front()); + } + } + + persistence_.set_pair(r,c); + report_pair(filtration[r].dimension(), r, c); + + // zero out the corresponding column (the clearing optimization) + persistence_.column(r).clear(); + } +} + diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/simplex.h b/src/Zigzag_persistence/example/ext_zz/dionysus/simplex.h new file mode 100644 index 0000000000..4ac5cb6945 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/simplex.h @@ -0,0 +1,280 @@ +#ifndef DIONYSUS_SIMPLEX_H +#define DIONYSUS_SIMPLEX_H + +#include +#include + +//#include +#include +#include +#include + +#include "chain.h" + +namespace dionysus +{ + +struct Empty {}; + +template +class Simplex +{ + public: + typedef Vertex_ Vertex; + typedef T Data; + typedef std::unique_ptr Vertices; + + template + struct BoundaryChainIterator; + struct BoundaryIterator; + + template + using BoundaryChainRange = boost::iterator_range>; + using BoundaryRange = boost::iterator_range; + + template + using Entry = ChainEntry; + + public: + Simplex(const Data& d = Data()): + dim_(-1), data_(d) {} + + Simplex(const std::initializer_list& vertices, + Data&& d = Data()): + Simplex(vertices.size() - 1, vertices.begin(), vertices.end(), std::move(d)) + {} + + Simplex(const std::initializer_list& vertices, + const Data& d): + Simplex(vertices.size() - 1, vertices.begin(), vertices.end(), d) {} + + Simplex(short unsigned dim, Vertices&& vertices, Data&& data = Data()): + dim_(dim), vertices_(std::move(vertices)), data_(std::move(data)) { std::sort(begin(), end()); } + + template + Simplex(const VertexRange& vertices, + Data&& d = Data()): + Simplex(vertices.size() - 1, vertices.begin(), vertices.end(), std::move(d)) + {} + + template + Simplex(const VertexRange& vertices, + const Data& d): + Simplex(vertices.size() - 1, vertices.begin(), vertices.end(), d) {} + + Simplex(const Simplex& other): + Simplex(other.dim_, other.begin(), other.end(), other.data_) {} + Simplex& operator=(const Simplex& other) { dim_ = other.dim_; vertices_ = Vertices(new Vertex[dim_+1]); std::copy(other.begin(), other.end(), begin()); data_ = other.data_; return *this; } + + Simplex(Simplex&& other) noexcept: + dim_(other.dim_), + vertices_(std::move(other.vertices_)), + data_(std::move(other.data_)) {} + Simplex& operator=(Simplex&& other) = default; + + template + Simplex(short unsigned dim, + Iterator b, Iterator e, + Data&& d = Data()): + dim_(dim), + vertices_(new Vertex[dim_+1]), + data_(std::move(d)) { std::copy(b, e, begin()); std::sort(begin(), end()); } + + template + Simplex(short unsigned dim, + Iterator b, Iterator e, + const Data& d): + dim_(dim), + vertices_(new Vertex[dim_+1]), + data_(d) { std::copy(b, e, begin()); std::sort(begin(), end()); } + + short unsigned dimension() const { return dim_; } + + BoundaryRange boundary() const { return BoundaryRange(boundary_begin(), boundary_end()); } + BoundaryIterator boundary_begin() const; + BoundaryIterator boundary_end() const; + + template + BoundaryChainRange + boundary(const Field& field) const { return BoundaryChainRange(boundary_begin(field), boundary_end(field)); } + + template + BoundaryChainIterator + boundary_begin(const Field& field) const; + template + BoundaryChainIterator + boundary_end(const Field& field) const; + + const Vertex* begin() const { return vertices_.get(); } + const Vertex* end() const { return begin() + dim_ + 1; } + size_t size() const { return dim_ + 1; } + + std::pair + range() const { return std::make_pair(begin(), end()); } + + Simplex join(const Vertex& v) const { Vertices vertices(new Vertex[dim_+2]); std::copy(begin(), end(), vertices.get()); vertices[dim_+1] = v; return Simplex(dim_ + 1, std::move(vertices), Data(data_)); } + + bool operator==(const Simplex& other) const { return dim_ == other.dim_ && std::equal(begin(), end(), other.begin()); } + bool operator!=(const Simplex& other) const { return !operator==(other); } + bool operator<(const Simplex& other) const { return dim_ < other.dim_ || (dim_ == other.dim_ && std::lexicographical_compare(begin(), end(), other.begin(), other.end())); } + bool operator>(const Simplex& other) const { return other < (*this); } + + Vertex operator[](short unsigned i) const { return vertices_[i]; } + const Data& data() const { return data_; } + Data& data() { return data_; } + + friend + std::ostream& operator<<(std::ostream& out, const Simplex& s) + { out << '<' << *s.begin(); for (auto it = s.begin() + 1; it != s.end(); ++it) out << ',' << *it; out << '>'; return out; } + + private: + Vertex* begin() { return vertices_.get(); } + Vertex* end() { return begin() + dim_ + 1; } + + private: + short unsigned dim_; + //boost::compressed_pair vertices_data_; + Vertices vertices_; + Data data_; // TODO: optimize +}; + +template +size_t hash_value(const Simplex& s) { return boost::hash_range(s.begin(), s.end()); } + + +template +struct Simplex::BoundaryIterator: + public boost::iterator_adaptor, // Value + boost::use_default, + Simplex> // Reference +{ + public: + typedef const V* Iterator; + typedef Simplex Value; + + typedef boost::iterator_adaptor Parent; + + BoundaryIterator() {} + explicit BoundaryIterator(short unsigned dim, Iterator iter, Iterator bg, Iterator end): + Parent(iter), dim_(dim), bg_(bg), end_(end) {} + + Iterator begin() const { return bg_; } + + private: + friend class boost::iterator_core_access; + Value dereference() const + { + typedef std::not_equal_to NotEqualVertex; + + using std::placeholders::_1; + return Simplex(dim_ - 1, + boost::make_filter_iterator(std::bind(NotEqualVertex(), _1, *(this->base())), bg_, end_), + boost::make_filter_iterator(std::bind(NotEqualVertex(), _1, *(this->base())), end_, end_)); + } + + short unsigned dim_; + Iterator bg_; + Iterator end_; +}; + +template +template +struct Simplex::BoundaryChainIterator: + public boost::iterator_adaptor, // Derived + BoundaryIterator, + ChainEntry>, // Value + boost::use_default, + ChainEntry>> // Reference +{ + public: + typedef F Field; + typedef BoundaryIterator Iterator; + typedef ChainEntry> Value; + + typedef boost::iterator_adaptor Parent; + + BoundaryChainIterator() {} + explicit BoundaryChainIterator(const Field& field, Iterator iter): + Parent(iter), field_(&field) {} + + private: + friend class boost::iterator_core_access; + Value dereference() const + { + return Value(((this->base().base() - this->base().begin()) % 2 == 0)? field_->id() : field_->neg(field_->id()), + *(this->base())); + } + + const Field* field_ = nullptr; +}; + + +/* Simplex */ +template +typename Simplex::BoundaryIterator +Simplex:: +boundary_begin() const +{ + if (dimension() == 0) return boundary_end(); + return BoundaryIterator(dimension(), begin(), begin(), end()); +} + +template +typename Simplex::BoundaryIterator +Simplex:: +boundary_end() const +{ + return BoundaryIterator(dimension(), end(), begin(), end()); +} + +template +template +#if defined(_MSC_VER) +typename Simplex::BoundaryChainIterator +#else +typename Simplex::template BoundaryChainIterator +#endif +Simplex:: +boundary_begin(const F& field) const +{ + if (dimension() == 0) return boundary_end(field); + return BoundaryChainIterator(field, boundary_begin()); +} + +template +template +#if defined(_MSC_VER) +typename Simplex::BoundaryChainIterator +#else +typename Simplex::template BoundaryChainIterator +#endif +Simplex:: +boundary_end(const F& field) const +{ + return BoundaryChainIterator(field, boundary_end()); +} + +} // dionysus + +namespace std +{ + +template +struct hash> +{ + size_t operator()(const dionysus::Simplex& s) const { return hash_value(s); } +}; + +} // std + +#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/sparse-row-matrix.h b/src/Zigzag_persistence/example/ext_zz/dionysus/sparse-row-matrix.h new file mode 100644 index 0000000000..fb1e929e02 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/sparse-row-matrix.h @@ -0,0 +1,184 @@ +#ifndef DIONYSUS_SPARSE_ROW_MATRIX_H +#define DIONYSUS_SPARSE_ROW_MATRIX_H + +#include +#include +#include +#include // for debugging output + +#include + +#include "chain.h" +#include "reduction.h" + +namespace dionysus +{ + +namespace bi = boost::intrusive; + +namespace detail +{ + typedef bi::list_base_hook> auto_unlink_hook; + + template + struct SparseRowMatrixEntry: + public ChainEntry, auto_unlink_hook> + { + typedef I Index; + typedef typename F::Element FieldElement; + typedef std::tuple IndexPair; // (id, pair) + typedef ChainEntry Parent; + typedef SparseRowMatrixEntry Entry; + + SparseRowMatrixEntry(FieldElement e, const IndexPair& ip): + Parent(e,ip) {} + + SparseRowMatrixEntry(FieldElement e, const Index& r, const Index& c): + Parent(e,IndexPair(r,c)) {} + + SparseRowMatrixEntry(const Entry& other) = default; + SparseRowMatrixEntry(Entry&& other) = default; + Entry& operator=(Entry&& other) = default; + + void unlink() { auto_unlink_hook::unlink(); } + bool is_linked() const { return auto_unlink_hook::is_linked(); } + }; +} + +template, + template class Column_ = std::vector> +class SparseRowMatrix +{ + public: + typedef Field_ Field; + typedef Index_ Index; + typedef Comparison_ Comparison; + + typedef typename Field::Element FieldElement; + + typedef detail::SparseRowMatrixEntry Entry; + typedef Column_ Column; + typedef typename Entry::IndexPair IndexPair; + typedef bi::list> Row; + + typedef std::vector> IndexChain; + + typedef std::unordered_map Columns; + typedef std::unordered_map Rows; + typedef std::unordered_map LowMap; + + public: + SparseRowMatrix(const Field& field, + const Comparison& cmp = Comparison()): + field_(field), cmp_(cmp) {} + + SparseRowMatrix(SparseRowMatrix&& other) = default; + + + template + Column reduce(const ChainRange& chain, IndexChain& trail); + + Index set(Index i, Column&& chain); // returns previous column with this low + void fix(Index c, Column& column); + void fix(Index c) { fix(c, col(c)); } + + const Row& prepend_row(Index r, FieldElement m, const Row& chain); // could be horribly inefficient if Column is chosen poorly + + void drop_row(Index r) { rows_.erase(r); if (is_low(r)) lows_.erase(r); } + void drop_col(Index c) + { + auto cit = columns_.find(c); + Column& column = cit->second; + if (!column.empty()) + { + Index rlow = std::get<0>(column.back().index()); + auto it = lows_.find(rlow); + if (it != lows_.end() && it->second == c) + lows_.erase(it); + } + columns_.erase(cit); + } + void drop_low(Index r) { lows_.erase(r); } + + // accessors + Row& row(Index r) { return rows_[r]; } + Column& col(Index c) { assert(col_exists(c)); return columns_.find(c)->second; } + const Column& col(Index c) const { assert(col_exists(c)); return columns_.find(c)->second; } + Index low(Index r) const { return lows_.find(r)->second; } + bool is_low(Index r) const { return lows_.find(r) != lows_.end(); } + void update_low(Index c) { lows_[std::get<0>(col(c).back().index())] = c; } + + const Field& field() const { return field_; } + void reserve(size_t) {} // here for compatibility only + const Comparison& cmp() const { return cmp_; } + + // debug + bool col_exists(Index c) const { return columns_.find(c) != columns_.end(); } + const Columns& columns() const { return columns_; } + void check_columns() const + { + for (auto& x : columns_) + { + Index c = x.first; + if (x.second.empty()) + std::cout << "Warning: empty column " << c << std::endl; + Index rl = std::get<0>(x.second.back().index()); + if (!is_low(rl) || low(rl) != c) + { + std::cout << "Columns don't check out: lows don't match" << std::endl; + std::cout << " " << c << ' ' << rl << ' ' << ' ' << low(rl) << std::endl; + std::cout << "---\n"; + for (auto& x : col(c)) + std::cout << " " << x.element() << ' ' << std::get<0>(x.index()) << ' ' << std::get<1>(x.index()) << '\n'; + std::cout << "---\n"; + for (auto& x : col(low(rl))) + std::cout << " " << x.element() << ' ' << std::get<0>(x.index()) << ' ' << std::get<1>(x.index()) << '\n'; + assert(0); + } + + for (auto& x : lows_) + { + if (!col_exists(x.second)) + { + std::cout << "Still keeping low of a removed column" << std::endl; + assert(0); + } + else if (std::get<0>(col(x.second).back().index()) != x.first) + { + std::cout << "Low mismatch: " << x.second << ' ' << std::get<0>(col(x.second).back().index()) << ' ' << x.first << '\n'; + assert(0); + } + } + } + } + + private: + Field field_; + Comparison cmp_; + + Columns columns_; + Rows rows_; + LowMap lows_; // column that has this low +}; + + +namespace detail +{ + +template +struct Unpaired> +{ + static + constexpr std::tuple + value() + { return std::make_tuple(std::numeric_limits::max(), + std::numeric_limits::max()); } +}; + +} + +} + +#include "sparse-row-matrix.hpp" + +#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/sparse-row-matrix.hpp b/src/Zigzag_persistence/example/ext_zz/dionysus/sparse-row-matrix.hpp new file mode 100644 index 0000000000..10f4808c17 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/sparse-row-matrix.hpp @@ -0,0 +1,103 @@ +template class Col> +template +typename dionysus::SparseRowMatrix::Column +dionysus::SparseRowMatrix:: +reduce(const ChainRange& chain_, IndexChain& trail) +{ + auto row_cmp = [this](const Entry& e1, const Entry& e2) + { return this->cmp_(std::get<0>(e1.index()), std::get<0>(e2.index())); }; + +#define __DIONYSUS_USE_VECTOR_CHAINS 1 + +#if !(__DIONYSUS_USE_VECTOR_CHAINS) + std::set chain(row_cmp); + for (auto x : chain_) + chain.insert(Entry(x.element(), IndexPair(x.index(), 0))); +#else + Column chain; + for (auto x : chain_) + chain.emplace_back(x.element(), IndexPair(x.index(), 0)); + std::sort(chain.begin(), chain.end(), row_cmp); +#endif + + typedef Reduction ReductionIP; + + auto chains = [this](const IndexPair& rc) -> const Column& { return this->col(std::get<1>(rc)); }; + auto lows = [this](const IndexPair& rc) -> IndexPair + { + Index r = std::get<0>(rc); + auto it = this->lows_.find(r); + if (it == this->lows_.end()) + return ReductionIP::unpaired; + else + { + Index rr = std::get<0>(col(it->second).back().index()); + if (rr != r) + std::cout << "Mismatch: " << rr << ' ' << r << std::endl; + return IndexPair(r, it->second); + } + }; + + auto addto = [&trail](FieldElement m, const IndexPair& rc) { trail.emplace_back(m, std::get<1>(rc)); }; + + ReductionIP::reduce(chain, + chains, lows, + field_, addto, row_cmp); + +#if !(__DIONYSUS_USE_VECTOR_CHAINS) + return Column(std::begin(chain), std::end(chain)); +#else + return chain; +#endif +} + +template class Col> +typename dionysus::SparseRowMatrix::Index +dionysus::SparseRowMatrix:: +set(Index col, Column&& chain) +{ + Column& column = columns_.emplace(col, std::move(chain)).first->second; + + fix(col, column); + + Index r = std::get<0>(column.back().index()); + Index res; + if (is_low(r)) + res = low(r); + else + res = col; + lows_[r] = col; + + return res; +} + +template class Col> +void +dionysus::SparseRowMatrix:: +fix(Index col, Column& column) +{ + for (auto& x : column) + { + std::get<1>(x.index()) = col; + Index r = std::get<0>(x.index()); + row(r).push_back(x); + } +} + +template class Col> +const typename dionysus::SparseRowMatrix::Row& +dionysus::SparseRowMatrix:: +prepend_row(Index r, FieldElement m, const Row& chain) +{ + Row& new_row = row(r); + + for (auto& x : chain) + { + Index c = std::get<1>(x.index()); + Column& column = col(c); + auto it = column.emplace(column.begin(), field().mul(x.element(), m), r, c); + new_row.push_back(*it); + } + + return new_row; +} diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/standard-reduction.h b/src/Zigzag_persistence/example/ext_zz/dionysus/standard-reduction.h new file mode 100644 index 0000000000..0477d4683f --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/standard-reduction.h @@ -0,0 +1,44 @@ +#ifndef DIONYSUS_STANDARD_REDUCTION_H +#define DIONYSUS_STANDARD_REDUCTION_H + +namespace dionysus +{ + +// Mid-level interface +template +class StandardReduction +{ + public: + typedef Persistence_ Persistence; + typedef typename Persistence::Field Field; + typedef typename Persistence::Index Index; + + public: + StandardReduction(Persistence& persistence): + persistence_(persistence) {} + + template + void operator()(const Filtration& f, const Relative& relative, const ReportPair& report_pair, const Progress& progress); + + template + void operator()(const Filtration& f, const ReportPair& report_pair); + + template + void operator()(const Filtration& f) { return (*this)(f, &no_report_pair); } + + static void no_report_pair(int, Index, Index) {} + static void no_progress() {} + + const Persistence& + persistence() const { return persistence_; } + Persistence& persistence() { return persistence_; } + + private: + Persistence& persistence_; +}; + +} + +#include "standard-reduction.hpp" + +#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/standard-reduction.hpp b/src/Zigzag_persistence/example/ext_zz/dionysus/standard-reduction.hpp new file mode 100644 index 0000000000..9aa3396a8c --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/standard-reduction.hpp @@ -0,0 +1,47 @@ +#include +namespace ba = boost::adaptors; + +template +template +void +dionysus::StandardReduction

:: +operator()(const Filtration& filtration, const ReportPair& report_pair) +{ + using Cell = typename Filtration::Cell; + (*this)(filtration, [](const Cell&) { return false; }, report_pair, no_progress); +} + +template +template +void +dionysus::StandardReduction

:: +operator()(const Filtration& filtration, const Relative& relative, const ReportPair& report_pair, const Progress& progress) +{ + persistence_.reserve(filtration.size()); + + typedef typename Filtration::Cell Cell; + typedef ChainEntry CellChainEntry; + typedef ChainEntry ChainEntry; + + unsigned i = 0; + for(auto& c : filtration) + { + progress(); + + if (relative(c)) + { + ++i; + persistence_.add_skip(); + continue; + } + + //std::cout << "Adding: " << c << " : " << boost::distance(c.boundary(persistence_.field())) << std::endl; + Index pair = persistence_.add(c.boundary(persistence_.field()) | + ba::filtered([relative](const CellChainEntry& e) { return !relative(e.index()); }) | + ba::transformed([this,&filtration](const CellChainEntry& e) + { return ChainEntry(e.element(), filtration.index(e.index())); })); + if (pair != persistence_.unpaired()) + report_pair(c.dimension(), pair, i); + ++i; + } +} diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/trails-chains.h b/src/Zigzag_persistence/example/ext_zz/dionysus/trails-chains.h new file mode 100644 index 0000000000..f18ff897e4 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/trails-chains.h @@ -0,0 +1,17 @@ +#ifndef DIONYSUS_TRAILS_CHAINS_H +#define DIONYSUS_TRAILS_CHAINS_H + +#include "ordinary-persistence.h" + +template +struct ChainsVisitor: public EmptyVisitor +{ + template + void chain_initialized(Chain& c) { } + + void addto(typename Field::Element m, Index cl) {} + void reduction_finished() {} +}; + + +#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/zigzag-persistence.h b/src/Zigzag_persistence/example/ext_zz/dionysus/zigzag-persistence.h new file mode 100644 index 0000000000..e9423099aa --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/zigzag-persistence.h @@ -0,0 +1,142 @@ +#ifndef DIONYSUS_ZIGZAG_PERSISTENCE_H +#define DIONYSUS_ZIGZAG_PERSISTENCE_H + +#include +#include +#include + +#include +#include + +#include "sparse-row-matrix.h" + +namespace dionysus +{ + +namespace ba = boost::adaptors; + +template> +class ZigzagPersistence +{ + static_assert(std::is_signed::value, "Index type used in ZigzagPersistence must be a *signed* integer"); + + public: + typedef Field_ Field; + typedef Index_ Index; + typedef Comparison_ Comparison; + + typedef SparseRowMatrix RowMatrix; + typedef SparseRowMatrix DequeRowMatrix; + typedef typename RowMatrix::IndexPair IndexPair; + typedef typename RowMatrix::FieldElement FieldElement; + typedef typename RowMatrix::IndexChain IndexChain; + typedef typename RowMatrix::Column Column; + typedef typename RowMatrix::Row Row; + typedef typename DequeRowMatrix::Column DequeColumn; + typedef typename DequeRowMatrix::Row DequeRow; + + typedef std::unordered_map BirthIndexMap; + + + ZigzagPersistence(const Field& field, + const Comparison& cmp = Comparison()): + Z(field, cmp), C(field, cmp), B(field, cmp), + operations(0), + cell_indices(0), + z_indicies_last(0), + z_indicies_first(-1), + b_indices(0) {} + + template + Index add(const ChainRange& chain) // returns the id of the dying cycle (or unpaired) + { + Index res = add_impl(chain); +#ifdef DIONYSUS_ZIGZAG_DEBUG + check_sorted(); + check_b_cols(); + Z.check_columns(); +#endif + return res; + } + Index remove(Index cell) + { + Index res = remove_impl(cell); +#ifdef DIONYSUS_ZIGZAG_DEBUG + check_sorted(); + check_b_cols(); + Z.check_columns(); +#endif + return res; + } + + struct IsAlive + { + IsAlive(const ZigzagPersistence& zz_): zz(&zz_) {} + bool operator()(const std::pair& x) const { return zz->is_alive(x.first); } + const ZigzagPersistence* zz; + }; + + bool is_alive(Index x) const { return !B.is_low(x); } + + auto alive_ops() const -> decltype(BirthIndexMap() | ba::filtered(IsAlive(*this)) | ba::map_values) + { return birth_index | ba::filtered(IsAlive(*this)) | ba::map_values; } + + auto alive_cycles() const -> decltype(BirthIndexMap() | ba::filtered(IsAlive(*this)) | ba::map_keys) + { return birth_index | ba::filtered(IsAlive(*this)) | ba::map_keys; } + + size_t alive_size() const { return Z.columns().size() - B.columns().size(); } + + void reserve(size_t) {} // here for compatibility only + const Field& field() const { return Z.field(); } + const Comparison& cmp() const { return Z.cmp(); } + + template + static Index row(const Entry& e) { return std::get<0>(e.index()); } + template + static Index col(const Entry& e) { return std::get<1>(e.index()); } + + static + const Index unpaired() { return Reduction::unpaired; } + + const Column& cycle(Index i) const { return Z.col(i); } + + // debug + void check_b_cols() const; + + template + void check_boundaries(const SimplexToIndex& s2i, const IndexToSimplex& i2s) const; + template + void check_cycles(const SimplexToIndex& s2i, const IndexToSimplex& i2s) const; + + Column zb_dot(Index c) const; + + template + Column dc_dot(Index c, const SimplexToIndex& s2i, const IndexToSimplex& i2s) const; + + template + Column boundary(Index i, const SimplexToIndex& s2i, const IndexToSimplex& i2s) const; + + void check_sorted() const; + + private: + template + Index add_impl(const ChainRange& chain); + Index remove_impl(Index cell); + + private: + RowMatrix Z, C; + DequeRowMatrix B; + + BirthIndexMap birth_index; + Index operations; + Index cell_indices; + Index z_indicies_last, z_indicies_first; + Index b_indices; +}; + +} + +#include "zigzag-persistence.hpp" + +#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/zigzag-persistence.hpp b/src/Zigzag_persistence/example/ext_zz/dionysus/zigzag-persistence.hpp new file mode 100644 index 0000000000..96331a3b15 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/dionysus/zigzag-persistence.hpp @@ -0,0 +1,541 @@ +#include + +template +template +typename dionysus::ZigzagPersistence::Index +dionysus::ZigzagPersistence:: +add_impl(const ChainRange& chain_) +{ + // std::cout << "add(" << cell_indices << ")" << std::endl; + Index op = operations++; + + IndexChain cycles; // chain_ -> Z*cycles + Column z_remainder = Z.reduce(chain_, cycles); + // std::cout << "cycle: "; + // for (auto& v : cycles){ + // std::cout << v.index() << " "; + // } + // std::cout << "\n"; + assert(z_remainder.empty()); + + IndexChain boundaries; + DequeColumn b_remainder = B.reduce(cycles, boundaries); + + // add up columns of C indexed by boundaries + typedef typename Column::value_type Entry; + auto row_cmp = [this](const Entry& e1, const Entry& e2) + { return this->cmp()(row(e1), row(e2)); }; + Column chain; + for (auto& x : boundaries) + Chain::addto(chain, x.element(), C.col(x.index()), field(), row_cmp); + chain.push_back(Entry(field().neg(field().id()), IndexPair(cell_indices++,0))); + + if (b_remainder.empty()) // birth + { + // std::cout << " birth" << std::endl; + Index z_col = z_indicies_last++; + Z.set(z_col, std::move(chain)); + birth_index[z_col] = op; + return unpaired(); + } + else // death + { + // std::cout << " death" << std::endl; + Index b_col = b_indices++; + Index pair = row(b_remainder.back()); + B.set(b_col, std::move(b_remainder)); + C.set(b_col, std::move(chain)); + return birth_index[pair]; + } +} + +template +typename dionysus::ZigzagPersistence::Index +dionysus::ZigzagPersistence:: +remove_impl(Index cell) +{ + //std::cout << "remove(" << cell << ")" << std::endl; + + Index op = operations++; + + typedef typename Column::value_type Entry; + auto row_cmp = [this](const Entry& e1, const Entry& e2) + { return this->cmp()(row(e1), row(e2)); }; + typedef typename DequeColumn::value_type DequeEntry; + auto b_row_cmp = [this](const DequeEntry& e1, const DequeEntry& e2) + { return this->cmp()(row(e1), row(e2)); }; + + IndexChain z_row; + for (auto& x : Z.row(cell)) + z_row.emplace_back(x.element(), col(x)); + + if (z_row.empty()) // birth + { + //std::cout << " birth" << std::endl; + Row& c_row = C.row(cell); + // c_row.front() may not be the first column in order, but that doesn't really matter, does it? (TODO) + auto& c_front = c_row.front(); + + Index j = col(c_front); + Index l = row(B.col(j).back()); + + //std::cout << j << ' ' << l << std::endl; + + // cycle = ZB[j] = DC[j] + Column cycle; + for (auto& x : B.col(j)) + Chain::addto(cycle, x.element(), Z.col(row(x)), field(), row_cmp); + + //std::cout << "Cycle:" << std::endl; + //for (auto& x : cycle) + // std::cout << x.element() << ' ' << row(x) << std::endl; + + // 1: prepend the cycle + Index znew = z_indicies_first--; + Index oth = Z.set(znew, std::move(cycle)); // oth records our collision (used in step 6) + birth_index[znew] = op; + + //std::cout << "znew oth: " << znew << ' ' << oth << std::endl; + //std::cout << "oth column:" << std::endl; + //for (auto& x : Z.col(oth)) + // std::cout << x.element() << ' ' << row(x) << std::endl; + + // 2: prepend the row to B + FieldElement m = field().neg(field().inv(c_front.element())); // m = -1/c + const DequeRow& b_row = B.prepend_row(znew, m, c_row); + //std::cout << "Prepended row with multiplier: " << m << " (" << b_row.size() << ")" << std::endl; + + // 3: subtract C[j] from every C[k] + const Column& Cj = C.col(j); + + // use the copy of c_row in B, since c_row will be modified in the following loop + for (auto it = std::next(b_row.begin()); it != b_row.end(); ++it) + { + Index c = col(*it); + assert(c != j); + //std::cout << "adding to " << c << " in C" << std::endl; + Chain::addto(C.col(c), it->element(), Cj, field(), row_cmp); // using it->element() since b_row = m*c_row + C.fix(c); // old elements got removed via auto_unlink_hook + // we don't need lows in C, so not updating them + } + //std::cout << "Done with step 3" << std::endl; + + // 4: subtract B[j] from every B[k] that has l + // (we don't need to update C because ZB[j] = 0 after step 2) + DequeColumn& Bj = B.col(j); + FieldElement bm = field().neg(field().inv(Bj.back().element())); // bm = -1/B[l,j] + IndexChain Bl_row; // make a copy of Bl_row, since it will be changing + for (auto& x : B.row(l)) + { + if (col(x) == j) + continue; + Bl_row.emplace_back(x.element(), col(x)); + } + for (auto& x : Bl_row) + { + Index c = x.index(); + assert(c != j); + Chain::addto(B.col(c), field().mul(bm, x.element()), Bj, field(), b_row_cmp); + B.fix(c); // old elements got removed via auto_unlink_hook + // l cannot be the low in c, so no need to update lows + } + //std::cout << "Done with step 4" << std::endl; + + // 5: drop row l and column j from B; drop column l from Z; drop column j from C + B.drop_col(j); + assert(B.row(l).empty()); + B.drop_row(l); + Index Zl_low = row(Z.col(l).back()); + Z.drop_col(l); + birth_index.erase(l); + C.drop_col(j); + assert(Z.row(cell).empty()); + assert(C.row(cell).empty()); + C.drop_row(cell); + Z.drop_row(cell); + //std::cout << "Done with step 5" << std::endl; + if (oth == l) // we just dropped our collision in Z + oth = znew; + else + Z.drop_low(Zl_low); + + // 6: reduce Z + std::unordered_map b_changes; // the columns to add in B to apply row changes + Index cur = znew; + while (oth != cur) + { + Column& cur_col = Z.col(cur); + Column& oth_col = Z.col(oth); + assert(row(cur_col.back()) == row(oth_col.back())); + //std::cout << "--- " << cur << " (" << cur_col.size() << ") " << oth << " (" << oth_col.size() << ")" << std::endl; + FieldElement m1 = cur_col.back().element(); + FieldElement m2 = oth_col.back().element(); + FieldElement m2_div_m1 = field().div(m2, m1); + Chain::addto(oth_col, field().neg(m2_div_m1), cur_col, field(), row_cmp); + Z.fix(oth, oth_col); + + // record the changes we need to make in B; + // because there is only one collision in the matrix during the reduction, + // once we use a row as the source, we never revisit it. This means once the row is updated in B, + // we never touch it again, so below record is fine. + for (auto& x : this->B.row(oth)) + b_changes[col(x)].emplace_back(field().mul(x.element(), m2_div_m1), cur, col(x)); + + cur = oth; + Index low = row(oth_col.back()); + if (Z.is_low(low)) + oth = Z.low(low); + //std::cout << "--- -- new low: " << low << ' ' << cur << ' ' << oth << std::endl; + + if (cmp()(oth, cur)) + std::swap(oth, cur); + else + Z.update_low(cur); + } + + // apply changes in B (the complexity here could get ugly) + for (auto& bx : b_changes) + { + std::sort(bx.second.begin(), bx.second.end(), b_row_cmp); + Chain::addto(B.col(bx.first), field().id(), bx.second, field(), b_row_cmp); + B.fix(bx.first); + // no need to update low (additions from bottom up) + } + //std::cout << "Done with step 6" << std::endl; + + return unpaired(); + } + else // death + { + //std::cout << " death" << std::endl; + + auto index_chain_cmp = [this](const typename IndexChain::value_type& e1, const typename IndexChain::value_type& e2) + { return this->cmp()(e1.index(), e2.index()); }; + + // 1: change basis to clear z_row + std::sort(z_row.begin(), z_row.end(), index_chain_cmp); // this adds a log factor, but it makes life easier + Index j = z_row.front().index(); + FieldElement e = z_row.front().element(); + + if (z_row.size() > 1) + { + // figure out the columns we use for reduction + typedef typename IndexChain::const_iterator RowIterator; + std::vector reducers; + reducers.push_back(z_row.begin()); + for (RowIterator it = std::next(z_row.begin()); it != z_row.end(); ++it) + { + Index c = it->index(); + + assert(Z.col_exists(c)); + assert(Z.col_exists(reducers.back()->index())); + if (cmp()(row(Z.col(c).back()), + row(Z.col(reducers.back()->index()).back()))) + reducers.push_back(it); + } + reducers.push_back(z_row.end()); + //std::cout << "reducers.size(): " << reducers.size() << std::endl; + //std::cout << "z_row.size(): " << z_row.size() << std::endl; + + + std::map b_changes; // the rows to add to B + auto add_in_z = [this,&b_changes,&row_cmp,&index_chain_cmp](Index to, Index from, FieldElement m, FieldElement e) + { + //std::cout << " add_in_z: " << from << ' ' << to << std::endl; + + FieldElement mult = this->field().mul(m, e); + assert(Z.col_exists(to)); + assert(Z.col_exists(from)); + Chain::addto(Z.col(to), mult, Z.col(from), this->field(), row_cmp); + assert(!Z.col(to).empty()); + this->Z.fix(to); // NB: rows will be linked in the back, so the iterators are Ok + this->Z.update_low(to); + + // subtract B.row(to) from B.row(from) + IndexChain Bto_row; + for (auto& x : this->B.row(to)) + Bto_row.emplace_back(x.element(), col(x)); + std::sort(Bto_row.begin(), Bto_row.end(), index_chain_cmp); + +#if 0 + for (auto& x : this->B.row(to)) + std::cout << x.element() << ' ' << row(x) << ' ' << col(x) << std::endl; + + std::cout << "---\n"; + + for (auto& x : this->B.row(from)) + std::cout << x.element() << ' ' << row(x) << ' ' << col(x) << std::endl; +#endif + + Chain::addto(b_changes[from], this->field().neg(mult), Bto_row, this->field(), index_chain_cmp); + + // if there is b_changes[to] add it, too + auto it = b_changes.find(to); + if (it != b_changes.end()) + Chain::addto(b_changes[from], this->field().neg(mult), it->second, this->field(), index_chain_cmp); + }; + Index last_low = row(Z.col(reducers[reducers.size() - 2]->index()).back()); + for (int i = reducers.size() - 2; i >= 0; --i) + { + auto rit = reducers[i]; + FieldElement m = field().neg(field().inv(rit->element())); + + for (auto it = std::next(rit); it != reducers[i+1]; ++it) + add_in_z(it->index(), rit->index(), m, it->element()); + + if (static_cast(i + 1) != reducers.size() - 1) + { + auto it = reducers[i+1]; + add_in_z(it->index(), rit->index(), m, it->element()); + } + } + if (reducers.size() > 2) + Z.drop_low(last_low); + + // apply changes in b (the complexity here could get ugly) + // Specifically, transpose b_changes and add it in + std::unordered_map b_changes_transposed; + for (auto& b_row : b_changes) + for (auto& bx : b_row.second) + b_changes_transposed[bx.index()].emplace_back(bx.element(), b_row.first, bx.index()); + + for (auto& b_col : b_changes_transposed) + { +#if 0 + std::cout << "Adding:" << std::endl; + for (auto& x : b_col.second) + std::cout << x.element() << ' ' << row(x) << ' ' << col(x) << std::endl; +#endif + Chain::addto(B.col(b_col.first), field().id(), b_col.second, field(), b_row_cmp); + assert(!B.col(b_col.first).empty()); + B.fix(b_col.first); + // no need to update low (additions from bottom up) + } + } // z_row.size() > 1 + + // 2: subtract cycle from every chain in C + const Column& Zj = Z.col(j); + //std::cout << "Zj:" << std::endl; + //for (auto& x : Zj) + // std::cout << x.element() << " * " << row(x) << std::endl; + + IndexChain Ccols; // save the columns in C, we'll be modifying C.row(cell) + for (auto& x : C.row(cell)) + Ccols.emplace_back(x.element(), col(x)); + + for (auto& x : Ccols) + { + Index c = x.index(); + FieldElement m = field().neg(field().div(x.element(), e)); // m = -C[k][cell]/Z[j][cell] + //std::cout << "Adding to C: " << c << std::endl; + Chain::addto(C.col(c), m, Zj, field(), row_cmp); + C.fix(c); + // we don't care about lows in C, so don't update them + } + + // 3: drop + assert(Z.row(cell).size() == 1); + Z.drop_col(j); + assert(Z.row(cell).empty()); + assert(C.row(cell).empty()); + Z.drop_row(cell); + C.drop_row(cell); + assert(B.row(j).empty()); + B.drop_row(j); + + Index birth = birth_index[j]; + birth_index.erase(j); + + return birth; + } +} + + +/* debug routines */ +template +void +dionysus::ZigzagPersistence:: +check_b_cols() const +{ + // check that entries in B refer to existing Z columns + bool stop = false; + for (auto& b : B.columns()) + for (auto& x : b.second) + if (!Z.col_exists(row(x))) + { + std::cout << "B refers to a non-existent column in Z: " << row(x) << std::endl; + stop = true; + } + if (stop) + assert(0); +} + +template +template +void +dionysus::ZigzagPersistence:: +check_cycles(const SimplexToIndex& s2i, const IndexToSimplex& i2s) const +{ + typedef typename Column::value_type Entry; + auto row_cmp = [this](const Entry& e1, const Entry& e2) + { return this->cmp()(row(e1), row(e2)); }; + + for (auto& z : Z.columns()) + { + Column res; + for (auto& x : z.second) + { + Column bdry = boundary(row(x), s2i, i2s); + Chain::addto(res, x.element(), bdry, field(), row_cmp); + } + assert(res.empty()); + } +} + +template +template +void +dionysus::ZigzagPersistence:: +check_boundaries(const SimplexToIndex& s2i, const IndexToSimplex& i2s) const +{ + check_cycles(s2i, i2s); + + for (auto& x : B.columns()) + if (!C.col_exists(x.first)) + { + std::cout << x.first << " in B, but not in C" << std::endl; + assert(0); + } + + for (auto& x : C.columns()) + if (!B.col_exists(x.first)) + { + std::cout << x.first << " in B, but not in C" << std::endl; + assert(0); + } + + for (auto& x : B.columns()) + { + auto zb = zb_dot(x.first); + auto dc = dc_dot(x.first, s2i, i2s); + + auto it_zb = zb.begin(), + it_dc = dc.begin(); + for (; it_zb != zb.end(); ++it_zb, ++it_dc) + { + if (it_zb->element() != it_dc->element() || row(*it_zb) != row(*it_dc)) + { + std::cout << "Boundary mismatch: " << x.first << std::endl; + std::cout << "===" << std::endl; + for (auto& x : zb) + std::cout << " " << x.element() << ' ' << row(x) << std::endl; + for (auto& y : B.col(x.first)) + { + std::cout << " " << y.element() << " * " << row(y) << std::endl; + for (auto& z : Z.col(row(y))) + std::cout << " " << z.element() << ' ' << row(z) << std::endl; + std::cout << " ---" << std::endl; + } + std::cout << "===" << std::endl; + for (auto& x : dc) + std::cout << " " << x.element() << ' ' << row(x) << std::endl; + for (auto& y : C.col(x.first)) + { + std::cout << " " << y.element() << " * " << row(y) << std::endl; + for (auto& z : boundary(row(y), s2i, i2s)) + std::cout << " " << z.element() << ' ' << row(z) << std::endl; + std::cout << " ---" << std::endl; + } + assert(0); + } + } + if (it_zb != zb.end() || it_dc != dc.end()) + { + std::cout << "zb.end() doesn't match dc.end()" << std::endl; + assert(0); + } + } +} + +template +typename dionysus::ZigzagPersistence::Column +dionysus::ZigzagPersistence:: +zb_dot(Index c) const +{ + typedef typename Column::value_type Entry; + auto row_cmp = [this](const Entry& e1, const Entry& e2) + { return this->cmp()(row(e1), row(e2)); }; + Column res; + for (auto& x : B.col(c)) + Chain::addto(res, x.element(), Z.col(row(x)), field(), row_cmp); + + return res; +} + +template +template +typename dionysus::ZigzagPersistence::Column +dionysus::ZigzagPersistence:: +dc_dot(Index c, const SimplexToIndex& s2i, const IndexToSimplex& i2s) const +{ + typedef typename Column::value_type Entry; + auto row_cmp = [this](const Entry& e1, const Entry& e2) + { return this->cmp()(row(e1), row(e2)); }; + Column res; + for (auto& x : C.col(c)) + { + Column bdry = boundary(row(x), s2i, i2s); + Chain::addto(res, x.element(), bdry, field(), row_cmp); + } + return res; +} + +template +template +typename dionysus::ZigzagPersistence::Column +dionysus::ZigzagPersistence:: +boundary(Index i, const SimplexToIndex& s2i, const IndexToSimplex& i2s) const +{ + typedef typename Column::value_type Entry; + auto row_cmp = [this](const Entry& e1, const Entry& e2) + { return this->cmp()(row(e1), row(e2)); }; + Column bdry; + auto s = i2s(i); + for (auto y : s.boundary(field())) + bdry.emplace_back(y.element(), s2i(y.index()), 0); + std::sort(bdry.begin(), bdry.end(), row_cmp); + return bdry; +} + +template +void +dionysus::ZigzagPersistence:: +check_sorted() const +{ + typedef typename Column::value_type Entry; + auto row_cmp = [this](const Entry& e1, const Entry& e2) + { return this->cmp()(row(e1), row(e2)); }; + typedef typename DequeColumn::value_type DequeEntry; + auto b_row_cmp = [this](const DequeEntry& e1, const DequeEntry& e2) + { return this->cmp()(row(e1), row(e2)); }; + + for (auto& x : Z.columns()) + if (!std::is_sorted(x.second.begin(), x.second.end(), row_cmp)) + { + std::cout << "Z column not sorted: " << x.first << std::endl; + assert(0); + } + for (auto& x : C.columns()) + if (!std::is_sorted(x.second.begin(), x.second.end(), row_cmp)) + { + std::cout << "C column not sorted: " << x.first << std::endl; + assert(0); + } + for (auto& x : B.columns()) + if (!std::is_sorted(x.second.begin(), x.second.end(), b_row_cmp)) + { + std::cout << "B column not sorted: " << x.first << std::endl; + assert(0); + } +} + diff --git a/src/Zigzag_persistence/example/ext_zz/fzz/fzz.cpp b/src/Zigzag_persistence/example/ext_zz/fzz/fzz.cpp new file mode 100644 index 0000000000..5217a4f38c --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/fzz/fzz.cpp @@ -0,0 +1,206 @@ +#include "fzz.h" + +#include + +// phat headers +// wrapper algorithm that computes the persistence pairs of a given boundary matrix using a specified algorithm +#include + +// main data structure (choice affects performance) +#include +#include + +// algorithm (choice affects performance) +#include +#include +#include +#include + +namespace FZZ { + +template +class VecHash { +public: + size_t operator()(const std::vector& v) const; +}; + +template +size_t VecHash + ::operator()(const std::vector& v) const { + + std::size_t seed = 0; + + for (auto e : v) { boost::hash_combine(seed, e); } + + return seed; +} + +template +class VecEqual { +public: + bool operator()(const std::vector& v1, + const std::vector& v2) const; +}; + +template +bool VecEqual + ::operator()(const std::vector& v1, + const std::vector& v2) const { + + if (v1.size() != v2.size()) { return false; } + + for (unsigned int i = 0; i < v1.size(); i ++) { + if (v1[i] != v2[i]) { + return false; + } + } + + return true; +} + +typedef std::unordered_map< Simplex, Integer, + VecHash, VecEqual > SimplexIdMap; + +void getBoundaryChainPhat(const std::vector &id_maps, + const Simplex &simp, std::vector &bound_c) { + + bound_c.clear(); + + if (simp.size() <= 1) { return; } + + bound_c.reserve(simp.size()); + + Simplex bound_simp(simp.begin()+1, simp.end()); + bound_c.push_back(id_maps.at(bound_simp.size() - 1).at(bound_simp)); + + for (unsigned int i = 0; i < simp.size()-1; ++i) { + bound_simp[i] = simp[i]; + bound_c.push_back(id_maps.at(bound_simp.size() - 1).at(bound_simp)); + } + + std::sort(bound_c.begin(), bound_c.end()); +} + +inline Integer getDim(const std::vector &bound_c) { + if (bound_c.size() == 0) { return 0; } + return bound_c.size() - 1; +} + +void FastZigzag::compute(const std::vector &filt_simp, + const std::vector &filt_op, + std::vector< std::tuple > *persistence) { + + orig_f_add_id.clear(); + orig_f_del_id.clear(); + persistence->clear(); + + simp_num = 0; + Integer max_dim = 0; + for (unsigned int i = 0; i < filt_op.size(); ++i) { + if (filt_op[i]) { + ++simp_num; + if (static_cast(filt_simp[i].size()) - 1 > max_dim) { max_dim = filt_simp[i].size() - 1; } + } + } + + std::vector bound_c; + // phat::boundary_matrix< phat::vector_vector > bound_chains; + phat::boundary_matrix< phat::bit_tree_pivot_column > bound_chains; + bound_chains.set_num_cols(simp_num * 2 + 1); + + // Add the Omega vertex for the coning + bound_chains.set_col(0, bound_c); + bound_chains.set_dim(0, 0); + + orig_f_add_id.reserve(simp_num); + orig_f_del_id.reserve(simp_num); + + std::vector del_ids; + del_ids.reserve(simp_num); + + std::vector *p_id_maps = new std::vector(max_dim+1); + std::vector &id_maps = *p_id_maps; + + Integer orig_f_id = 0; + Integer s_id = 1; + + for (unsigned int i = 0; i < filt_simp.size(); ++i) { + const Simplex &simp = filt_simp[i]; + + if (filt_op[i]) { + getBoundaryChainPhat(id_maps, simp, bound_c); + bound_chains.set_col(s_id, bound_c); + bound_chains.set_dim(s_id, getDim(bound_c)); + + // assert(s_id == bound_chains.size()-1); + id_maps.at(simp.size() - 1)[simp] = s_id; + orig_f_add_id.push_back(orig_f_id); + s_id ++; + } else { + del_ids.push_back(id_maps.at(simp.size() - 1)[simp]); + id_maps.at(simp.size() - 1).erase(simp); + orig_f_del_id.push_back(orig_f_id); + } + + orig_f_id ++; + } + + for (Integer i = id_maps.size() - 1; i >= 0; -- i) { + for (const auto &it : id_maps.at(i)) { + del_ids.push_back(it.second); + orig_f_del_id.push_back(orig_f_id); + orig_f_id ++; + } + } + + assert(del_ids.size() == s_id-1); + delete p_id_maps; + + assert(simp_num == del_ids.size()); + + std::vector cone_sid(simp_num+1); + + for (auto del_id_it = del_ids.rbegin(); del_id_it != del_ids.rend(); ++del_id_it) { + bound_c.clear(); + bound_c.push_back(*del_id_it); + + std::vector orig_bound_c; + bound_chains.get_col(*del_id_it, orig_bound_c); + + if (orig_bound_c.size() == 0) { + bound_c.push_back(0); + } else { + for (auto bsimp : orig_bound_c) { + // assert(cone_sid[bsimp] >= 0); + bound_c.push_back(cone_sid[bsimp]); + } + } + + std::sort(bound_c.begin(), bound_c.end()); + + bound_chains.set_col(s_id, bound_c); + bound_chains.set_dim(s_id, getDim(bound_c)); + + cone_sid[*del_id_it] = s_id; + + s_id ++; + } + + phat::persistence_pairs pairs; + phat::compute_persistence_pairs< phat::twist_reduction >( pairs, bound_chains ); + + for (phat::index idx = 0; idx < pairs.get_num_pairs(); idx++) { + Integer b = pairs.get_pair(idx).first; + Integer d = pairs.get_pair(idx).second - 1; + Integer p = bound_chains.get_dim(b); + + if (d < simp_num) { mapOrdIntv(b, d); } + else { mapRelExtIntv(p, b, d); } + + if (b > static_cast(filt_simp.size())) { continue; } + if (d > static_cast(filt_simp.size())) { d = filt_simp.size(); } + persistence->emplace_back(b, d, p); + } +} + +} // namespace FZZ { diff --git a/src/Zigzag_persistence/example/ext_zz/fzz/fzz.h b/src/Zigzag_persistence/example/ext_zz/fzz/fzz.h new file mode 100644 index 0000000000..8613bc3793 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/fzz/fzz.h @@ -0,0 +1,87 @@ +#ifndef _FZZ_H_ +#define _FZZ_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace FZZ { + +typedef int Integer; +typedef std::vector Simplex; + +class FastZigzag { +public: + /* + 'filt_simp' and 'filt_op' should have the same length which altogether + specify the input zigzag filtration. 'filt_simp' specifies the simplices + being added or deleted (following the order of the filtration) and + 'filt_op' specifies whether it's an addition (true) or deletion (false). + 'persistence' returns the barcode, with the first element of the tuple + being the birth, the second element being the death, and the third + being the dimension. + */ + void compute( + const std::vector &filt_simp, + const std::vector &filt_op, + std::vector< std::tuple > *persistence); + +private: + void mapOrdIntv(Integer &b, Integer &d) { + // assert(b-1 > 0); + // assert(d < orig_f_add_id.size()); + + // Up-down interval is same, + // so directly map to interval of input filtration + b = orig_f_add_id[b-1] + 1; + d = orig_f_add_id[d]; + } + + void mapRelExtIntv(Integer &p, Integer &b, Integer &d) { + // assert(d >= simp_num); + + if (b > simp_num) { // Open-closed + // Map to up-down interval + std::swap(b, d); + b = 3*simp_num - b; + d = 3*simp_num - d; + p --; + + // Map to interval of input filtration + b = orig_f_del_id[b-1-simp_num] + 1; + d = orig_f_del_id[d-simp_num]; + } else { // Closed-closed + // Map to up-down interval + d = 3*simp_num - d-1; + + // Map to interval of input filtration + b = orig_f_add_id[b-1]; + d = orig_f_del_id[d-simp_num]; + + if (b < d) { + b = b+1; + } else { + std::swap(b, d); + b = b+1; + p = p-1; + } + } + } + +private: + // 'orig_f_add_id' and 'orig_f_del_id' form a mapping + // from the up-down filtration to the original filtration + std::vector orig_f_add_id; + std::vector orig_f_del_id; + + Integer simp_num; +}; + +} + +#endif diff --git a/src/Zigzag_persistence/example/ext_zz/phat/algorithms/chunk_reduction.h b/src/Zigzag_persistence/example/ext_zz/phat/algorithms/chunk_reduction.h new file mode 100644 index 0000000000..179702312f --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/phat/algorithms/chunk_reduction.h @@ -0,0 +1,223 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + class chunk_reduction { + public: + enum column_type { GLOBAL + , LOCAL_POSITIVE + , LOCAL_NEGATIVE }; + + public: + template< typename Representation > + void operator() ( boundary_matrix< Representation >& boundary_matrix ) { + + + const index nr_columns = boundary_matrix.get_num_cols(); + if( omp_get_max_threads( ) > nr_columns ) + omp_set_num_threads( 1 ); + + const dimension max_dim = boundary_matrix.get_max_dim(); + + std::vector< index > lowest_one_lookup( nr_columns, -1 ); + std::vector < column_type > column_type( nr_columns, GLOBAL ); + std::vector< char > is_active( nr_columns, false ); + + const index chunk_size = omp_get_max_threads() == 1 ? (index)sqrt( (double)nr_columns ) : nr_columns / omp_get_max_threads(); + + std::vector< index > chunk_boundaries; + for( index cur_boundary = 0; cur_boundary < nr_columns; cur_boundary += chunk_size ) + chunk_boundaries.push_back( cur_boundary ); + chunk_boundaries.push_back( nr_columns ); + + for( dimension cur_dim = max_dim; cur_dim >= 1; cur_dim-- ) { + // Phase 1: Reduce chunks locally -- 1st pass + #pragma omp parallel for schedule( guided, 1 ) + for( index chunk_id = 0; chunk_id < (index)chunk_boundaries.size() - 1; chunk_id++ ) + _local_chunk_reduction( boundary_matrix, lowest_one_lookup, column_type, cur_dim, + chunk_boundaries[ chunk_id ], chunk_boundaries[ chunk_id + 1 ], chunk_boundaries[ chunk_id ] ); + boundary_matrix.sync(); + + // Phase 1: Reduce chunks locally -- 2nd pass + #pragma omp parallel for schedule( guided, 1 ) + for( index chunk_id = 1; chunk_id < (index)chunk_boundaries.size( ) - 1; chunk_id++ ) + _local_chunk_reduction( boundary_matrix, lowest_one_lookup, column_type, cur_dim, + chunk_boundaries[ chunk_id ], chunk_boundaries[ chunk_id + 1 ], chunk_boundaries[ chunk_id - 1 ] ); + boundary_matrix.sync( ); + } + + // get global columns + std::vector< index > global_columns; + for( index cur_col_idx = 0; cur_col_idx < nr_columns; cur_col_idx++ ) + if( column_type[ cur_col_idx ] == GLOBAL ) + global_columns.push_back( cur_col_idx ); + + // get active columns + #pragma omp parallel for + for( index idx = 0; idx < (index)global_columns.size(); idx++ ) + is_active[ global_columns[ idx ] ] = true; + _get_active_columns( boundary_matrix, lowest_one_lookup, column_type, global_columns, is_active ); + + // Phase 2+3: Simplify columns and reduce them + for( dimension cur_dim = max_dim; cur_dim >= 1; cur_dim-- ) { + // Phase 2: Simplify columns + std::vector< index > temp_col; + #pragma omp parallel for schedule( guided, 1 ), private( temp_col ) + for( index idx = 0; idx < (index)global_columns.size(); idx++ ) + if( boundary_matrix.get_dim( global_columns[ idx ] ) == cur_dim ) + _global_column_simplification( global_columns[ idx ], boundary_matrix, lowest_one_lookup, column_type, is_active, temp_col ); + boundary_matrix.sync(); + + // Phase 3: Reduce columns + for( index idx = 0; idx < (index)global_columns.size(); idx++ ) { + index cur_col = global_columns[ idx ]; + if( boundary_matrix.get_dim( cur_col ) == cur_dim && column_type[ cur_col ] == GLOBAL ) { + index lowest_one = boundary_matrix.get_max_index( cur_col ); + while( lowest_one != -1 && lowest_one_lookup[ lowest_one ] != -1 ) { + boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); + lowest_one = boundary_matrix.get_max_index( cur_col ); + } + if( lowest_one != -1 ) { + lowest_one_lookup[ lowest_one ] = cur_col; + boundary_matrix.clear( lowest_one ); + } + boundary_matrix.finalize( cur_col ); + } + } + } + + boundary_matrix.sync(); + } + + protected: + template< typename Representation > + void _local_chunk_reduction( boundary_matrix< Representation >& boundary_matrix + , std::vector& lowest_one_lookup + , std::vector< column_type >& column_type + , const dimension cur_dim + , const index chunk_begin + , const index chunk_end + , const index row_begin ) { + + for( index cur_col = chunk_begin; cur_col < chunk_end; cur_col++ ) { + if( column_type[ cur_col ] == GLOBAL && boundary_matrix.get_dim( cur_col ) == cur_dim ) { + index lowest_one = boundary_matrix.get_max_index( cur_col ); + while( lowest_one != -1 && lowest_one >= row_begin && lowest_one_lookup[ lowest_one ] != -1 ) { + boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); + lowest_one = boundary_matrix.get_max_index( cur_col ); + } + if( lowest_one >= row_begin ) { + lowest_one_lookup[ lowest_one ] = cur_col; + column_type[ cur_col ] = LOCAL_NEGATIVE; + column_type[ lowest_one ] = LOCAL_POSITIVE; + boundary_matrix.clear( lowest_one ); + boundary_matrix.finalize( cur_col ); + } + } + } + } + + template< typename Representation > + void _get_active_columns( const boundary_matrix< Representation >& boundary_matrix + , const std::vector< index >& lowest_one_lookup + , const std::vector< column_type >& column_type + , const std::vector< index >& global_columns + , std::vector< char >& is_active ) { + + const index nr_columns = boundary_matrix.get_num_cols(); + std::vector< char > finished( nr_columns, false ); + + std::vector< std::pair < index, index > > stack; + std::vector< index > cur_col_values; + #pragma omp parallel for schedule( guided, 1 ), private( stack, cur_col_values ) + for( index idx = 0; idx < (index)global_columns.size(); idx++ ) { + bool pop_next = false; + index start_col = global_columns[ idx ]; + stack.push_back( std::pair< index, index >( start_col, -1 ) ); + while( !stack.empty() ) { + index cur_col = stack.back().first; + index prev_col = stack.back().second; + if( pop_next ) { + stack.pop_back(); + pop_next = false; + if( prev_col != -1 ) { + if( is_active[ cur_col ] ) { + is_active[ prev_col ] = true; + } + if( prev_col == stack.back().first ) { + finished[ prev_col ] = true; + pop_next = true; + } + } + } else { + pop_next = true; + boundary_matrix.get_col( cur_col, cur_col_values ); + for( index idx = 0; idx < (index) cur_col_values.size(); idx++ ) { + index cur_row = cur_col_values[ idx ]; + if( ( column_type[ cur_row ] == GLOBAL ) ) { + is_active[ cur_col ] = true; + } else if( column_type[ cur_row ] == LOCAL_POSITIVE ) { + index next_col = lowest_one_lookup[ cur_row ]; + if( next_col != cur_col && !finished[ cur_col ] ) { + stack.push_back( std::make_pair( next_col, cur_col ) ); + pop_next = false; + } + } + } + } + } + } + } + + template< typename Representation > + void _global_column_simplification( const index col_idx + , boundary_matrix< Representation >& boundary_matrix + , const std::vector< index >& lowest_one_lookup + , const std::vector< column_type >& column_type + , const std::vector< char >& is_active + , std::vector< index >& temp_col ) + { + temp_col.clear(); + while( !boundary_matrix.is_empty( col_idx ) ) { + index cur_row = boundary_matrix.get_max_index( col_idx ); + switch( column_type[ cur_row ] ) { + case GLOBAL: + temp_col.push_back( cur_row ); + boundary_matrix.remove_max( col_idx ); + break; + case LOCAL_NEGATIVE: + boundary_matrix.remove_max( col_idx ); + break; + case LOCAL_POSITIVE: + if( is_active[ lowest_one_lookup[ cur_row ] ] ) + boundary_matrix.add_to( lowest_one_lookup[ cur_row ], col_idx ); + else + boundary_matrix.remove_max( col_idx ); + break; + } + } + std::reverse( temp_col.begin(), temp_col.end() ); + boundary_matrix.set_col( col_idx, temp_col ); + } + }; +} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/algorithms/row_reduction.h b/src/Zigzag_persistence/example/ext_zz/phat/algorithms/row_reduction.h new file mode 100644 index 0000000000..cdd1a8fd18 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/phat/algorithms/row_reduction.h @@ -0,0 +1,56 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + class row_reduction { + public: + template< typename Representation > + void operator() ( boundary_matrix< Representation >& boundary_matrix ) { + + const index nr_columns = boundary_matrix.get_num_cols(); + std::vector< std::vector< index > > lowest_one_lookup( nr_columns ); + + for( index cur_col = nr_columns - 1; cur_col >= 0; cur_col-- ) { + if( !boundary_matrix.is_empty( cur_col ) ) + lowest_one_lookup[ boundary_matrix.get_max_index( cur_col ) ].push_back( cur_col ); + + if( !lowest_one_lookup[ cur_col ].empty() ) { + boundary_matrix.clear( cur_col ); + boundary_matrix.finalize( cur_col ); + std::vector< index >& cols_with_cur_lowest = lowest_one_lookup[ cur_col ]; + index source = *min_element( cols_with_cur_lowest.begin(), cols_with_cur_lowest.end() ); + for( index idx = 0; idx < (index)cols_with_cur_lowest.size(); idx++ ) { + index target = cols_with_cur_lowest[ idx ]; + if( target != source && !boundary_matrix.is_empty( target ) ) { + boundary_matrix.add_to( source, target ); + if( !boundary_matrix.is_empty( target ) ) { + index lowest_one_of_target = boundary_matrix.get_max_index( target ); + lowest_one_lookup[ lowest_one_of_target ].push_back( target ); + } + } + } + } + } + } + }; +} \ No newline at end of file diff --git a/src/Zigzag_persistence/example/ext_zz/phat/algorithms/spectral_sequence_reduction.h b/src/Zigzag_persistence/example/ext_zz/phat/algorithms/spectral_sequence_reduction.h new file mode 100644 index 0000000000..bf442e6089 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/phat/algorithms/spectral_sequence_reduction.h @@ -0,0 +1,80 @@ +/* Copyright 2013 IST Austria + Contributed by: Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + class spectral_sequence_reduction { + public: + template< typename Representation > + void operator () ( boundary_matrix< Representation >& boundary_matrix ) { + + const index nr_columns = boundary_matrix.get_num_cols(); + std::vector< index > lowest_one_lookup( nr_columns, -1 ); + + //const index num_stripes = (index) sqrt( (double)nr_columns ); + const index num_stripes = omp_get_max_threads(); + + index block_size = ( nr_columns % num_stripes == 0 ) ? nr_columns / num_stripes : block_size = nr_columns / num_stripes + 1; + + std::vector< std::vector< index > > unreduced_cols_cur_pass( num_stripes ); + std::vector< std::vector< index > > unreduced_cols_next_pass( num_stripes ); + + for( index cur_dim = boundary_matrix.get_max_dim(); cur_dim >= 1 ; cur_dim-- ) { + #pragma omp parallel for schedule( guided, 1 ) + for( index cur_stripe = 0; cur_stripe < num_stripes; cur_stripe++ ) { + index col_begin = cur_stripe * block_size; + index col_end = std::min( (cur_stripe+1) * block_size, nr_columns ); + for( index cur_col = col_begin; cur_col < col_end; cur_col++ ) + if( boundary_matrix.get_dim( cur_col ) == cur_dim && boundary_matrix.get_max_index( cur_col ) != -1 ) + unreduced_cols_cur_pass[ cur_stripe ].push_back( cur_col ); + } + for( index cur_pass = 0; cur_pass < num_stripes; cur_pass++ ) { + boundary_matrix.sync(); + #pragma omp parallel for schedule( guided, 1 ) + for( int cur_stripe = 0; cur_stripe < num_stripes; cur_stripe++ ) { + index row_begin = (cur_stripe - cur_pass) * block_size; + index row_end = row_begin + block_size; + unreduced_cols_next_pass[ cur_stripe ].clear(); + for( index idx = 0; idx < (index)unreduced_cols_cur_pass[ cur_stripe ].size(); idx++ ) { + index cur_col = unreduced_cols_cur_pass[ cur_stripe ][ idx ]; + index lowest_one = boundary_matrix.get_max_index( cur_col ); + while( lowest_one != -1 && lowest_one >= row_begin && lowest_one < row_end && lowest_one_lookup[ lowest_one ] != -1 ) { + boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); + lowest_one = boundary_matrix.get_max_index( cur_col ); + } + if( lowest_one != -1 ) { + if( lowest_one >= row_begin && lowest_one < row_end ) { + lowest_one_lookup[ lowest_one ] = cur_col; + boundary_matrix.clear( lowest_one ); + boundary_matrix.finalize( cur_col ); + } else { + unreduced_cols_next_pass[ cur_stripe ].push_back( cur_col ); + } + } + } + unreduced_cols_next_pass[ cur_stripe ].swap( unreduced_cols_cur_pass[ cur_stripe ] ); + } + } + } + } + }; +} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/algorithms/standard_reduction.h b/src/Zigzag_persistence/example/ext_zz/phat/algorithms/standard_reduction.h new file mode 100644 index 0000000000..e490a5e0d1 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/phat/algorithms/standard_reduction.h @@ -0,0 +1,47 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + class standard_reduction { + public: + template< typename Representation > + void operator() ( boundary_matrix< Representation >& boundary_matrix ) { + + const index nr_columns = boundary_matrix.get_num_cols(); + std::vector< index > lowest_one_lookup( nr_columns, -1 ); + + for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { + index lowest_one = boundary_matrix.get_max_index( cur_col ); + while( lowest_one != -1 && lowest_one_lookup[ lowest_one ] != -1 ) { + boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); + lowest_one = boundary_matrix.get_max_index( cur_col ); + } + if( lowest_one != -1 ) { + lowest_one_lookup[ lowest_one ] = cur_col; + } + boundary_matrix.finalize( cur_col ); + } + } + }; +} + diff --git a/src/Zigzag_persistence/example/ext_zz/phat/algorithms/twist_reduction.h b/src/Zigzag_persistence/example/ext_zz/phat/algorithms/twist_reduction.h new file mode 100644 index 0000000000..2357df0256 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/phat/algorithms/twist_reduction.h @@ -0,0 +1,51 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + class twist_reduction { + public: + template< typename Representation > + void operator () ( boundary_matrix< Representation >& boundary_matrix ) { + + const index nr_columns = boundary_matrix.get_num_cols(); + std::vector< index > lowest_one_lookup( nr_columns, -1 ); + + for( index cur_dim = boundary_matrix.get_max_dim(); cur_dim >= 1 ; cur_dim-- ) { + for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { + if( boundary_matrix.get_dim( cur_col ) == cur_dim ) { + index lowest_one = boundary_matrix.get_max_index( cur_col ); + while( lowest_one != -1 && lowest_one_lookup[ lowest_one ] != -1 ) { + boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); + lowest_one = boundary_matrix.get_max_index( cur_col ); + } + if( lowest_one != -1 ) { + lowest_one_lookup[ lowest_one ] = cur_col; + boundary_matrix.clear( lowest_one ); + } + boundary_matrix.finalize( cur_col ); + } + } + } + } + }; +} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/boundary_matrix.h b/src/Zigzag_persistence/example/ext_zz/phat/boundary_matrix.h new file mode 100644 index 0000000000..10c66cca13 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/phat/boundary_matrix.h @@ -0,0 +1,343 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +// interface class for the main data structure -- implementations of the interface can be found in ./representations +namespace phat { + template< class Representation = bit_tree_pivot_column > + class boundary_matrix + { + + protected: + Representation rep; + + // interface functions -- actual implementation and complexity depends on chosen @Representation template + public: + // get overall number of columns in boundary_matrix + index get_num_cols() const { return rep._get_num_cols(); } + + // set overall number of columns in boundary_matrix + void set_num_cols( index nr_of_columns ) { rep._set_num_cols( nr_of_columns ); } + + // get dimension of given index + dimension get_dim( index idx ) const { return rep._get_dim( idx ); } + + // set dimension of given index + void set_dim( index idx, dimension dim ) { rep._set_dim( idx, dim ); } + + // replaces content of @col with boundary of given index + void get_col( index idx, column& col ) const { col.clear(); rep._get_col( idx, col ); } + + // set column @idx to the values contained in @col + void set_col( index idx, const column& col ) { rep._set_col( idx, col ); } + + // true iff boundary of given column is empty + bool is_empty( index idx ) const { return rep._is_empty( idx ); } + + // largest index of given column (new name for lowestOne()) -- NOT thread-safe + index get_max_index( index idx ) const { return rep._get_max_index( idx ); } + + // removes maximal index from given column + void remove_max( index idx ) { rep._remove_max( idx ); } + + // adds column @source to column @target' + void add_to( index source, index target ) { rep._add_to( source, target ); } + + // clears given column + void clear( index idx ) { rep._clear( idx ); } + + // finalizes given column + void finalize( index idx ) { rep._finalize( idx ); } + + // syncronizes all internal data structures -- has to be called before and after any multithreaded access! + void sync() { rep._sync(); } + + // info functions -- independent of chosen 'Representation' + public: + // maximal dimension + dimension get_max_dim() const { + dimension cur_max_dim = 0; + for( index idx = 0; idx < get_num_cols(); idx++ ) + cur_max_dim = get_dim( idx ) > cur_max_dim ? get_dim( idx ) : cur_max_dim; + return cur_max_dim; + } + + // number of nonzero rows for given column @idx + index get_num_rows( index idx ) const { + column cur_col; + get_col( idx, cur_col ); + return cur_col.size(); + } + + // maximal number of nonzero rows of all columns + index get_max_col_entries() const { + index max_col_entries = -1; + const index nr_of_columns = get_num_cols(); + for( index idx = 0; idx < nr_of_columns; idx++ ) + max_col_entries = get_num_rows( idx ) > max_col_entries ? get_num_rows( idx ) : max_col_entries; + return max_col_entries; + } + + // maximal number of nonzero cols of all rows + index get_max_row_entries() const { + size_t max_row_entries = 0; + const index nr_of_columns = get_num_cols(); + std::vector< std::vector< index > > transposed_matrix( nr_of_columns ); + column temp_col; + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { + get_col( cur_col, temp_col ); + for( index idx = 0; idx < (index)temp_col.size(); idx++) + transposed_matrix[ temp_col[ idx ] ].push_back( cur_col ); + } + for( index idx = 0; idx < nr_of_columns; idx++ ) + max_row_entries = transposed_matrix[ idx ].size() > max_row_entries ? transposed_matrix[ idx ].size() : max_row_entries; + return max_row_entries; + } + + // overall number of entries in the matrix + index get_num_entries() const { + index number_of_nonzero_entries = 0; + const index nr_of_columns = get_num_cols(); + for( index idx = 0; idx < nr_of_columns; idx++ ) + number_of_nonzero_entries += get_num_rows( idx ); + return number_of_nonzero_entries; + } + + // operators / constructors + public: + boundary_matrix() {}; + + template< class OtherRepresentation > + boundary_matrix( const boundary_matrix< OtherRepresentation >& other ) { + *this = other; + } + + template< typename OtherRepresentation > + bool operator==( const boundary_matrix< OtherRepresentation >& other_boundary_matrix ) const { + const index number_of_columns = this->get_num_cols(); + + if( number_of_columns != other_boundary_matrix.get_num_cols() ) + return false; + + column temp_col; + column other_temp_col; + for( index idx = 0; idx < number_of_columns; idx++ ) { + this->get_col( idx, temp_col ); + other_boundary_matrix.get_col( idx, other_temp_col ); + if( temp_col != other_temp_col || this->get_dim( idx ) != other_boundary_matrix.get_dim( idx ) ) + return false; + } + return true; + } + + template< typename OtherRepresentation > + bool operator!=( const boundary_matrix< OtherRepresentation >& other_boundary_matrix ) const { + return !( *this == other_boundary_matrix ); + } + + template< typename OtherRepresentation > + boundary_matrix< Representation >& operator=( const boundary_matrix< OtherRepresentation >& other ) + { + const index nr_of_columns = other.get_num_cols(); + this->set_num_cols( nr_of_columns ); + column temp_col; + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { + this->set_dim( cur_col, other.get_dim( cur_col ) ); + other.get_col( cur_col, temp_col ); + this->set_col( cur_col, temp_col ); + } + + // by convention, always return *this + return *this; + } + + // I/O -- independent of chosen 'Representation' + public: + + // initializes boundary_matrix from (vector, vector) pair -- untested + template< typename index_type, typename dimemsion_type > + void load_vector_vector( const std::vector< std::vector< index_type > >& input_matrix, const std::vector< dimemsion_type >& input_dims ) { + const index nr_of_columns = (index)input_matrix.size(); + this->set_num_cols( nr_of_columns ); + column temp_col; + #pragma omp parallel for private( temp_col ) + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { + this->set_dim( cur_col, (dimension)input_dims[ cur_col ] ); + + index num_rows = input_matrix[ cur_col ].size(); + temp_col.resize( num_rows ); + for( index cur_row = 0; cur_row < num_rows; cur_row++ ) + temp_col[ cur_row ] = (index)input_matrix[ cur_col ][ cur_row ]; + this->set_col( cur_col, temp_col ); + } + } + + template< typename index_type, typename dimemsion_type > + void save_vector_vector( std::vector< std::vector< index_type > >& output_matrix, std::vector< dimemsion_type >& output_dims ) { + const index nr_of_columns = get_num_cols(); + output_matrix.resize( nr_of_columns ); + output_dims.resize( nr_of_columns ); + column temp_col; + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { + output_dims[ cur_col ] = (dimemsion_type)get_dim( cur_col ); + get_col( cur_col, temp_col ); + index num_rows = temp_col.size(); + output_matrix[ cur_col ].clear(); + output_matrix[ cur_col ].resize( num_rows ); + for( index cur_row = 0; cur_row < num_rows; cur_row++ ) + output_matrix[ cur_col ][ cur_row ] = (index_type)temp_col[ cur_row ]; + } + } + + + // Loads the boundary_matrix from given file in ascii format + // Format: each line represents a column, first number is dimension, other numbers are the content of the column. + // Ignores empty lines and lines starting with a '#'. + bool load_ascii( std::string filename ) { + // first count number of columns: + std::string cur_line; + std::ifstream dummy( filename .c_str() ); + if( dummy.fail() ) + return false; + + index number_of_columns = 0; + while( getline( dummy, cur_line ) ) { + cur_line.erase(cur_line.find_last_not_of(" \t\n\r\f\v") + 1); + if( cur_line != "" && cur_line[ 0 ] != '#' ) + number_of_columns++; + + } + this->set_num_cols( number_of_columns ); + dummy.close(); + + std::ifstream input_stream( filename.c_str() ); + if( input_stream.fail() ) + return false; + + column temp_col; + index cur_col = -1; + while( getline( input_stream, cur_line ) ) { + cur_line.erase(cur_line.find_last_not_of(" \t\n\r\f\v") + 1); + if( cur_line != "" && cur_line[ 0 ] != '#' ) { + cur_col++; + std::stringstream ss( cur_line ); + + int64_t temp_dim; + ss >> temp_dim; + this->set_dim( cur_col, (dimension) temp_dim ); + + int64_t temp_index; + temp_col.clear(); + while( ss.good() ) { + ss >> temp_index; + temp_col.push_back( (index)temp_index ); + } + std::sort( temp_col.begin(), temp_col.end() ); + this->set_col( cur_col, temp_col ); + } + } + + input_stream.close(); + return true; + } + + // Saves the boundary_matrix to given file in ascii format + // Format: each line represents a column, first number is dimension, other numbers are the content of the column + bool save_ascii( std::string filename ) { + std::ofstream output_stream( filename.c_str() ); + if( output_stream.fail() ) + return false; + + const index nr_columns = this->get_num_cols(); + column tempCol; + for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { + output_stream << (int64_t)this->get_dim( cur_col ); + this->get_col( cur_col, tempCol ); + for( index cur_row_idx = 0; cur_row_idx < (index)tempCol.size(); cur_row_idx++ ) + output_stream << " " << tempCol[ cur_row_idx ]; + output_stream << std::endl; + } + + output_stream.close(); + return true; + } + + // Loads boundary_matrix from given file + // Format: nr_columns % dim1 % N1 % row1 row2 % ...% rowN1 % dim2 % N2 % ... + bool load_binary( std::string filename ) + { + std::ifstream input_stream( filename.c_str( ), std::ios_base::binary | std::ios_base::in ); + if( input_stream.fail( ) ) + return false; + + int64_t nr_columns; + input_stream.read( (char*)&nr_columns, sizeof( int64_t ) ); + this->set_num_cols( (index)nr_columns ); + + column temp_col; + for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { + int64_t cur_dim; + input_stream.read( (char*)&cur_dim, sizeof( int64_t ) ); + this->set_dim( cur_col, (dimension)cur_dim ); + int64_t nr_rows; + input_stream.read( (char*)&nr_rows, sizeof( int64_t ) ); + temp_col.resize( ( std::size_t )nr_rows ); + for( index idx = 0; idx < nr_rows; idx++ ) { + int64_t cur_row; + input_stream.read( (char*)&cur_row, sizeof( int64_t ) ); + temp_col[ idx ] = (index)cur_row; + } + this->set_col( cur_col, temp_col ); + } + + input_stream.close( ); + return true; + } + + // Saves the boundary_matrix to given file in binary format + // Format: nr_columns % dim1 % N1 % row1 row2 % ...% rowN1 % dim2 % N2 % ... + bool save_binary( std::string filename ) + { + std::ofstream output_stream( filename.c_str( ), std::ios_base::binary | std::ios_base::out ); + if( output_stream.fail( ) ) + return false; + + const int64_t nr_columns = this->get_num_cols( ); + output_stream.write( (char*)&nr_columns, sizeof( int64_t ) ); + column tempCol; + for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { + int64_t cur_dim = this->get_dim( cur_col ); + output_stream.write( (char*)&cur_dim, sizeof( int64_t ) ); + this->get_col( cur_col, tempCol ); + int64_t cur_nr_rows = tempCol.size( ); + output_stream.write( (char*)&cur_nr_rows, sizeof( int64_t ) ); + for( index cur_row_idx = 0; cur_row_idx < (index)tempCol.size( ); cur_row_idx++ ) { + int64_t cur_row = tempCol[ cur_row_idx ]; + output_stream.write( (char*)&cur_row, sizeof( int64_t ) ); + } + } + + output_stream.close( ); + return true; + } + }; +} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/compute_persistence_pairs.h b/src/Zigzag_persistence/example/ext_zz/phat/compute_persistence_pairs.h new file mode 100644 index 0000000000..48be65c28e --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/phat/compute_persistence_pairs.h @@ -0,0 +1,128 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include +#include +#include + +namespace phat { + // Extracts persistence pairs in separate dimensions from a reduced + // boundary matrix representing ``double`` filtration. The pairs + // give persistent relative homology of the pair of filtrations. + // TODO: Use it with standard reduction algorithm (no template option). + template< typename ReductionAlgorithm, typename Representation > + void compute_relative_persistence_pairs(std::vector& pairs, boundary_matrix& boundary_matrix, const std::map& L) { + ReductionAlgorithm reduce; + reduce(boundary_matrix); + std::map free; + std::map invL; + for (std::map::const_iterator it = L.begin(); it != L.end(); ++it) { invL[it->second] = it->first; } + for (std::vector::iterator it = pairs.begin(); it != pairs.end(); ++it) { it->clear(); } + for (index idx = 0; idx < boundary_matrix.get_num_cols(); ++idx) { + int dimension = boundary_matrix.get_dim(idx); + if (L.find(idx) != L.end()) { ++dimension; } + free[idx] = true; + if (!boundary_matrix.is_empty(idx)) { + index birth = boundary_matrix.get_max_index(idx); + index death = idx; + pairs[dimension-1].append_pair(birth, death); + free[birth] = false; + free[death] = false; + } else { + // This is an L-simplex and a (dimension+1)-dimensional cycle + if (L.find(idx) != L.end()) { + assert(dimension < pairs.size()); + pairs[dimension].append_pair(idx, -1); + } + } + } + for (std::map::iterator it = free.begin(); it != free.end(); ++it) { + if (it->second) { + int dimension = boundary_matrix.get_dim(it->first); + if (invL.find(it->first) == invL.end() && L.find(it->first) == L.end()) { + assert(dimension < pairs.size()); + pairs[dimension].append_pair(it->first, -1); + } + } + } + } + + // Extracts persistence pairs in separate dimensions; expects a d-dimensional vector of persistent_pairs + template< typename ReductionAlgorithm, typename Representation > + void compute_persistence_pairs(std::vector& pairs, boundary_matrix& boundary_matrix) { + ReductionAlgorithm reduce; + reduce(boundary_matrix); + std::map free; + for (std::vector::iterator it = pairs.begin(); it != pairs.end(); ++it) { it->clear(); } + for (index idx = 0; idx < boundary_matrix.get_num_cols(); ++idx) { + int dimension = boundary_matrix.get_dim(idx); + free[idx] = true; + if (!boundary_matrix.is_empty(idx)) { + index birth = boundary_matrix.get_max_index(idx); + index death = idx; + pairs[dimension-1].append_pair(birth, death); + // Cannot be of the form (a, infinity) + free[birth] = false; + free[death] = false; + } + } + for (std::map::iterator it = free.begin(); it != free.end(); ++it) { + if (it->second) { + int dimension = boundary_matrix.get_dim(it->first); + pairs[dimension].append_pair(it->first, -1); + } + } + } + + template< typename ReductionAlgorithm, typename Representation > + void compute_persistence_pairs( persistence_pairs& pairs, boundary_matrix< Representation >& boundary_matrix ) { + ReductionAlgorithm reduce; + reduce( boundary_matrix ); + pairs.clear(); + for( index idx = 0; idx < boundary_matrix.get_num_cols(); idx++ ) { + if( !boundary_matrix.is_empty( idx ) ) { + index birth = boundary_matrix.get_max_index( idx ); + index death = idx; + pairs.append_pair( birth, death ); + } + } + } + + template< typename ReductionAlgorithm, typename Representation > + void compute_persistence_pairs_dualized( persistence_pairs& pairs, boundary_matrix< Representation >& boundary_matrix ) { + + dualize( boundary_matrix ); + compute_persistence_pairs< ReductionAlgorithm >( pairs, boundary_matrix ); + dualize_persistence_pairs( pairs, boundary_matrix.get_num_cols() ); + } + + template< typename Representation > + void compute_persistence_pairs( persistence_pairs& pairs, boundary_matrix< Representation >& boundary_matrix ) { + phat::compute_persistence_pairs< twist_reduction >( pairs, boundary_matrix ); + } + + + template< typename Representation > + void compute_persistence_pairs_dualized( persistence_pairs& pairs, boundary_matrix< Representation >& boundary_matrix ) { + compute_persistence_pairs_dualized< twist_reduction >( pairs, boundary_matrix ); + } + +} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/helpers/dualize.h b/src/Zigzag_persistence/example/ext_zz/phat/helpers/dualize.h new file mode 100644 index 0000000000..3ffedf875f --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/phat/helpers/dualize.h @@ -0,0 +1,74 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include +#include + + +namespace phat { + template< typename Representation > + void dualize( boundary_matrix< Representation >& boundary_matrix ) { + + std::vector< dimension > dual_dims; + std::vector< std::vector< index > > dual_matrix; + + index nr_of_columns = boundary_matrix.get_num_cols(); + dual_matrix.resize( nr_of_columns ); + dual_dims.resize( nr_of_columns ); + + std::vector< index > dual_sizes( nr_of_columns, 0 ); + + column temp_col; + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { + boundary_matrix.get_col( cur_col, temp_col ); + for( index idx = 0; idx < (index)temp_col.size(); idx++) + dual_sizes[ nr_of_columns - 1 - temp_col[ idx ] ]++; + } + + #pragma omp parallel for + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) + dual_matrix[cur_col].reserve(dual_sizes[cur_col]); + + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { + boundary_matrix.get_col( cur_col, temp_col ); + for( index idx = 0; idx < (index)temp_col.size(); idx++) + dual_matrix[ nr_of_columns - 1 - temp_col[ idx ] ].push_back( nr_of_columns - 1 - cur_col ); + } + + const dimension max_dim = boundary_matrix.get_max_dim(); + #pragma omp parallel for + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) + dual_dims[ nr_of_columns - 1 - cur_col ] = max_dim - boundary_matrix.get_dim( cur_col ); + + #pragma omp parallel for + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) + std::reverse( dual_matrix[ cur_col ].begin(), dual_matrix[ cur_col ].end() ); + + boundary_matrix.load_vector_vector( dual_matrix, dual_dims ); + } + + void dualize_persistence_pairs( persistence_pairs& pairs, const index n ) { + for (index i = 0; i < pairs.get_num_pairs(); ++i) { + std::pair< index, index > pair = pairs.get_pair( i ); + pairs.set_pair( i , n - 1 - pair.second, n - 1 - pair.first); + } + } +} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/helpers/misc.h b/src/Zigzag_persistence/example/ext_zz/phat/helpers/misc.h new file mode 100644 index 0000000000..fb5c07acb0 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/phat/helpers/misc.h @@ -0,0 +1,75 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +// STL includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// VS2008 and below unfortunately do not support stdint.h +#if defined(_MSC_VER)&& _MSC_VER < 1600 + typedef __int8 int8_t; + typedef unsigned __int8 uint8_t; + typedef __int16 int16_t; + typedef unsigned __int16 uint16_t; + typedef __int32 int32_t; + typedef unsigned __int32 uint32_t; + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; +#else + #include +#endif + +// basic types. index can be changed to int32_t to save memory on small instances +namespace phat { + typedef int64_t index; + typedef int8_t dimension; + typedef std::vector< index > column; +} + +// OpenMP (proxy) functions +#if defined _OPENMP + #include +#else + #define omp_get_thread_num() 0 + #define omp_get_max_threads() 1 + #define omp_get_num_threads() 1 + void omp_set_num_threads( int ) {}; + #include + #define omp_get_wtime() (float)clock() / (float)CLOCKS_PER_SEC +#endif + +#include + + + diff --git a/src/Zigzag_persistence/example/ext_zz/phat/helpers/thread_local_storage.h b/src/Zigzag_persistence/example/ext_zz/phat/helpers/thread_local_storage.h new file mode 100644 index 0000000000..d0b5332bc1 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/phat/helpers/thread_local_storage.h @@ -0,0 +1,52 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include + +// should ideally be equal to the cache line size of the CPU +#define PHAT_TLS_SPACING_FACTOR 64 + +// ThreadLocalStorage with some spacing to avoid "false sharing" (see wikipedia) +template< typename T > +class thread_local_storage +{ +public: + + thread_local_storage() : per_thread_storage( omp_get_max_threads() * PHAT_TLS_SPACING_FACTOR ) {}; + + T& operator()() { + return per_thread_storage[ omp_get_thread_num() * PHAT_TLS_SPACING_FACTOR ]; + } + + const T& operator()() const { + return per_thread_storage[ omp_get_thread_num() * PHAT_TLS_SPACING_FACTOR ]; + } + + T& operator[]( int tid ) { + return per_thread_storage[ tid * PHAT_TLS_SPACING_FACTOR ]; + } + + const T& operator[]( int tid ) const { + return per_thread_storage[ tid * PHAT_TLS_SPACING_FACTOR ]; + } + +protected: + std::vector< T > per_thread_storage; +}; diff --git a/src/Zigzag_persistence/example/ext_zz/phat/persistence_pairs.h b/src/Zigzag_persistence/example/ext_zz/phat/persistence_pairs.h new file mode 100644 index 0000000000..eafc6389e2 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/phat/persistence_pairs.h @@ -0,0 +1,155 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include + +namespace phat { + class persistence_pairs { + + protected: + std::vector< std::pair< index, index > > pairs; + + public: + index get_num_pairs() const { + return (index)pairs.size(); + } + + void append_pair( index birth, index death ) { + pairs.push_back( std::make_pair( birth, death ) ); + } + + std::pair< index, index > get_pair( index idx ) const { + return pairs[ idx ]; + } + + void set_pair( index idx, index birth, index death ) { + pairs[ idx ] = std::make_pair( birth, death ); + } + + void clear() { + pairs.clear(); + } + + void sort() { + std::sort( pairs.begin(), pairs.end() ); + } + + // Loads the persistence pairs from given file in asci format + // Format: nr_pairs % newline % birth1 % death1 % newline % birth2 % death2 % newline ... + bool load_ascii( std::string filename ) { + std::ifstream input_stream( filename.c_str() ); + if( input_stream.fail() ) + return false; + + int64_t nr_pairs; + input_stream >> nr_pairs; + pairs.clear(); + for( index idx = 0; idx < nr_pairs; idx++ ) { + int64_t birth; + input_stream >> birth; + int64_t death; + input_stream >> death; + append_pair( (index)birth, (index)death ); + } + + input_stream.close(); + return true; + } + + // Saves the persistence pairs to given file in binary format + // Format: nr_pairs % newline % birth1 % death1 % newline % birth2 % death2 % newline ... + bool save_ascii( std::string filename ) { + std::ofstream output_stream( filename.c_str() ); + if( output_stream.fail() ) + return false; + + this->sort(); + output_stream << get_num_pairs() << std::endl; + for( std::size_t idx = 0; idx < pairs.size(); idx++ ) { + output_stream << pairs[idx].first << " " << pairs[idx].second << std::endl; + } + + output_stream.close(); + return true; + } + + // Loads the persistence pairs from given file in binary format + // Format: nr_pairs % birth1 % death1 % birth2 % death2 ... + bool load_binary( std::string filename ) { + std::ifstream input_stream( filename.c_str(), std::ios_base::binary | std::ios_base::in ); + if( input_stream.fail() ) + return false; + + int64_t nr_pairs; + input_stream.read( (char*)&nr_pairs, sizeof( int64_t ) ); + for( index idx = 0; idx < nr_pairs; idx++ ) { + int64_t birth; + input_stream.read( (char*)&birth, sizeof( int64_t ) ); + int64_t death; + input_stream.read( (char*)&death, sizeof( int64_t ) ); + append_pair( (index)birth, (index)death ); + } + + input_stream.close(); + return true; + } + + // Saves the persistence pairs to given file in binary format + // Format: nr_pairs % birth1 % death1 % birth2 % death2 ... + bool save_binary( std::string filename ) { + std::ofstream output_stream( filename.c_str(), std::ios_base::binary | std::ios_base::out ); + if( output_stream.fail() ) + return false; + + this->sort(); + int64_t nr_pairs = get_num_pairs(); + output_stream.write( (char*)&nr_pairs, sizeof( int64_t ) ); + for( std::size_t idx = 0; idx < pairs.size(); idx++ ) { + int64_t birth = pairs[ idx ].first; + output_stream.write( (char*)&birth, sizeof( int64_t ) ); + int64_t death = pairs[ idx ].second; + output_stream.write( (char*)&death, sizeof( int64_t ) ); + } + + output_stream.close(); + return true; + } + + bool operator==( persistence_pairs& other_pairs ) { + this->sort(); + other_pairs.sort(); + if( pairs.size() != (std::size_t)other_pairs.get_num_pairs() ) + return false; + + for( index idx = 0; idx < (index)pairs.size(); idx++ ) + if( get_pair( idx ) != other_pairs.get_pair( idx ) ) + return false; + + return true; + } + + bool operator!=( persistence_pairs& other_pairs ) { + return !( *this == other_pairs ); + } + }; + + + +} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/representations/abstract_pivot_column.h b/src/Zigzag_persistence/example/ext_zz/phat/representations/abstract_pivot_column.h new file mode 100644 index 0000000000..e16d7a5d13 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/phat/representations/abstract_pivot_column.h @@ -0,0 +1,102 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + + // Note: We could even make the rep generic in the underlying Const representation + // But I cannot imagine that anything else than vector> would + // make sense + template< typename PivotColumn > + class abstract_pivot_column : public vector_vector { + + protected: + typedef vector_vector Base; + typedef PivotColumn pivot_col; + + // For parallization purposes, it could be more than one full column + mutable thread_local_storage< pivot_col > pivot_cols; + mutable thread_local_storage< index > idx_of_pivot_cols; + + pivot_col& get_pivot_col() const { + return pivot_cols(); + } + + bool is_pivot_col( index idx ) const { + return idx_of_pivot_cols() == idx; + } + + void release_pivot_col() { + index idx = idx_of_pivot_cols(); + if( idx != -1 ) { + this->matrix[ idx ].clear(); + pivot_cols().get_col_and_clear( this->matrix[ idx ] ); + } + idx_of_pivot_cols() = -1; + } + + void make_pivot_col( index idx ) { + release_pivot_col(); + idx_of_pivot_cols() = idx; + get_pivot_col().add_col( matrix[ idx ] ); + } + + public: + + void _set_num_cols( index nr_of_cols ) { + #pragma omp parallel for + for( int tid = 0; tid < omp_get_num_threads(); tid++ ) { + pivot_cols[ tid ].init( nr_of_cols ); + idx_of_pivot_cols[ tid ] = -1; + } + Base::_set_num_cols( nr_of_cols ); + } + + void _add_to( index source, index target ) { + if( !is_pivot_col( target ) ) + make_pivot_col( target ); + get_pivot_col().add_col( matrix[source] ); + } + + void _sync() { + #pragma omp parallel for + for( int tid = 0; tid < omp_get_num_threads(); tid++ ) + release_pivot_col(); + } + + void _get_col( index idx, column& col ) const { is_pivot_col( idx ) ? get_pivot_col().get_col( col ) : Base::_get_col( idx, col ); } + + bool _is_empty( index idx ) const { return is_pivot_col( idx ) ? get_pivot_col().is_empty() : Base::_is_empty( idx ); } + + index _get_max_index( index idx ) const { return is_pivot_col( idx ) ? get_pivot_col().get_max_index() : Base::_get_max_index( idx ); } + + void _clear( index idx ) { is_pivot_col( idx ) ? get_pivot_col().clear() : Base::_clear( idx ); } + + void _set_col( index idx, const column& col ) { is_pivot_col( idx ) ? get_pivot_col().set_col( col ) : Base::_set_col( idx, col ); } + + void _remove_max( index idx ) { is_pivot_col( idx ) ? get_pivot_col().remove_max() : Base::_remove_max( idx ); } + + void finalize( index idx ) { Base::_finalize( idx ); } + }; +} + + diff --git a/src/Zigzag_persistence/example/ext_zz/phat/representations/bit_tree_pivot_column.h b/src/Zigzag_persistence/example/ext_zz/phat/representations/bit_tree_pivot_column.h new file mode 100644 index 0000000000..4d48e8853d --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/phat/representations/bit_tree_pivot_column.h @@ -0,0 +1,165 @@ +/* Copyright 2013 IST Austria + Contributed by: Hubert Wagner + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + + // This is a bitset indexed with a 64-ary tree. Each node in the index + // has 64 bits; i-th bit says that the i-th subtree is non-empty. + // Supports practically O(1), inplace, zero-allocation: insert, remove, max_element + // and clear in O(number of ones in the bitset). + // 'add_index' is still the real bottleneck in practice. + class bit_tree_column + { + protected: + + size_t offset; // data[i + offset] = ith block of the data-bitset + typedef uint64_t block_type; + std::vector< block_type > data; + + + size_t debrujin_magic_table[ 64 ]; + + enum { block_size_in_bits = 64 }; + enum { block_shift = 6 }; + + // Some magic: http://graphics.stanford.edu/~seander/bithacks.html + // Gets the position of the rightmost bit of 'x'. 0 means the most significant bit. + // (-x)&x isolates the rightmost bit. + // The whole method is much faster than calling log2i, and very comparable to using ScanBitForward/Reverse intrinsic, + // which should be one CPU instruction, but is not portable. + size_t rightmost_pos( const block_type value ) const { + return 64 - 1 - debrujin_magic_table[ ( (value & (-(int64_t)value) ) * 0x07EDD5E59A4E28C2 ) >> 58 ]; + } + + public: + + void init( index num_cols ) { + int64_t n = 1; // in case of overflow + int64_t bottom_blocks_needed = ( num_cols + block_size_in_bits - 1 ) / block_size_in_bits; + int64_t upper_blocks = 1; + + // How many blocks/nodes of index needed to index the whole bitset? + while( n * block_size_in_bits < bottom_blocks_needed ) { + n *= block_size_in_bits; + upper_blocks += n; + } + + offset = upper_blocks; + data.resize( upper_blocks + bottom_blocks_needed, 0 ); + + std::size_t temp_array[ 64 ] = { + 63, 0, 58, 1, 59, 47, 53, 2, + 60, 39, 48, 27, 54, 33, 42, 3, + 61, 51, 37, 40, 49, 18, 28, 20, + 55, 30, 34, 11, 43, 14, 22, 4, + 62, 57, 46, 52, 38, 26, 32, 41, + 50, 36, 17, 19, 29, 10, 13, 21, + 56, 45, 25, 31, 35, 16, 9, 12, + 44, 24, 15, 8, 23, 7, 6, 5 }; + + std::copy( &temp_array[ 0 ], &temp_array[ 64 ], &debrujin_magic_table[ 0 ] ); + } + + index get_max_index() const { + if( !data[ 0 ] ) + return -1; + + size_t n = 0; + size_t newn = 0; + size_t index = 0; + while( newn < data.size() ) { + n = newn; + index = rightmost_pos( data[ n ] ); + newn = ( n << block_shift ) + index + 1; + } + + return ( ( n - offset ) << block_shift ) + index; + } + + bool is_empty() const { + return data[ 0 ] == 0; + } + + void add_index( const size_t entry ) { + const block_type ONE = 1; + const block_type block_modulo_mask = ( ONE << block_shift ) - 1; + size_t index_in_level = entry >> block_shift; + size_t address = index_in_level + offset; + size_t index_in_block = entry & block_modulo_mask; + + block_type mask = ( ONE << ( block_size_in_bits - index_in_block - 1 ) ); + + data[ address ] ^= mask; + + // Check if we reached the root. Also, if anyone else was in this block, we don't need to update the path up. + while( address && !( data[ address ] & ~mask ) ) { + index_in_block = index_in_level & block_modulo_mask; + index_in_level >>= block_shift; + --address; + address >>= block_shift; + mask = ( ONE << ( block_size_in_bits - index_in_block - 1 ) ); + data[ address ] ^= mask; + } + } + + void get_col_and_clear( column &out ) { + index mx = this->get_max_index(); + while( mx != -1 ) { + out.push_back( mx ); + add_index( mx ); + mx = this->get_max_index(); + } + + std::reverse( out.begin(), out.end() ); + } + + void add_col(const column &col) { + for( size_t i = 0; i < col.size(); ++i ) + add_index(col[i]); + } + + void clear() { + index mx = this->get_max_index(); + while( mx != -1 ) { + add_index( mx ); + mx = this->get_max_index(); + } + } + + void remove_max() { + add_index( get_max_index() ); + } + + void set_col( const column& col ) { + clear(); + add_col( col ); + } + + void get_col( column& col ) { + get_col_and_clear( col ); + add_col( col ); + } + }; + + typedef abstract_pivot_column bit_tree_pivot_column; +} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/representations/full_pivot_column.h b/src/Zigzag_persistence/example/ext_zz/phat/representations/full_pivot_column.h new file mode 100644 index 0000000000..c2e9e3c574 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/phat/representations/full_pivot_column.h @@ -0,0 +1,100 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + class full_column { + + protected: + std::priority_queue< index > history; + std::vector< char > is_in_history; + std::vector< char > col_bit_field; + + public: + void init( const index total_size ) { + col_bit_field.resize( total_size, false ); + is_in_history.resize( total_size, false ); + } + + void add_col( const column& col ) { + for( index idx = 0; idx < (index) col.size(); idx++ ) { + add_index( col[ idx ] ); + } + } + + void add_index( const index idx ) { + if( !is_in_history[ idx ] ) { + history.push( idx ); + is_in_history[ idx ] = true; + } + + col_bit_field[ idx ] = !col_bit_field[ idx ]; + } + + index get_max_index() { + while( history.size() > 0 ) { + index topIndex = history.top(); + if( col_bit_field[ topIndex ] ) { + return topIndex; + } else { + history.pop(); + is_in_history[ topIndex ] = false; + } + } + + return -1; + } + + void get_col_and_clear( column& col ) { + while( !is_empty() ) { + col.push_back( get_max_index() ); + add_index( get_max_index() ); + } + std::reverse( col.begin(), col.end() ); + } + + bool is_empty() { + return (get_max_index() == -1); + } + + void clear() { + while( !is_empty() ) + add_index( get_max_index() ); + } + + void remove_max() { + add_index( get_max_index() ); + } + + void set_col( const column& col ) { + clear(); + add_col( col ); + } + + void get_col( column& col ) { + get_col_and_clear( col ); + add_col( col ); + } + }; + + typedef abstract_pivot_column< full_column > full_pivot_column; +} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/representations/heap_pivot_column.h b/src/Zigzag_persistence/example/ext_zz/phat/representations/heap_pivot_column.h new file mode 100644 index 0000000000..33cd07b40d --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/phat/representations/heap_pivot_column.h @@ -0,0 +1,126 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + class heap_column { + + protected: + std::priority_queue< index > data; + + column temp_col; + index inserts_since_last_prune; + + void prune() + { + temp_col.clear( ); + index max_index = pop_max_index( ); + while( max_index != -1 ) { + temp_col.push_back( max_index ); + max_index = pop_max_index( ); + } + + for( index idx = 0; idx < (index)temp_col.size( ); idx++ ) + data.push( temp_col[ idx ] ); + + inserts_since_last_prune = 0; + } + + index pop_max_index() + { + if( data.empty( ) ) + return -1; + else { + index max_element = data.top( ); + data.pop(); + while( !data.empty( ) && data.top( ) == max_element ) { + data.pop( ); + if( data.empty( ) ) + return -1; + else { + max_element = data.top( ); + data.pop( ); + } + } + return max_element; + } + } + + public: + void init( const index total_size ) { + inserts_since_last_prune = 0; + clear(); + } + + void add_col( const column& col ) { + for( index idx = 0; idx < (index) col.size(); idx++ ) + data.push( col[ idx ] ); + inserts_since_last_prune += col.size( ); + if( 2 * inserts_since_last_prune >( index ) data.size( ) ) + prune(); + } + + index get_max_index() { + index max_element = pop_max_index( ); + if( max_element == -1 ) + return -1; + else { + data.push( max_element ); + return max_element; + } + } + + void get_col_and_clear( column& col ) { + col.clear(); + index max_index = pop_max_index( ); + while( max_index != -1 ) { + col.push_back( max_index ); + max_index = pop_max_index( ); + } + std::reverse( col.begin(), col.end() ); + } + + bool is_empty() { + return get_max_index() == -1; + } + + void clear() { + data = std::priority_queue< index >(); + } + + void remove_max() { + pop_max_index(); + } + + void set_col( const column& col ) { + clear(); + add_col( col ); + } + + void get_col( column& col ) { + get_col_and_clear( col ); + add_col( col ); + } + }; + + typedef abstract_pivot_column< heap_column > heap_pivot_column; +} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/representations/sparse_pivot_column.h b/src/Zigzag_persistence/example/ext_zz/phat/representations/sparse_pivot_column.h new file mode 100644 index 0000000000..390fd91a99 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/phat/representations/sparse_pivot_column.h @@ -0,0 +1,79 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + class sparse_column { + + protected: + std::set< index > data; + + void add_index( const index idx ) { + std::pair< std::set< index >::iterator, bool > result = data.insert( idx ); + if( result.second == false ) + data.erase( result.first ); + } + + public: + void init( const index total_size ) { + data.clear(); + } + + void add_col( const column& col ) { + for( index idx = 0; idx < (index) col.size(); idx++ ) + add_index( col[ idx ] ); + } + + index get_max_index() { + return data.empty() ? -1 : *data.rbegin(); + } + + void get_col_and_clear( column& col ) { + col.assign( data.begin(), data.end() ); + data.clear(); + } + + bool is_empty() { + return data.empty(); + } + + void clear() { + data.clear(); + } + + void remove_max() { + add_index( get_max_index() ); + } + + void set_col( const column& col ) { + clear(); + add_col( col ); + } + + void get_col( column& col ) { + get_col_and_clear( col ); + add_col( col ); + } + }; + + typedef abstract_pivot_column< sparse_column > sparse_pivot_column; +} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/representations/vector_heap.h b/src/Zigzag_persistence/example/ext_zz/phat/representations/vector_heap.h new file mode 100644 index 0000000000..db0420ff23 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/phat/representations/vector_heap.h @@ -0,0 +1,170 @@ +/* Copyright 2013 IST Austria +Contributed by: Jan Reininghaus + +This file is part of PHAT. + +PHAT is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +PHAT 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with PHAT. If not, see . */ + +#pragma once + +#include + +namespace phat { + class vector_heap { + + protected: + std::vector< dimension > dims; + std::vector< column > matrix; + + std::vector< index > inserts_since_last_prune; + + mutable thread_local_storage< column > temp_column_buffer; + + protected: + void _prune( index idx ) + { + column& col = matrix[ idx ]; + column& temp_col = temp_column_buffer(); + temp_col.clear(); + index max_index = _pop_max_index( col ); + while( max_index != -1 ) { + temp_col.push_back( max_index ); + max_index = _pop_max_index( col ); + } + col = temp_col; + std::reverse( col.begin( ), col.end( ) ); + std::make_heap( col.begin( ), col.end( ) ); + inserts_since_last_prune[ idx ] = 0; + } + + index _pop_max_index( index idx ) + { + return _pop_max_index( matrix[ idx ] ); + } + + index _pop_max_index( column& col ) const + { + if( col.empty( ) ) + return -1; + else { + index max_element = col.front( ); + std::pop_heap( col.begin( ), col.end( ) ); + col.pop_back( ); + while( !col.empty( ) && col.front( ) == max_element ) { + std::pop_heap( col.begin( ), col.end( ) ); + col.pop_back( ); + if( col.empty( ) ) + return -1; + else { + max_element = col.front( ); + std::pop_heap( col.begin( ), col.end( ) ); + col.pop_back( ); + } + } + return max_element; + } + } + + public: + // overall number of cells in boundary_matrix + index _get_num_cols( ) const + { + return (index)matrix.size( ); + } + void _set_num_cols( index nr_of_columns ) + { + dims.resize( nr_of_columns ); + matrix.resize( nr_of_columns ); + inserts_since_last_prune.assign( nr_of_columns, 0 ); + } + + // dimension of given index + dimension _get_dim( index idx ) const + { + return dims[ idx ]; + } + void _set_dim( index idx, dimension dim ) + { + dims[ idx ] = dim; + } + + // replaces(!) content of 'col' with boundary of given index + void _get_col( index idx, column& col ) const + { + temp_column_buffer( ) = matrix[ idx ]; + + index max_index = _pop_max_index( temp_column_buffer() ); + while( max_index != -1 ) { + col.push_back( max_index ); + max_index = _pop_max_index( temp_column_buffer( ) ); + } + std::reverse( col.begin( ), col.end( ) ); + } + void _set_col( index idx, const column& col ) + { + matrix[ idx ] = col; + std::make_heap( matrix[ idx ].begin( ), matrix[ idx ].end( ) ); + } + + // true iff boundary of given idx is empty + bool _is_empty( index idx ) const + { + return _get_max_index( idx ) == -1; + } + + // largest row index of given column idx (new name for lowestOne()) + index _get_max_index( index idx ) const + { + column& col = const_cast< column& >( matrix[ idx ] ); + index max_element = _pop_max_index( col ); + col.push_back( max_element ); + std::push_heap( col.begin( ), col.end( ) ); + return max_element; + } + + // removes the maximal index of a column + void _remove_max( index idx ) + { + _pop_max_index( idx ); + } + + // clears given column + void _clear( index idx ) + { + matrix[ idx ].clear( ); + } + + // syncronizes all data structures (essential for openmp stuff) + void _sync( ) {} + + // adds column 'source' to column 'target' + void _add_to( index source, index target ) + { + for( index idx = 0; idx < (index)matrix[ source ].size( ); idx++ ) { + matrix[ target ].push_back( matrix[ source ][ idx ] ); + std::push_heap( matrix[ target ].begin(), matrix[ target ].end() ); + } + inserts_since_last_prune[ target ] += matrix[ source ].size(); + + if( 2 * inserts_since_last_prune[ target ] > ( index )matrix[ target ].size() ) + _prune( target ); + } + + // finalizes given column + void _finalize( index idx ) { + _prune( idx ); + } + + }; +} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/representations/vector_list.h b/src/Zigzag_persistence/example/ext_zz/phat/representations/vector_list.h new file mode 100644 index 0000000000..ca0b5b8e79 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/phat/representations/vector_list.h @@ -0,0 +1,101 @@ +/* Copyright 2013 IST Austria + Contributed by: Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include + +namespace phat { + class vector_list { + + protected: + std::vector< dimension > dims; + std::vector< std::list< index > > matrix; + + public: + // overall number of cells in boundary_matrix + index _get_num_cols() const { + return (index)matrix.size(); + } + void _set_num_cols( index nr_of_columns ) { + dims.resize( nr_of_columns ); + matrix.resize( nr_of_columns ); + } + + // dimension of given index + dimension _get_dim( index idx ) const { + return dims[ idx ]; + } + void _set_dim( index idx, dimension dim ) { + dims[ idx ] = dim; + } + + // replaces(!) content of 'col' with boundary of given index + void _get_col( index idx, column& col ) const { + col.clear(); + col.reserve( matrix[idx].size() ); + std::copy (matrix[idx].begin(), matrix[idx].end(), std::back_inserter(col) ); + } + + void _set_col( index idx, const column& col ) { + matrix[ idx ].clear(); + matrix[ idx ].resize( col.size() ); + std::copy (col.begin(), col.end(), matrix[ idx ].begin() ); + } + + // true iff boundary of given idx is empty + bool _is_empty( index idx ) const { + return matrix[ idx ].empty(); + } + + // largest row index of given column idx (new name for lowestOne()) + index _get_max_index( index idx ) const { + return matrix[ idx ].empty() ? -1 : *matrix[ idx ].rbegin(); + } + + // removes the maximal index of a column + void _remove_max( index idx ) { + std::list< index >::iterator it = matrix[ idx ].end(); + it--; + matrix[ idx ].erase( it ); + } + + // clears given column + void _clear( index idx ) { + matrix[ idx ].clear(); + } + + // syncronizes all data structures (essential for openmp stuff) + void _sync() {} + + // adds column 'source' to column 'target' + void _add_to( index source, index target ) { + std::list< index >& source_col = matrix[ source ]; + std::list< index >& target_col = matrix[ target ]; + std::list< index > temp_col; + target_col.swap( temp_col ); + std::set_symmetric_difference( temp_col.begin(), temp_col.end(), + source_col.begin(), source_col.end(), + std::back_inserter( target_col ) ); + } + + // finalizes given column + void _finalize( index idx ) { + } + }; +} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/representations/vector_set.h b/src/Zigzag_persistence/example/ext_zz/phat/representations/vector_set.h new file mode 100644 index 0000000000..6878a270c0 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/phat/representations/vector_set.h @@ -0,0 +1,99 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include + +namespace phat { + class vector_set { + + protected: + std::vector< dimension > dims; + std::vector< std::set< index > > matrix; + + public: + // overall number of cells in boundary_matrix + index _get_num_cols() const { + return (index)matrix.size(); + } + void _set_num_cols( index nr_of_columns ) { + dims.resize( nr_of_columns ); + matrix.resize( nr_of_columns ); + } + + // dimension of given index + dimension _get_dim( index idx ) const { + return dims[ idx ]; + } + void _set_dim( index idx, dimension dim ) { + dims[ idx ] = dim; + } + + // replaces(!) content of 'col' with boundary of given index + void _get_col( index idx, column& col ) const { + col.clear(); + col.reserve( matrix[idx].size() ); + std::copy (matrix[idx].begin(), matrix[idx].end(), std::back_inserter(col) ); + } + void _set_col( index idx, const column& col ) { + matrix[ idx ].clear(); + matrix[ idx ].insert( col.begin(), col.end() ); + } + + // true iff boundary of given idx is empty + bool _is_empty( index idx ) const { + return matrix[ idx ].empty(); + } + + // largest row index of given column idx (new name for lowestOne()) + index _get_max_index( index idx ) const { + return matrix[ idx ].empty() ? -1 : *matrix[ idx ].rbegin(); + } + + // removes the maximal index of a column + void _remove_max( index idx ) { + std::set< index >::iterator it = matrix[ idx ].end(); + it--; + matrix[ idx ].erase( it ); + } + + // clears given column + void _clear( index idx ) { + matrix[ idx ].clear(); + } + + // syncronizes all data structures (essential for openmp stuff) + void _sync() {} + + // adds column 'source' to column 'target' + void _add_to( index source, index target ) { + for( std::set< index >::iterator it = matrix[ source ].begin(); it != matrix[ source ].end(); it++ ) { + std::set< index >& col = matrix[ target ]; + std::pair< std::set< index >::iterator, bool > result = col.insert( *it ); + if( !result.second ) + col.erase( result.first ); + } + } + + // finalizes given column + void _finalize( index idx ) { + } + + }; +} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/representations/vector_vector.h b/src/Zigzag_persistence/example/ext_zz/phat/representations/vector_vector.h new file mode 100644 index 0000000000..f111d6b572 --- /dev/null +++ b/src/Zigzag_persistence/example/ext_zz/phat/representations/vector_vector.h @@ -0,0 +1,107 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include + +namespace phat { + class vector_vector { + + protected: + std::vector< dimension > dims; + std::vector< column > matrix; + + thread_local_storage< column > temp_column_buffer; + + public: + // overall number of cells in boundary_matrix + index _get_num_cols() const { + return (index)matrix.size(); + } + void _set_num_cols( index nr_of_columns ) { + dims.resize( nr_of_columns ); + matrix.resize( nr_of_columns ); + } + + // dimension of given index + dimension _get_dim( index idx ) const { + return dims[ idx ]; + } + void _set_dim( index idx, dimension dim ) { + dims[ idx ] = dim; + } + + // replaces(!) content of 'col' with boundary of given index + void _get_col( index idx, column& col ) const { + col = matrix[ idx ]; + } + void _set_col( index idx, const column& col ) { + matrix[ idx ] = col; + } + + // true iff boundary of given idx is empty + bool _is_empty( index idx ) const { + return matrix[ idx ].empty(); + } + + // largest row index of given column idx (new name for lowestOne()) + index _get_max_index( index idx ) const { + return matrix[ idx ].empty() ? -1 : matrix[ idx ].back(); + } + + // removes the maximal index of a column + void _remove_max( index idx ) { + matrix[ idx ].pop_back(); + } + + // clears given column + void _clear( index idx ) { + matrix[ idx ].clear(); + } + + // syncronizes all data structures (essential for openmp stuff) + void _sync() {} + + // adds column 'source' to column 'target' + void _add_to( index source, index target ) { + column& source_col = matrix[ source ]; + column& target_col = matrix[ target ]; + column& temp_col = temp_column_buffer(); + + + size_t new_size = source_col.size() + target_col.size(); + + if (new_size > temp_col.size()) temp_col.resize(new_size); + + std::vector::iterator col_end = std::set_symmetric_difference( target_col.begin(), target_col.end(), + source_col.begin(), source_col.end(), + temp_col.begin() ); + temp_col.erase(col_end, temp_col.end()); + + + target_col.swap(temp_col); + } + + // finalizes given column + void _finalize( index idx ) { + column& col = matrix[ idx ]; + column(col.begin(), col.end()).swap(col); + } + }; +} diff --git a/src/Zigzag_persistence/example/rips-zigzag-dionysus.h b/src/Zigzag_persistence/example/rips-zigzag-dionysus.h new file mode 100644 index 0000000000..62db6d834d --- /dev/null +++ b/src/Zigzag_persistence/example/rips-zigzag-dionysus.h @@ -0,0 +1,211 @@ +#include +#include +#include +#include + +#include +namespace ba = boost::adaptors; + +#include +#include +#include +namespace d = dionysus; + +#include + +typedef std::vector Point; +typedef std::vector PointContainer; + +typedef d::PairwiseDistances> PairDistances; +typedef PairDistances::DistanceType DistanceType; +typedef PairDistances::IndexType Vertex; + +typedef d::Rips Generator; +typedef Generator::Simplex Simplex; +typedef std::set SimplexSet; + +typedef std::vector VertexVector; +typedef std::vector EpsilonVector; +typedef std::tuple Edge; +typedef std::vector EdgeVector; + +inline PointContainer compute_points(unsigned int numberOfPoints, int seed = -1) +{ + PointContainer finalPoints; + std::set points; + std::random_device dev; + std::mt19937 rng(dev()); + if (seed > -1) rng.seed(seed); + std::uniform_real_distribution dist(0,10); + + for (unsigned int i = 0; i < numberOfPoints; ++i){ + auto res = points.insert({dist(rng), dist(rng)}); + while(!res.second){ + res = points.insert({dist(rng), dist(rng)}); + } + finalPoints.push_back(*res.first); + } + + return finalPoints; +} + +inline void compute_vertices_and_epsilons(const PairDistances& distances, + VertexVector& vertices, + EpsilonVector& epsilons) +{ + DistanceType inf = std::numeric_limits::infinity(); + EpsilonVector dist(distances.size(), inf); + + vertices.push_back(distances.begin()); + // epsilons.push_back(inf); + while (vertices.size() < distances.size()) { + for (Vertex v = distances.begin(); v != distances.end(); ++v) + dist[v] = std::min(dist[v], distances(v, vertices.back())); + auto max = std::max_element(dist.begin(), dist.end()); + vertices.push_back(max - dist.begin()); + epsilons.push_back(*max); + } + epsilons.push_back(0); +} + +inline void compute_edges(const PairDistances& distances, + const VertexVector& vertices, + const EpsilonVector& epsilons, + const DistanceType& multiplier, + EdgeVector& edges) +{ + for (unsigned i = 0; i != vertices.size(); ++i) + for (unsigned j = i + 1; j != vertices.size(); ++j) { + Vertex u = vertices[i]; + Vertex v = vertices[j]; + if (distances(u, v) <= multiplier * epsilons[j - 1]) edges.emplace_back(u, v); + } + std::sort(edges.begin(), edges.end(), + [&distances](const Edge& e1, const Edge& e2) { + return distances(std::get<0>(e1), std::get<1>(e1)) < + distances(std::get<0>(e2), std::get<1>(e2)); + }); +} + +inline void compute_positive_cofaces( + const PairDistances& distances, + const VertexVector& vertices, + const EdgeVector& edges, + const EpsilonVector& epsilons, + const DistanceType& multiplier, + Generator& rips, + short unsigned& skeleton, + unsigned& ce, + unsigned& i, + SimplexSet& cofaces) +{ + cofaces.clear(); + + // Add anything else that needs to be inserted into the complex + while (ce < edges.size()) { + Vertex u, v; + std::tie(u, v) = edges[ce]; + if (distances(u, v) <= multiplier * epsilons[i - 1]) + ++ce; + else + break; + // std::cout << "Adding cofaces of " << u << ' ' << v << std::endl; + rips.edge_cofaces( + u, v, + skeleton, + multiplier * epsilons[i - 1], + [&cofaces](Simplex&& s) { cofaces.insert(s); }, + vertices.begin(), + vertices.begin() + i + 1); + } +} + +inline void compute_negative_cofaces( + const VertexVector& vertices, + const EpsilonVector& epsilons, + const DistanceType& multiplier, + Generator& rips, + short unsigned& skeleton, + unsigned& i, + SimplexSet& cofaces) +{ + cofaces.clear(); + rips.vertex_cofaces( + vertices[i], + skeleton, + multiplier * epsilons[i - 1], + [&cofaces](Simplex&& s) { cofaces.insert(s); }, + vertices.begin(), + vertices.begin() + i + 1); + // std::cout << "Total cofaces: " << cofaces.size() << std::endl; +} + +inline unsigned int build_rips_zigzag_filtration(std::vector > &simpls, + std::vector& dirs, + unsigned int numberOfPoints, + int seed = -1, + short unsigned skeleton = 2, + DistanceType multiplier = 6) +{ + // std::cout << "Building filtration" << std::endl; + unsigned int numberOfSimplices = 0; + + PointContainer points = compute_points(numberOfPoints, seed); + + // Construct distances and Rips generator + PairDistances distances(points); + Generator rips(distances); + + // Order vertices and epsilons (in maxmin fashion) + VertexVector vertices; + EpsilonVector epsilons; + EdgeVector edges; + + compute_vertices_and_epsilons(distances, vertices, epsilons); + + // Generate and sort all the edges + compute_edges(distances, vertices, epsilons, multiplier, edges); + + // Insert vertices + for (auto v : vertices) { + // Add a vertex + simpls.push_back({static_cast(v)}); + dirs.push_back(true); + ++numberOfSimplices; + } + + // Process vertices + dlog::progress progress(vertices.size()); + unsigned ce = 0; // index of the current one past last edge in the complex + SimplexSet cofaces; // record the cofaces of all the simplices that need to be removed and reinserted + + for (unsigned stage = 0; stage != vertices.size() - 1; ++stage) { + unsigned i = vertices.size() - 1 - stage; + + /* Increase epsilon */ + compute_positive_cofaces(distances, vertices, edges, epsilons, multiplier, rips, skeleton, ce, i, cofaces); + + for (auto& s : cofaces) { + // std::cout << "Inserting: " << s << std::endl; + simpls.emplace_back(s.begin(), s.end()); + dirs.push_back(true); + ++numberOfSimplices; + } + + /* Remove the vertex */ + // std::cout << "Removing vertex: " << vertices[i] << std::endl; + compute_negative_cofaces(vertices, epsilons, multiplier, rips, skeleton, i, cofaces); + + for (auto& s : cofaces | ba::reversed) { + // std::cout << "Removing: " << s << std::endl; + simpls.emplace_back(s.begin(), s.end()); + dirs.push_back(false); + } + + ++progress; + } + + std::cout << std::endl; + return numberOfSimplices; +} diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h new file mode 100644 index 0000000000..bccf73ab03 --- /dev/null +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h @@ -0,0 +1,725 @@ +/* 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): Clément Maria + * + * Copyright (C) 2021 Inria + * + * Modification(s): + * - 2023/05 Hannah Schreiber: Rework of the interface, reorganization and debug + * - 2023/05 Hannah Schreiber: Addition of infinit bars + * - YYYY/MM Author: Description of the modification + */ + +#ifndef ZIGZAG_PERSISTENCE_H_ +#define ZIGZAG_PERSISTENCE_H_ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace Gudhi { +namespace zigzag_persistence { +//---------------------------------------------------------------------------------- +/** \class Zigzag_persistence Zigzag_persistence.h gudhi/Zigzag_persistence.h + * \brief Computation of the zigzag persistent homology of a zigzag + * filtered complex. + * + * \details The type ZigzagFilteredComplex::Simplex_key counts the number of + * insertions and + * deletions of simplices, which may be large in zigzag persistence and require + * more than 32 bits of storage. The type used (int, long, etc) should be chosen in + * consequence. Simplex_key must be signed. + * + * Over all insertions, the Simplex_key must be positive and strictly increasing + * when forward iterating along the zigzag filtration. + */ +template < typename ZigzagFilteredComplex + , typename ZigzagPersistenceOptions = Gudhi::persistence_matrix::Zigzag_options<> > +class Zigzag_persistence { +public: + typedef ZigzagFilteredComplex Complex; + typedef ZigzagPersistenceOptions Options; + /*** Types defined in the complex ***/ + // Data attached to each simplex to interface with a Property Map. + typedef typename Complex::Simplex_key Simplex_key;//must be signed + typedef typename Complex::Simplex_handle Simplex_handle; + typedef typename Complex::Vertex_handle Vertex_handle; + typedef typename Complex::Filtration_value Filtration_value; + // + + /** \brief Structure to store persistence intervals by their filtration values. + * + * \details By convention, interval \f$[b;d]\f$ are + * closed for finite indices b and d, and open for left-infinite and/or + * right-infinite endpoints.*/ + struct interval_filtration { + interval_filtration() {} + interval_filtration(int dim, Filtration_value b, Filtration_value d) : dim_(dim), b_(b), d_(d) {} + /** Returns the absolute length of the interval \f$|d-b|\f$. */ + Filtration_value length() { + if(b_ == d_) { return 0; } //otherwise inf - inf would return nan. + return std::abs(b_ - d_); + } + /** Returns the absolute length of the log values of birth and death, i.e. \f$|\log d - \log b|\f$.. */ + Filtration_value log_length() {//return the log-length + if(b_ == d_) { return 0; } //otherwise inf - inf would return nan. + return std::abs(log2((double)b_) - log2((double)d_)); + } + /** Returns the dimension of the homological feature corresponding to the + * interval. */ + int dim() const { return dim_; }//return the homological dimension of the interval + /** Returns the birth of the interval.*/ + Filtration_value birth() const { return b_; }//return the birth value + /** Returns the death of the interval.*/ + Filtration_value death() const { return d_; }//return the death value + /** Swaps the values of birth and death.*/ + void swap_birth_death() { std::swap(b_,d_); } + + private://note that we don't assume b_ <= d_ + int dim_; //homological dimension + Filtration_value b_; //filtration value associated to birth index + Filtration_value d_; //filtration value associated to death index + }; + + /** \brief Structure to store persistence intervals by their index values. + * + * \details By convention, interval [b;d] are + * closed for finite indices b and d, and open for left-infinite and/or + * right-infinite endpoints. + */ + struct interval_index { + interval_index() {} + interval_index(int dim, Simplex_key b, Simplex_key d) : dim_(dim), b_(b), d_(d) {} + /** Returns the dimension of the homological feature corresponding to the + * interval. */ + int dim() const { return dim_; }//return the homological dimension of the interval + /** Returns the birth index of the interval.*/ + Filtration_value birth() const { return b_; }//return the birth value + /** Returns the death index of the interval.*/ + Filtration_value death() const { return d_; }//return the death value + + private://note that we don't assume b_ <= d_ + int dim_; //homological dimension + Simplex_key b_; //filtration value associated to birth index + Simplex_key d_; //filtration value associated to death index + }; + +private: + /* Comparison function to sort intervals by decreasing log-length in the + * output persistence diagram, i.e., + * [f(b),f(d)]<[f(b'),f(d')] iff |log2(f(b))-log2(f(d))|> |log2(f(b'))-log2(f(d'))| + */ + struct cmp_intervals_by_log_length { + cmp_intervals_by_log_length(){} + bool operator()( interval_filtration p, interval_filtration q) + { + if(p.dim() != q.dim()) {return p.dim() < q.dim();}//lower dimension first + if(p.log_length() != q.log_length()) {return p.log_length() > q.log_length();} + if(p.birth() != q.birth()) {return p.birth() < q.birth();}//lex order + return p.death() < q.death(); + } + }; + /* Comparison function to sort intervals by decreasing length in the + * output persistence diagram, i.e., + * [f(b),f(d)]<[f(b'),f(d')] iff |f(b)-f(d)| > |f(b')-f(d')| + */ + struct cmp_intervals_by_length { + cmp_intervals_by_length(){} + bool operator()( interval_filtration p, interval_filtration q) + { + if(p.length() != q.length()) { return p.length() > q.length(); }//longest 1st + if(p.dim() != q.dim()) {return p.dim() < q.dim();}//lower dimension first + if(p.birth() != q.birth()) {return p.birth() < q.birth();}//lex order + return p.death() < q.death(); + } + }; + + using matrix_type = Gudhi::persistence_matrix::Matrix; + using index = Gudhi::persistence_matrix::index; + +public: + /** \brief Initialization of the Zigzag_persistence class. + * + * \param[in] cpx A model of ZigzagFilteredComplex. + * */ + Zigzag_persistence(unsigned int min_number_of_simplices = 0, int ignore_cycles_above_dim = -1) + : cpx_() + , dim_max_(ignore_cycles_above_dim) + , matrix_(min_number_of_simplices, + [this](index columnIndex1, index columnIndex2){ + return birth_ordering_.birth_order(births_.at(columnIndex1), births_.at(columnIndex2)); + }) + , birth_ordering_() + , persistence_diagram_() + , num_arrow_(-1) + , previous_filtration_value_(std::numeric_limits::infinity()) + , filtration_values_() {} + +private: + + /** Maintains the birth ordering <=b. Contains an std::map of size the number of + * non-zero rows of the homology matrix, at any time during the computation of + * zigzag persistence. + * + * By construction, we maintain the map satisfying + * 'birth_to_pos_[i] < birth_to_pos_[j]', + * with 0 <= i,j <= k indices in the quiver '0 \leftrightarrow ... \leftrightarrow i \leftrightarrow .. \leftrightarrow k' + * visited at time k of the algorithm (prefix of length k of the full zigzag + * filtration '0 \leftrightarrow ... \leftrightarrow i \leftrightarrow .. \leftrightarrow k \leftrightarrow ... \leftrightarrow n' that is studied), + * iff i k+1 forward, then j 0 -> 1 -> 2 <- 3 <- 4 -> 5 <- 6 etc + birth_ordering() : birth_to_pos_(), max_birth_pos_(0), min_birth_pos_(-1) {} + + //when the arrow key-1 -> key is forward, key is larger than any other index + //i < key in the birth ordering b k2 + bool reverse_birth_order(Simplex_key k1, Simplex_key k2) { + return birth_to_pos_[k1] > birth_to_pos_[k2]; + } + + private: + //birth_to_pos_[i] < birth_to_pos_[j] iff i birth_to_pos_; + //by construction, max_birth_pos_ (resp. min_birth_pos_) is strictly larger + //(resp. strictly smaller) than any value assigned to a key so far. + Simplex_key max_birth_pos_; + Simplex_key min_birth_pos_; + }; + +public: + /** \brief Computes the zigzag persistent homology of a zigzag filtered complex, + * using the reflection and transposition algorithm of \cite zigzag_reflection. + * + * \details After computation, the persistence diagram can be accessed via + * member method persistence_diagram, for the diagram with filtration + * values, or member method index_persistence_diagram, for the + * diagram with + * indices of paired simplices. + * + * + * matrix_, originally empty, maintains the set of chains, with a + * partition \f$ F \sqcup G \sqcup H\f$ + * representing a compatible homology basis as in \cite zigzag_reflection. + * + * Each simplex in the complex stores a key field that stores the index of + * its insertion in the zigzag filtration. + * + * The algorithm maintains a compatible homology basis for the zigzag filtration. + * + * \f$$\emptyset = K_0 \leftrightarrow (...) \leftrightarrow K_i \leftarrow ... \leftarrow \emptyset\f$$ + * + * where the prefix from \f$K_0\f$ to \f$K_i\f$ is equal to the i-th prefix of + * the input zigzag + * filtration given by cpx_.filtration_simplex_range(), and + * the suffix + * (from \f$K_i\f$ + * to the right) is a sequence of simplex removals. Due to the structure of + * reflection diamonds, the removals are in reverse order of the insertions, to + * reduce the amount of transposition diamonds. + * + * Consequently, using cpx_.key(zzsh) as indexing for the matrix + * rows/cells, + * with the natural order on integers, makes our homology matrix matrix_ upper + * triangular for the suffix \f$K_i \leftarrow ... \leftarrow 0\f$, seen as a + * standard persistence + * filtration. At \f$K_i\f$, the natural order on integers is also equivalent to the + * death-order \f$\leq_d\f$ (because all arrows in the suffix are backward). + * + * Insertion: cpx_.key(*zzit) is a strictly increasing sequence + * for zzit + * insertion of cells (does not need to be contiguous). However, for every forward + * arrow, we have cpx_.key(*zzit) == num_arrows_. + * Removal: cpx_.key(*zzit) gives the assigned key (during past + * insertion) of a + * cell == *zzit during a removal. We use num_arrows_ + * to record the deaths in the + * persistence diagram. + * Insertion and Removal: zzit.filtration() is totally monotone. + * Note that the + * iterator encodes the filtration, and not the cells within the complex structure. + */ + + template> + void insert_simplex(const VertexRange& simplex, Filtration_value filtration_value) + { + if (dim_max_ != -1 && simplex.size() > static_cast(dim_max_) + 1) return; + + ++num_arrow_; + + if (filtration_value != previous_filtration_value_) //check whether the filt value has changed + { //consecutive pairs (i,f), (j,f') mean simplices of index k in [i,j-1] have + previous_filtration_value_ = filtration_value; //filtration value f + filtration_values_.emplace_back(num_arrow_, previous_filtration_value_); + } + + std::pair res = cpx_.insert_simplex(simplex, filtration_value); + GUDHI_CHECK(res.second, "Zigzag_persistence::insert_simplex - insertion of a simplex already in the complex"); + cpx_.assign_key(res.first, num_arrow_); + forward_arrow(res.first); + } + + template> + void remove_simplex(const VertexRange& simplex, Filtration_value filtration_value) + { + if (dim_max_ != -1 && simplex.size() > static_cast(dim_max_) + 1) return; + + ++num_arrow_; + + Simplex_handle sh = cpx_.find(simplex); + GUDHI_CHECK(sh != cpx_.null_simplex(), "Zigzag_persistence::remove_simplex - removal of a simplex not in the complex"); + + if (filtration_value != previous_filtration_value_) //check whether the filt value has changed + { //consecutive pairs (i,f), (j,f') mean simplices of index k in [i,j-1] have + previous_filtration_value_ = filtration_value; //filtration value f + filtration_values_.emplace_back(num_arrow_, previous_filtration_value_); + } + + backward_arrow(sh); + cpx_.remove_maximal_simplex(sh); + } + + template>, + class FiltrationRange = std::initializer_list> + void insert_simplices_contiguously(const SimplexRange& simplices, const FiltrationRange& filtration_values) + { + auto simplexIt = simplices.begin(); + auto filIt = filtration_values.begin(); + for (; simplexIt != simplices.end(); ++simplexIt, ++filIt) { + insert_simplex(*simplexIt, *filIt); + } + } + + template>, + class FiltrationRange = std::initializer_list> + void remove_simplices_contiguously(const SimplexRange& simplices, const FiltrationRange& filtration_values) + { + auto simplexIt = simplices.begin(); + auto filIt = filtration_values.begin(); + for (; simplexIt != simplices.end(); ++simplexIt, ++filIt) { + remove_simplex(*simplexIt, *filIt); + } + } + + template + void insert_simplices_contiguously(SimplexRangeIterators simplex_range_start, + SimplexRangeIterators simplex_range_end, + FiltrationRangeIterators filtration_range_start) + { + for (; simplex_range_start != simplex_range_end; ++simplex_range_start, ++filtration_range_start) { + insert_simplex(*simplex_range_start, *filtration_range_start); + } + } + + template + void remove_simplices_contiguously(SimplexRangeIterators simplex_range_start, + SimplexRangeIterators simplex_range_end, + FiltrationRangeIterators filtration_range_start) + { + for (; simplex_range_start != simplex_range_end; ++simplex_range_start, ++filtration_range_start) { + remove_simplex(*simplex_range_start, *filtration_range_start); + } + } + + void print_current_complex(){ + for (auto& sh : cpx_.complex_simplex_range()){ + for (auto v : cpx_.simplex_vertex_range(sh)){ + std::cout << v << " "; + } + std::cout << " - " << cpx_.filtration(sh) << "\n"; + } + } + +private: + /** \brief Computes the boundary cycle of the new simplex zzsh, and express it as a + * sum of cycles. If all cycles are boundary cycles, i.e., columns with G-index + * in the matrix, then [\partial zzsh] = 0 and we apply an injective diamond to + * the zigzag module. Otherwise, we keep reducing with boundary- and live- cycles, + * i.e., columns with (F \cup G)-indices, and then apply a surjective diamond to + * the zigzag module. + */ + void forward_arrow( Simplex_handle zzsh ) + { //maintain the <=b order + birth_ordering_.add_birth_forward(num_arrow_); + + //Reduce the boundary of zzsh in the basis of cycles. + //Compute the simplex keys of the simplices of the boundary of zzsh. + std::set< Simplex_key > col_bsh; //set maintains the natural order on indices + for( auto b_sh : cpx_.boundary_simplex_range(zzsh) ) + { col_bsh.insert(cpx_.key(b_sh)); } + + std::vector chains_in_F; + matrix_.insert_boundary(num_arrow_, col_bsh, chains_in_F); + + if (!chains_in_F.empty()){ + births_.try_emplace(matrix_.get_column_with_pivot(num_arrow_), -2); + surjective_reflection_diamond(zzsh, chains_in_F); + } else { + births_.try_emplace(matrix_.get_column_with_pivot(num_arrow_), num_arrow_); + } + } + + /** The vector chains_in_F is sorted by decreasing lowest index values in the + * columns corresponding to the chains, due to its computation in the reduction of + * \partial zzsh in forward_arrow(...). It is equivalent to decreasing death index + * order w.r.t. the & chains_in_F) + { //fp is the largest death index for <=d + //Set col_fp: col_fp <- col_f1+...+col_fp (now in G); preserves lowest idx + auto chain_fp = chains_in_F.front(); //col_fp, with largest death bool + { return birth_ordering_.reverse_birth_order(k1,k2); };//true iff b(k1) >b b(k2) + + //available_birth: for all i by >d value of the d_i, + //contains at step i all b_j, j > i, and maybe b_i if not stolen + std::set< Simplex_key, decltype(cmp_birth) > available_birth(cmp_birth); + //for f1 to f_{p} (i by <=d), insertion in available_birth_to_fidx sorts by >=b + for(auto &chain_f : chains_in_F) { available_birth.insert(births_.at(chain_f)); } + + auto maxb_it = available_birth.begin();//max birth cycle + auto maxb = *maxb_it; //max birth value, for persistence diagram + available_birth.erase(maxb_it); //remove max birth cycle (stolen) + + auto last_modified_chain_it = chains_in_F.rbegin(); + + //consider all death indices by increasing the maximal availabe death. + //Let c_1 ... c_f be the chains s.t. <[c_1+...+c_f]> is the kernel and + // death(c_i) >d death(c_i-1). If the birth of c_i is not available, we set + //c_i <- c_i + c_i-1 + ... + c_1, which is [c_i + c_i-1 + ... + c_1] on + //the right (of death the maximali <=> the maxj>k, are indeed c_j + //set c_i <- c_i + (c_i-1) + ... + (c_k+1) + (c_k + ... + c_1) + for(auto chain_passed_it = last_modified_chain_it;//all with smaller modified_columns; + const auto& row = matrix_.get_row(cpx_.key(zzsh)); + modified_columns.reserve(row.size()); + std::transform(row.begin(), + row.end(), + std::back_inserter(modified_columns), + [](const auto& cell) { return cell.get_column_index(); }); + //Sort by left-to-right order in the matrix_ (no order maintained in rows) + std::stable_sort(modified_columns.begin(),modified_columns.end(), + [this](index i1, index i2){ + return matrix_.get_pivot(i1) < matrix_.get_pivot(i2); + }); + + //Modifies the pointer curr_col, not the other one. + for(auto other_col_it = std::next(modified_columns.begin()); + other_col_it != modified_columns.end(); ++other_col_it) + { + // index ci = other_col_it->get_column_index(); + // ++other_col_it; //vine swap unvalidates iterator + curr_col = matrix_.vine_swap_with_z_eq_1_case(curr_col, *other_col_it); + } + + //curr_col points to the column to remove by restriction of K to K-{\sigma} + if(!matrix_.get_column(curr_col).is_paired()) { // in F + int dim_zzsh = cpx_.dimension(zzsh); + if(dim_max_ == -1 || (dim_max_ != -1 && dim_zzsh < dim_max_)) { //don't record intervals of max dim + persistence_diagram_.emplace_back( dim_zzsh + , births_.at(curr_col) + , num_arrow_);// -1); + } + } + else { //in H -> paired with c_g, that now belongs to F now + births_.at(matrix_.get_column(curr_col).get_paired_chain_index()) = num_arrow_; + } + + //cannot be in G as the removed simplex is maximal + matrix_.remove_maximal_simplex(cpx_.key(zzsh)); + } + +public: + /** \brief Returns the index persistence diagram as an std::list of intervals.*/ + const std::list< interval_index > & index_persistence_diagram() const + { + return persistence_diagram_; + } + + /** \brief Returns the filtration values \f$[f(b),f(d)]\f$ (generally real-valued) + * associated to the indices \f$[b,d]\f$ (integer valued) of the insertion or + * deletion of a simplex in the zigzag filtration. + * + * \details Used to convert a persistent interval \f$[b,d]\f$, computed by the + * persistent homology algorithm, into its filtration valued version + * \f$[f(b),f(d)]\f$ used in the persistence barcode. The information + * index->filtration is stored in the member filtration_values_ of + * the class Zigzag_persistence. + * + * @param[in] b_key, d_key The indices of birth and death of a persistent + * interval. + * + * @param[out] std::pair A pair of real values \f$(f(b),f(d))\f$. + */ + std::pair index_to_filtration( + Simplex_key b_key, Simplex_key d_key) { + // filtration_values_ must be sorted by increasing keys. + auto it_b = //lower_bound(x) returns leftmost y s.t. x <= y + std::lower_bound( filtration_values_.begin(), filtration_values_.end() + , std::pair(b_key + , std::numeric_limits::infinity() ) + , []( std::pair p1 + , std::pair p2) + { return p1.first < p2.first; } + ); + if(it_b == filtration_values_.end() || it_b->first > b_key) { --it_b; } + //it points to the rightmost z such that z <= x + + auto it_d = // + std::lower_bound( filtration_values_.begin(), filtration_values_.end() + , std::pair(d_key + , std::numeric_limits::infinity() ) + , []( std::pair p1 + , std::pair p2) + { return p1.first < p2.first; } + ); + if(it_d == filtration_values_.end() || it_d->first > d_key) { --it_d; } + + return std::make_pair(it_b->second, it_d->second); + } + + /** \brief Writes the persistence diagram in a file. + * + * \details The filtration values are given by the zigzag persistence iterator, that assigns + * to any insertion or deletion of a simplex a filtration value ; we say that an + * arrow has an index \f$i\f$, and a corresponding filtration value \f$f(i)\f$. + * Reading a zigzag filtration from left to right, indices are strictly + * monotonically increasing, and the associated filtration values are monotonous + * (not necessarily + * strictly, either increasing or decreasing). + * + * Consider two consecutive arrows (insertion or deletion): + * + * \f$$K_1 \leftrightarrow K_2 \leftrightarrow K_3\f$$ + * + * with respectively indices \f$i\f$ (left) and \f$i+1\f$ (right), and associated + * filtration values \f$f(i)\f$ and \f$f(i+1)\f$ respectively. + * + * If, the arrow \f$K_2 \leftrightarrow K_3\f$ leads to the creation of a new + * homology feature in \f$K_3\f$, it creates an (indexed) persistent interval + * \f$[\f$i+1; \cdot\f$, and a corresponding (filtration) persistent interval + * \f$[f(i+1); \cdot]\f$ in the persistence diagram. + * + * If a homology feature in \f$K_2\f$ is destroyed by the arrow \f$K_2 \leftrightarrow K_3\f$, it closes an (indexed) + * interval \f$[\cdot ; i+1]\f$, and a corresponding (filtration) persistent + * interval \f$[\cdot ; f(i+1)]\f$ in the persistence diagram. + * + * For example, in an oscillating Rips zigzag filtration, if, in the following + * chunk of filtration: + * + * \f$R_{\eta \varepsilon_i}(P_i) \rightarrow \cdots \leftarrow R_{\eta \varepsilon_{i+1}}(P_{i+1}),\f$ + * + * if anything is created by any of the arrows above, it leads to an interval + * \f$[\varepsilon_{i+1}; \cdot]\f$. If anything is destroyed by any of the arrows + * above, if leads to an interval \f$[\cdot;\varepsilon_i]\f$. Note that we may + * have \f$\varepsilon_i > \varepsilon_{i+1}\f$. + * + * The bars are ordered by decreasing length. + * + * @param[in] os the output stream in which the diagram is written. + * @param[in] shortest_interval all intervals of lenght smaller or equal to + * this value are ignore. Default is 0. + */ + void persistence_diagram( std::ostream& os + , Filtration_value shortest_interval = 0.) { + + std::stable_sort(filtration_values_.begin(), filtration_values_.end(), + []( std::pair< Simplex_key, Filtration_value > p1 + , std::pair< Simplex_key, Filtration_value > p2 ) + { return p1.first < p2.first; } + ); + + std::vector< interval_filtration > tmp_diag; + tmp_diag.reserve(persistence_diagram_.size()); + for(auto bar : persistence_diagram_) + { + Filtration_value birth,death; + std::tie(birth,death) = index_to_filtration(bar.birth(), bar.death()); + + if( std::abs(birth - death) > shortest_interval ) { + tmp_diag.emplace_back(bar.dim(), birth, death ); + } + } + // cmp_intervals_by_length cmp; + std::stable_sort(tmp_diag.begin(), tmp_diag.end(), cmp_intervals_by_length()); + + os << "# dim birth death [length]\n"; + for(auto bar : tmp_diag) { + if(bar.birth() > bar.death()) { bar.swap_birth_death(); } + os << bar.dim() << " " << bar.birth() << " " << bar.death() << + " - [" << bar.length() << "] \n"; + } + } + + /** \brief Returns the persistence diagram as a vector of real-valued intervals. */ + std::vector< interval_filtration > + persistence_diagram(Filtration_value shortest_interval = 0., bool include_infinit_bars = false) + { + std::stable_sort(filtration_values_.begin(), filtration_values_.end(), + []( std::pair< Simplex_key, Filtration_value > p1 + , std::pair< Simplex_key, Filtration_value > p2 ) + { return p1.first < p2.first; } + ); + + std::vector< interval_filtration > diag; + diag.reserve(persistence_diagram_.size()); + for(auto bar : persistence_diagram_) + { + Filtration_value birth,death; + std::tie(birth,death) = index_to_filtration(bar.birth(), bar.death()); + + if( std::abs(birth - death) > shortest_interval ) { + diag.emplace_back(bar.dim(), birth, death ); + } + } + //put lower value as birth + for(auto &bar : diag) { + if( bar.birth() > bar.death() ) { bar.swap_birth_death(); } + } + std::stable_sort(diag.begin(), diag.end(), cmp_intervals_by_length()); + + auto birth = + [this](Simplex_key b_key) { + auto it_b = // lower_bound(x) returns leftmost y s.t. x <= y + std::lower_bound(filtration_values_.begin(), filtration_values_.end(), + std::pair( + b_key, std::numeric_limits::infinity()), + [](std::pair p1, + std::pair p2) { return p1.first < p2.first; }); + if (it_b == filtration_values_.end() || it_b->first > b_key) { + --it_b; + } + return it_b->second; + }; + + //TODO: better recording? + if (include_infinit_bars) { + for (unsigned int i = 0; i < matrix_.get_number_of_columns(); ++i) { + const auto& col = matrix_.get_column(i); + if (!col.is_paired()) + diag.emplace_back(col.get_dimension(), birth(col.get_pivot()), std::numeric_limits::infinity()); + } + } + + return diag; + } + +private: + Complex cpx_; // complex + int dim_max_;//max dim complex + matrix_type matrix_; // 0 ... m-1 + std::unordered_map births_; + birth_ordering birth_ordering_; + std::list< interval_index > persistence_diagram_; + Simplex_key num_arrow_; //current index + Filtration_value previous_filtration_value_; + // filtration_values stores consecutive pairs (i,f) , (j,f') with f != f', + // meaning that all inserted simplices with key in [i;j-1] have filtration value f + //i is the smallest simplex index whose simplex has filtration value f. + std::vector< std::pair< Simplex_key, Filtration_value > > filtration_values_; +};//end class Zigzag_persistence + +} //namespace zigzag_persistence + +} //namespace Gudhi + +#endif //ZIGZAG_PERSISTENCE_H_ + diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence_old.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence_old.h new file mode 100644 index 0000000000..c364fc10c2 --- /dev/null +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence_old.h @@ -0,0 +1,1326 @@ +/* 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): Clément Maria + * + * Copyright (C) 2021 Inria + * + * Modification(s): + * - 2023/05 Hannah Schreiber: Rework of the interface, reorganization and debug + * - 2023/05 Hannah Schreiber: Addition of infinit bars + * - YYYY/MM Author: Description of the modification + */ + +#ifndef ZIGZAG_PERSISTENCE_H_ +#define ZIGZAG_PERSISTENCE_H_ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace Gudhi { +namespace zigzag_persistence { +//represent matrix columns with sets. +struct Zigzag_persistence_colset; +//---------------------------------------------------------------------------------- +/** \class Zigzag_persistence Zigzag_persistence.h gudhi/Zigzag_persistence.h + * \brief Computation of the zigzag persistent homology of a zigzag + * filtered complex. + * + * \details The type ZigzagFilteredComplex::Simplex_key counts the number of + * insertions and + * deletions of simplices, which may be large in zigzag persistence and require + * more than 32 bits of storage. The type used (int, long, etc) should be chosen in + * consequence. Simplex_key must be signed. + * + * Over all insertions, the Simplex_key must be positive and strictly increasing + * when forward iterating along the zigzag filtration. + */ +template < typename ZigzagFilteredComplex + , typename ZigzagPersistenceOptions = Zigzag_persistence_colset > +class Zigzag_persistence { +public: + typedef ZigzagFilteredComplex Complex; + typedef ZigzagPersistenceOptions Options; + /*** Types defined in the complex ***/ + // Data attached to each simplex to interface with a Property Map. + typedef typename Complex::Simplex_key Simplex_key;//must be signed + typedef typename Complex::Simplex_handle Simplex_handle; + typedef typename Complex::Vertex_handle Vertex_handle; + typedef typename Complex::Filtration_value Filtration_value; + // +private: + /*** Matrix cells and columns types ***/ + struct matrix_row_tag; // for horizontal traversal in the persistence matrix + struct matrix_column_tag; // for vertical traversal in the persistence matrix + typedef boost::intrusive::list_base_hook< + boost::intrusive::tag < matrix_row_tag > //allows .unlink() + , boost::intrusive::link_mode < boost::intrusive::auto_unlink > + > base_hook_matrix_row_list; + //hook for a column represented by an intrusive list + typedef boost::intrusive::list_base_hook < //faster hook, less safe + boost::intrusive::tag < matrix_column_tag > + //, boost::intrusive::link_mode < boost::intrusive::auto_unlink > + , boost::intrusive::link_mode < boost::intrusive::safe_link > + > base_hook_matrix_column_list; + //hook for a column represented by an intrusive set + typedef boost::intrusive::set_base_hook < //faster hook, less safe + boost::intrusive::tag < matrix_column_tag > + , boost::intrusive::optimize_size + //, boost::intrusive::link_mode < boost::intrusive::auto_unlink > + , boost::intrusive::link_mode < boost::intrusive::safe_link > + > base_hook_matrix_column_set; + //the data structure for columns is selected in Options::searchable_column + typedef typename std::conditional::type base_hook_matrix_column; + //the only option for rows is the intrusive list + typedef base_hook_matrix_row_list base_hook_matrix_row; + + /* Cell for the persistence matrix. Contains a key for the simplex index, and + * horizontal and vertical hooks for connections within sparse rows and columns. + */ + struct matrix_chain;//defined below, a chain contains a row, a column, and more + /** Type of cell in the sparse homology matrix. + * For now, only coefficients in Z/2Z, so only the row index (called key) is + * stored in the cell. + */ + struct Zigzag_persistence_cell + : public base_hook_matrix_row, public base_hook_matrix_column + { + Zigzag_persistence_cell(Simplex_key key, matrix_chain *self_chain) + : key_(key) + , self_chain_(self_chain) + {} + + Simplex_key key() const { return key_; } + //compare by increasing key value + friend bool operator<( const Zigzag_persistence_cell& c1 + , const Zigzag_persistence_cell& c2) { + return c1.key() < c2.key(); + } + /* In a matrix M, if M[i][j] == x not 0, we represent a cell with key_=i + * (the row index), + * self_chain_ points to the chain corresponding the j-th column, and x_=x. + * Currently, only Z/2Z coefficients are implemented, so x=1. + * + * A cell is connected to all cells of the same row, and all cells of the same + * column, via the two boost::intrusive hooks (row and column). + */ + Simplex_key key_; + matrix_chain * self_chain_; + }; + + //Homology matrix cell + typedef Zigzag_persistence_cell Cell; + // Remark: constant_time_size must be false because base_hook_matrix_row and + // base_hook_matrix_column have auto_unlink link_mode + //vertical list of cells, forming a matrix column stored as an intrusive list + typedef boost::intrusive::list < + Cell + , boost::intrusive::constant_time_size + , boost::intrusive::base_hook< base_hook_matrix_column_list > > Column_list; + //vertical list of cells, forming a matrix column stored as an intrusive set + typedef boost::intrusive::set < + Cell + , boost::intrusive::constant_time_size + , boost::intrusive::base_hook< base_hook_matrix_column_set > > Column_set; + //choice encoded in Options::searchable_column. a column can be + //iterated through, and keys are read in strictly increasing natural order. + typedef typename std::conditional< + Options::searchable_column, + Column_set, + Column_list >::type Column; + //horizontal list of cells, forming a matrix row, no particular order on keys. + typedef boost::intrusive::list < + Cell + , boost::intrusive::constant_time_size + , boost::intrusive::base_hook< base_hook_matrix_row > > Row_list; + //rows are encoded by lists. need to be sorted and traversed + typedef Row_list Row; + + /* Chain for zigzag persistence. A chain stores: + * - a matrix column (col_i) that represents the chain as a sum of simplices + * (represented by their unique key, stored in the cells of the sparse column), + * - a matrix row of all elements of index the lowest index of the column (row_i), + * - is paired with another chain, indicating its type F, G, or H, + * - has a direct access to its lowest index. + */ + struct matrix_chain { + /* Trivial constructor, birth == -3 */ + matrix_chain() : column_(nullptr), row_(nullptr), paired_col_(nullptr), + birth_(-3), lowest_idx_(-1) {} + + /* Creates a matrix chain of type F with one cell of index 'key'. */ + matrix_chain(Simplex_key key) + : paired_col_(nullptr), birth_(key), lowest_idx_(key) + { + // Cell *new_cell = new Cell(key, this); + Cell *new_cell = cellPool_.construct(key, this); + if constexpr(Options::searchable_column) { column_.insert(*new_cell); } + else { column_.push_back(*new_cell); } + row_.push_back(*new_cell); + } + /* Creates a matrix chain of type F with new cells of key indices given by a + * range. Birth and lowest indices are given by 'key'. + * The range [beg,end) must be sorted by increasing key values, the same + * order as the column_ when read from column_.begin() to column_.end(). + * + * SimplexKeyIterator value_type must be Simplex_key. + * KeyToMatrixChain must be of type + * std::map< Simplex_key, typename std::list::iterator > + */ + template< typename SimplexKeyIterator, typename KeyToMatrixChain > + matrix_chain(Simplex_key key, SimplexKeyIterator beg, SimplexKeyIterator end, KeyToMatrixChain &lowidx_to_matidx) + : paired_col_(nullptr), birth_(key), lowest_idx_(key) + { + for(SimplexKeyIterator it = beg; it != end; ++it) + { + // Cell *new_cell = new Cell(*it, this);//create a new cell + Cell *new_cell = cellPool_.construct(*it, this); + //insertion in the column + if constexpr(Options::searchable_column) { + column_.insert(column_.end(), *new_cell); //ordered range + } + else { column_.push_back(*new_cell); } + //insertion in a row, not corresponding to the row stored in this->row_. + lowidx_to_matidx[*it]->row_.push_back( *new_cell ); + } + //Add the bottom coefficient for the chain + // Cell *new_cell = new Cell(key, this); + Cell *new_cell = cellPool_.construct(key, this); + //insertion in the column, key is larger than any *it in [beg, end) above. + if constexpr(Options::searchable_column) { + column_.insert(column_.end(), *new_cell); + } + else { column_.push_back(*new_cell); } + //insertion of row_, that stores no particular order. + row_.push_back( *new_cell ); + } + + /* Creates a matrix chain of type H with new cells of key indices given by a + * range. Birth and lowest indices are given by 'key'. + * The range [beg,end) must be sorted by increasing key values, the same + * order as the column_ when read from column_.begin() to column_.end(). + * + * SimplexKeyIterator value_type must be Simplex_key. + * KeyToMatrixChain must be of type + * std::map< Simplex_key, typename std::list::iterator > + */ + template< typename SimplexKeyIterator, typename KeyToMatrixChain > + matrix_chain(Simplex_key key, matrix_chain *paired_col, SimplexKeyIterator beg, SimplexKeyIterator end, KeyToMatrixChain &lowidx_to_matidx) + : paired_col_(paired_col), birth_(-2), lowest_idx_(key) + { + for(SimplexKeyIterator it = beg; it != end; ++it) + { + // Cell * new_cell = new Cell(*it, this);//create a new cell + Cell *new_cell = cellPool_.construct(*it, this); + //insertion in the column + if constexpr(Options::searchable_column) { + column_.insert(column_.end(), *new_cell); //ordered range + } + else { column_.push_back(*new_cell); } + //insertion in a row, not corresponding to the row stored in this->row_. + lowidx_to_matidx[*it]->row_.push_back( *new_cell ); + } + //Add the bottom coefficient for the chain + // Cell * new_cell = new Cell(key, this); + Cell *new_cell = cellPool_.construct(key, this); + //insertion in the column, key is larger than any *it in [beg, end) above. + if constexpr(Options::searchable_column) { + column_.insert(column_.end(), *new_cell); + } + else { column_.push_back(*new_cell); } + //insertion of row_, that stores no particular order. + row_.push_back( *new_cell ); + } + + /* Erase the chain, all cells were allocated with operator new. */ + ~matrix_chain() + { //empty the column, call delete on all cells + for(typename Column::iterator c_it = column_.begin(); c_it != column_.end(); ) + { + auto tmp_it = c_it; ++c_it; + Cell * tmp_cell = &(*tmp_it); + tmp_it->base_hook_matrix_row::unlink(); //rm from row + column_.erase(tmp_it); + cellPool_.destroy(tmp_cell); + // delete tmp_cell; + } + } + + /* Returns the chain with which *this is paired in the F,G,H classification. + * If in F (i.e., paired with no other column), return nullptr.*/ + matrix_chain * paired_chain() const { return paired_col_; } + /* Assign a paired chain. */ + void assign_paired_chain(matrix_chain *other_col) { paired_col_ = other_col; } + /* Access the column. */ + Column & column() { return column_; } + /* Returns the birth index (b >= 0) of the chain if the column is in F. + * Returns -2 if the chain is in H, and -1 if the chain is in G. */ + Simplex_key birth() const { return birth_; } + /* Assign a birth index to the chain. */ + void assign_birth(Simplex_key b) { birth_ = b; } + void assign_birth(matrix_chain *other) { birth_ = other->birth_; } + /* Returns true iff the chain is indexed in F. */ + bool inF() const { return birth_ > -1; } + /* Returns true iff the chain is indexed in G. */ + bool inG() const { return birth_ == -1; } + /* Returns true iff the chain is indexed in H. */ + bool inH() const { return birth_ == -2; } + Simplex_key lowest_idx() const { return lowest_idx_; } + + Column column_ ; //col at index i, with lowest index i + Row row_ ; //row at index i + matrix_chain * paired_col_ ; //\in F -> nullptr, \in H -> g, \in G -> h + Simplex_key birth_ ; //\in F -> b, \in H -> -2 \in G -> -1 + Simplex_key lowest_idx_ ; //lowest_idx_ = i (upper triangular matrix) + inline static Simple_object_pool cellPool_; + }; + +public: + /** \brief Structure to store persistence intervals by their filtration values. + * + * \details By convention, interval \f$[b;d]\f$ are + * closed for finite indices b and d, and open for left-infinite and/or + * right-infinite endpoints.*/ + struct interval_filtration { + interval_filtration() {} + interval_filtration(int dim, Filtration_value b, Filtration_value d) : dim_(dim), b_(b), d_(d) {} + /** Returns the absolute length of the interval \f$|d-b|\f$. */ + Filtration_value length() { + if(b_ == d_) { return 0; } //otherwise inf - inf would return nan. + return std::abs(b_ - d_); + } + /** Returns the absolute length of the log values of birth and death, i.e. \f$|\log d - \log b|\f$.. */ + Filtration_value log_length() {//return the log-length + if(b_ == d_) { return 0; } //otherwise inf - inf would return nan. + return std::abs(log2((double)b_) - log2((double)d_)); + } + /** Returns the dimension of the homological feature corresponding to the + * interval. */ + int dim() const { return dim_; }//return the homological dimension of the interval + /** Returns the birth of the interval.*/ + Filtration_value birth() const { return b_; }//return the birth value + /** Returns the death of the interval.*/ + Filtration_value death() const { return d_; }//return the death value + /** Swaps the values of birth and death.*/ + void swap_birth_death() { std::swap(b_,d_); } + + private://note that we don't assume b_ <= d_ + int dim_; //homological dimension + Filtration_value b_; //filtration value associated to birth index + Filtration_value d_; //filtration value associated to death index + }; + + /** \brief Structure to store persistence intervals by their index values. + * + * \details By convention, interval [b;d] are + * closed for finite indices b and d, and open for left-infinite and/or + * right-infinite endpoints. + */ + struct interval_index { + interval_index() {} + interval_index(int dim, Simplex_key b, Simplex_key d) : dim_(dim), b_(b), d_(d) {} + /** Returns the dimension of the homological feature corresponding to the + * interval. */ + int dim() const { return dim_; }//return the homological dimension of the interval + /** Returns the birth index of the interval.*/ + Filtration_value birth() const { return b_; }//return the birth value + /** Returns the death index of the interval.*/ + Filtration_value death() const { return d_; }//return the death value + + private://note that we don't assume b_ <= d_ + int dim_; //homological dimension + Simplex_key b_; //filtration value associated to birth index + Simplex_key d_; //filtration value associated to death index + }; + +private: + /* Comparison function to sort intervals by decreasing log-length in the + * output persistence diagram, i.e., + * [f(b),f(d)]<[f(b'),f(d')] iff |log2(f(b))-log2(f(d))|> |log2(f(b'))-log2(f(d'))| + */ + struct cmp_intervals_by_log_length { + cmp_intervals_by_log_length(){} + bool operator()( interval_filtration p, interval_filtration q) + { + if(p.dim() != q.dim()) {return p.dim() < q.dim();}//lower dimension first + if(p.log_length() != q.log_length()) {return p.log_length() > q.log_length();} + if(p.birth() != q.birth()) {return p.birth() < q.birth();}//lex order + return p.death() < q.death(); + } + }; + /* Comparison function to sort intervals by decreasing length in the + * output persistence diagram, i.e., + * [f(b),f(d)]<[f(b'),f(d')] iff |f(b)-f(d)| > |f(b')-f(d')| + */ + struct cmp_intervals_by_length { + cmp_intervals_by_length(){} + bool operator()( interval_filtration p, interval_filtration q) + { + if(p.length() != q.length()) { return p.length() > q.length(); }//longest 1st + if(p.dim() != q.dim()) {return p.dim() < q.dim();}//lower dimension first + if(p.birth() != q.birth()) {return p.birth() < q.birth();}//lex order + return p.death() < q.death(); + } + }; + +public: + /** \brief Initialization of the Zigzag_persistence class. + * + * \param[in] cpx A model of ZigzagFilteredComplex. + * */ + Zigzag_persistence(int ignore_cycles_above_dim = -1) + : cpx_() + , dim_max_(ignore_cycles_above_dim) + , lowidx_to_matidx_() + , matrix_() + , birth_ordering_() + , persistence_diagram_() + , num_arrow_(-1) + , previous_filtration_value_(std::numeric_limits::infinity()) + , filtration_values_() {} + +private: + /* Set c1 <- c1 + c2, assuming canonical order of indices induced by the order in + * the vertical lists. self1 is the matrix_chain whose column is c1, for self + * reference of the new cells. + */ + void plus_equal_column(matrix_chain * self1, Column & c1, Column & c2) + { + //insert all elements of c2 in c1, in O(|c2| * log(|c1|+|c2|)) + if constexpr (Options::searchable_column) { + for(auto &cell : c2) { + auto it1 = c1.find(cell); + if(it1 != c1.end()) {//already there => remove as 1+1=0 + Cell * tmp_ptr = &(*it1); + it1->base_hook_matrix_row::unlink(); //unlink from row + c1.erase(it1); //remove from col + matrix_chain::cellPool_.destroy(tmp_ptr); + // delete tmp_ptr; + } + else {//not there, insert new cell + // Cell *new_cell = new Cell(cell.key(), self1); + Cell *new_cell = matrix_chain::cellPool_.construct(cell.key(), self1); + c1.insert(*new_cell); + lowidx_to_matidx_[cell.key()]->row_.push_back(*new_cell);//row link,no order + } + } + } + else {//traverse both columns doing a standard column addition, in O(|c1|+|c2|) + auto it1 = c1.begin(); auto it2 = c2.begin(); + while(it1 != c1.end() && it2 != c2.end()) + { + if(it1->key() < it2->key()) { ++it1; } + else { + if(it1->key() > it2->key()) { + // Cell * new_cell = new Cell(it2->key(), self1); + Cell *new_cell = matrix_chain::cellPool_.construct(it2->key(), self1); + c1.insert(it1, *new_cell); //col link, in order + lowidx_to_matidx_[it2->key()]->row_.push_back(*new_cell);//row link,no order + ++it2; + } + else { //it1->key() == it2->key() + auto tmp_it = it1; ++it1; ++it2; + Cell * tmp_ptr = &(*tmp_it); + tmp_it->base_hook_matrix_row::unlink(); //unlink from row + c1.erase(tmp_it); //remove from col + matrix_chain::cellPool_.destroy(tmp_ptr); + // delete tmp_ptr; + } + } + } + while(it2 != c2.end()) {//if it1 reached the end of its column, but not it2 + // Cell * new_cell = new Cell(it2->key(),self1); + Cell *new_cell = matrix_chain::cellPool_.construct(it2->key(), self1); + lowidx_to_matidx_[it2->key()]->row_.push_back(*new_cell); //row links + c1.push_back(*new_cell); + ++it2; + } + } + } + + /** Maintains the birth ordering <=b. Contains an std::map of size the number of + * non-zero rows of the homology matrix, at any time during the computation of + * zigzag persistence. + * + * By construction, we maintain the map satisfying + * 'birth_to_pos_[i] < birth_to_pos_[j]', + * with 0 <= i,j <= k indices in the quiver '0 \leftrightarrow ... \leftrightarrow i \leftrightarrow .. \leftrightarrow k' + * visited at time k of the algorithm (prefix of length k of the full zigzag + * filtration '0 \leftrightarrow ... \leftrightarrow i \leftrightarrow .. \leftrightarrow k \leftrightarrow ... \leftrightarrow n' that is studied), + * iff i k+1 forward, then j 0 -> 1 -> 2 <- 3 <- 4 -> 5 <- 6 etc + birth_ordering() : birth_to_pos_(), max_birth_pos_(0), min_birth_pos_(-1) {} + + //when the arrow key-1 -> key is forward, key is larger than any other index + //i < key in the birth ordering b k2 + bool reverse_birth_order(Simplex_key k1, Simplex_key k2) { + return birth_to_pos_[k1] > birth_to_pos_[k2]; + } + + private: + //birth_to_pos_[i] < birth_to_pos_[j] iff i birth_to_pos_; + //by construction, max_birth_pos_ (resp. min_birth_pos_) is strictly larger + //(resp. strictly smaller) than any value assigned to a key so far. + Simplex_key max_birth_pos_; + Simplex_key min_birth_pos_; + }; + +public: + /** \brief Computes the zigzag persistent homology of a zigzag filtered complex, + * using the reflection and transposition algorithm of \cite zigzag_reflection. + * + * \details After computation, the persistence diagram can be accessed via + * member method persistence_diagram, for the diagram with filtration + * values, or member method index_persistence_diagram, for the + * diagram with + * indices of paired simplices. + * + * + * matrix_, originally empty, maintains the set of chains, with a + * partition \f$ F \sqcup G \sqcup H\f$ + * representing a compatible homology basis as in \cite zigzag_reflection. + * + * Each simplex in the complex stores a key field that stores the index of + * its insertion in the zigzag filtration. + * + * The algorithm maintains a compatible homology basis for the zigzag filtration. + * + * \f$$\emptyset = K_0 \leftrightarrow (...) \leftrightarrow K_i \leftarrow ... \leftarrow \emptyset\f$$ + * + * where the prefix from \f$K_0\f$ to \f$K_i\f$ is equal to the i-th prefix of + * the input zigzag + * filtration given by cpx_.filtration_simplex_range(), and + * the suffix + * (from \f$K_i\f$ + * to the right) is a sequence of simplex removals. Due to the structure of + * reflection diamonds, the removals are in reverse order of the insertions, to + * reduce the amount of transposition diamonds. + * + * Consequently, using cpx_.key(zzsh) as indexing for the matrix + * rows/cells, + * with the natural order on integers, makes our homology matrix matrix_ upper + * triangular for the suffix \f$K_i \leftarrow ... \leftarrow 0\f$, seen as a + * standard persistence + * filtration. At \f$K_i\f$, the natural order on integers is also equivalent to the + * death-order \f$\leq_d\f$ (because all arrows in the suffix are backward). + * + * Insertion: cpx_.key(*zzit) is a strictly increasing sequence + * for zzit + * insertion of cells (does not need to be contiguous). However, for every forward + * arrow, we have cpx_.key(*zzit) == num_arrows_. + * Removal: cpx_.key(*zzit) gives the assigned key (during past + * insertion) of a + * cell == *zzit during a removal. We use num_arrows_ + * to record the deaths in the + * persistence diagram. + * Insertion and Removal: zzit.filtration() is totally monotone. + * Note that the + * iterator encodes the filtration, and not the cells within the complex structure. + */ +// void zigzag_persistent_homology() +// { //compute index persistence, interval are closed, i.e., [b,d) is stored as +// //[b,d-1]. The filtration values are maintained in field filtration_values_ +// Filtration_value prev_fil_, curr_fil_; + +// assert(num_arrow_ == 0); +// auto zzrg = cpx_.filtration_simplex_range(); +// auto zzit = zzrg.begin(); +// dim_max_ = zzit.dim_max(); + +// num_arrow_ = cpx_.key(*zzit);//should be 0 + +// prev_fil_ = zzit.filtration(); +// filtration_values_.emplace_back(num_arrow_, prev_fil_); + +// while( zzit != zzrg.end() ) +// { //insertion of a simplex +// if(zzit.arrow_direction()) { num_arrow_ = cpx_.key(*zzit); } +// else { ++num_arrow_; } //removal of a simplex, a simplex key corresponds to the index of its INSERTION +// curr_fil_ = zzit.filtration();//cpx_.filtration(*zzit) is invalid for (<-); +// if(curr_fil_ != prev_fil_) //check whether the filt value has changed +// { //consecutive pairs (i,f), (j,f') mean simplices of index k in [i,j-1] have +// prev_fil_ = curr_fil_; //filtration value f +// filtration_values_.emplace_back(num_arrow_, prev_fil_); +// } +// if(zzit.arrow_direction()) { //forward arrow, only consider critical cells +// forward_arrow(*zzit); +// } +// else { //backward arrow +// backward_arrow(*zzit); +// } +// ++zzit; +// } + +//// if(!matrix_.empty()) { +//// std::cout << "There remain " << matrix_.size() << " columns in the matrix.\n"; +//// } +// } + + template> + void insert_simplex(const VertexRange& simplex, Filtration_value filtration_value) + { + if (dim_max_ != -1 && simplex.size() > static_cast(dim_max_) + 1) return; + + ++num_arrow_; + + if (filtration_value != previous_filtration_value_) //check whether the filt value has changed + { //consecutive pairs (i,f), (j,f') mean simplices of index k in [i,j-1] have + previous_filtration_value_ = filtration_value; //filtration value f + filtration_values_.emplace_back(num_arrow_, previous_filtration_value_); + } + + std::pair res = cpx_.insert_simplex(simplex, filtration_value); + GUDHI_CHECK(res.second, "Zigzag_persistence::insert_simplex - insertion of a simplex already in the complex"); + cpx_.assign_key(res.first, num_arrow_); + forward_arrow(res.first); + } + + template> + void remove_simplex(const VertexRange& simplex, Filtration_value filtration_value) + { + if (dim_max_ != -1 && simplex.size() > static_cast(dim_max_) + 1) return; + + ++num_arrow_; + + Simplex_handle sh = cpx_.find(simplex); + GUDHI_CHECK(sh != cpx_.null_simplex(), "Zigzag_persistence::remove_simplex - removal of a simplex not in the complex"); + + if (filtration_value != previous_filtration_value_) //check whether the filt value has changed + { //consecutive pairs (i,f), (j,f') mean simplices of index k in [i,j-1] have + previous_filtration_value_ = filtration_value; //filtration value f + filtration_values_.emplace_back(num_arrow_, previous_filtration_value_); + } + + backward_arrow(sh); + cpx_.remove_maximal_simplex(sh); + } + + template>, + class FiltrationRange = std::initializer_list> + void insert_simplices_contiguously(const SimplexRange& simplices, const FiltrationRange& filtration_values) + { + auto simplexIt = simplices.begin(); + auto filIt = filtration_values.begin(); + for (; simplexIt != simplices.end(); ++simplexIt, ++filIt) { + insert_simplex(*simplexIt, *filIt); + } + } + + template>, + class FiltrationRange = std::initializer_list> + void remove_simplices_contiguously(const SimplexRange& simplices, const FiltrationRange& filtration_values) + { + auto simplexIt = simplices.begin(); + auto filIt = filtration_values.begin(); + for (; simplexIt != simplices.end(); ++simplexIt, ++filIt) { + remove_simplex(*simplexIt, *filIt); + } + } + + template + void insert_simplices_contiguously(SimplexRangeIterators simplex_range_start, + SimplexRangeIterators simplex_range_end, + FiltrationRangeIterators filtration_range_start) + { + for (; simplex_range_start != simplex_range_end; ++simplex_range_start, ++filtration_range_start) { + insert_simplex(*simplex_range_start, *filtration_range_start); + } + } + + template + void remove_simplices_contiguously(SimplexRangeIterators simplex_range_start, + SimplexRangeIterators simplex_range_end, + FiltrationRangeIterators filtration_range_start) + { + for (; simplex_range_start != simplex_range_end; ++simplex_range_start, ++filtration_range_start) { + remove_simplex(*simplex_range_start, *filtration_range_start); + } + } + + void print_current_complex(){ + for (auto& sh : cpx_.complex_simplex_range()){ + for (auto v : cpx_.simplex_vertex_range(sh)){ + std::cout << v << " "; + } + std::cout << " - " << cpx_.filtration(sh) << "\n"; + } + } + +private: + /** \brief Computes the boundary cycle of the new simplex zzsh, and express it as a + * sum of cycles. If all cycles are boundary cycles, i.e., columns with G-index + * in the matrix, then [\partial zzsh] = 0 and we apply an injective diamond to + * the zigzag module. Otherwise, we keep reducing with boundary- and live- cycles, + * i.e., columns with (F \cup G)-indices, and then apply a surjective diamond to + * the zigzag module. + */ + void forward_arrow( Simplex_handle zzsh ) + { //maintain the <=b order + birth_ordering_.add_birth_forward(num_arrow_); + + //Reduce the boundary of zzsh in the basis of cycles. + //Compute the simplex keys of the simplices of the boundary of zzsh. + std::set< Simplex_key > col_bsh; //set maintains the natural order on indices + for( auto b_sh : cpx_.boundary_simplex_range(zzsh) ) + { col_bsh.insert(cpx_.key(b_sh)); } + + //If empty boundary (e.g., when zzsh is a vertex in a simplicial complex) + //Add a non-trivial cycle [c = zzsh] to the matrix, lowidx_to_matidx_make it a creator in F. + if(col_bsh.empty()) // -> creator + { //New row and column with a bottom-right non-zero element, at index key(zzsh) + //i.e., create a new cycle in F, equal to *zzsh alone. + matrix_.emplace_front(num_arrow_); + auto new_chain_it = matrix_.begin();//the new chain + //Update the map [index idx -> chain with lowest index idx] in matrix_ + lowidx_to_matidx_[num_arrow_] = new_chain_it; + return; + } + + // col_bsh.rbegin()) is idx of lowest element in col_bsh, because it is a set. + matrix_chain *col_low = &(*lowidx_to_matidx_[*(col_bsh.rbegin())]); + auto paired_idx = col_low->paired_col_; //col with which col_low is paired + std::vector< matrix_chain * > chains_in_H; //for corresponding indices in H + std::vector< matrix_chain * > chains_in_G; + + //Reduce col_bsh with boundary cycles, i.e., indices in G. + std::pair< typename std::set< Simplex_key >::iterator, bool > res_insert; + while( paired_idx != nullptr ) + { + chains_in_H.push_back(paired_idx);//keep the col_h with which col_g is paired + chains_in_G.push_back(col_low); //keep the col_g + for(auto &cell : (col_low->column())) { //Reduce with the column col_g + res_insert = col_bsh.insert(cell.key()); + if( !res_insert.second ) { col_bsh.erase(res_insert.first); } //1+1 = 0 + //o.w. insertion has succeeded. + } + //If col_bsh is entirely reduced, \partial zzsh is a boundary cycle. + if(col_bsh.empty()) { + // if(cpx_.dimension(zzsh) >= max_dim_) {return;} we need max_dim creators + injective_reflection_diamond(zzsh, chains_in_H); + return; + } + //Continue the reduction + col_low = &(*lowidx_to_matidx_[*(col_bsh.rbegin())]);//curr low index col + paired_idx = col_low->paired_col_;//col with which col_low is paired + } + + //Continue reducing with boundary and 'live' cycles, i.e., indices in G U F. + std::vector< matrix_chain * > chains_in_F; + while(true) + { + if(paired_idx == nullptr) { chains_in_F.push_back(col_low); }//col_low is in F + else { chains_in_H.push_back(paired_idx); } //col_low in G, paired_idx is in H + //Reduce with the column col_g or col_f + for(auto &cell : (col_low->column())) { + res_insert = col_bsh.insert(cell.key()); + if( !res_insert.second ) { col_bsh.erase(res_insert.first); } //1+1 = 0 + //o.w. insertion has succeeded. + } + //If col_bsh is entirely reduced, i.e. col_bsh == \emptyset. + if(col_bsh.empty()) + { + surjective_reflection_diamond(zzsh, chains_in_F, chains_in_H); + return; + } + //Else, keep reducing. + col_low = &(*lowidx_to_matidx_[*(col_bsh.rbegin())]); //curr low index col + paired_idx = col_low->paired_col_;//col with which col_low is paired + } + } + + /** \brief Computes an injective diamond in the zigzag module, by inserting a new + * column for the chain zzsh - \sum col_h, for all col_h in chains_in_H, and a + * new row for the simplex zzsh. + */ + void injective_reflection_diamond ( Simplex_handle zzsh + , std::vector< matrix_chain * > & chains_in_H ) + { //Compute the chain zzsh + \sum col_h, for col_h \in chains_in_H + std::set< Simplex_key > col_bsh; + std::pair< typename std::set< Simplex_key >::iterator, bool > res_insert; + //produce the sum of all col_h in chains_in_H + for( matrix_chain *idx_h : chains_in_H ) { + for(auto &cell : (idx_h->column()) ) { + res_insert = col_bsh.insert(cell.key()); + if( !res_insert.second ) { col_bsh.erase(res_insert.first); } + } + } + //create a new cycle (in F) sigma - \sum col_h + matrix_.emplace_front(num_arrow_, col_bsh.begin(), col_bsh.end(), + lowidx_to_matidx_); + //Update the map 'index idx -> chain with lowest index idx' in matrix_ + auto chain_it = matrix_.begin(); + lowidx_to_matidx_[num_arrow_] = chain_it; + } + + /** The vector chains_in_F is sorted by decreasing lowest index values in the + * columns corresponding to the chains, due to its computation in the reduction of + * \partial zzsh in forward_arrow(...). It is equivalent to decreasing death index + * order w.r.t. the & chains_in_F + , std::vector< matrix_chain * > & chains_in_H ) + { //fp is the largest death index for <=d + //Set col_fp: col_fp <- col_f1+...+col_fp (now in G); preserves lowest idx + auto chain_fp = *(chains_in_F.begin()); //col_fp, with largest death column(), (*other_col_it)->column()); } + //doesn't change the lowest idx as chain_fp has maximal lowest idx of all + + //chains_in_F is ordered, from .begin() to end(), by decreasing lowest_idx_. The + //lowest_idx_ is also the death of the chain in the right suffix of the + //filtration (all backward arrows). Consequently, the chains in F are ordered by + //decreasing death for bool + { return birth_ordering_.reverse_birth_order(k1,k2); };//true iff b(k1) >b b(k2) + + //available_birth: for all i by >d value of the d_i, + //contains at step i all b_j, j > i, and maybe b_i if not stolen + std::set< Simplex_key, decltype(cmp_birth) > available_birth(cmp_birth); + //for f1 to f_{p} (i by <=d), insertion in available_birth_to_fidx sorts by >=b + for(auto &chain_f : chains_in_F) { available_birth.insert(chain_f->birth()); } + + auto maxb_it = available_birth.begin();//max birth cycle + auto maxb = *maxb_it; //max birth value, for persistence diagram + available_birth.erase(maxb_it); //remove max birth cycle (stolen) + + auto last_modified_chain_it = chains_in_F.rbegin(); + + //consider all death indices by increasing birth()); + if(birth_it == available_birth.end()) //birth is not available. *chain_f_it + { //must become the sum of all chains in F with smaller death index. + //this gives as birth the maximal birth of all chains with strictly larger + //death <=> the maximal availabe death. + //Let c_1 ... c_f be the chains s.t. <[c_1+...+c_f]> is the kernel and + // death(c_i) >d death(c_i-1). If the birth of c_i is not available, we set + //c_i <- c_i + c_i-1 + ... + c_1, which is [c_i + c_i-1 + ... + c_1] on + //the right (of death the maximali <=> the maxj>k, are indeed c_j + //set c_i <- c_i + (c_i-1) + ... + (c_k+1) + (c_k + ... + c_1) + for(auto chain_passed_it = last_modified_chain_it;//all with smaller column() + , (*chain_passed_it)->column() ); + } + last_modified_chain_it = chain_f_it;//new cumulated c_i+...+c_1 + //remove the max available death + auto max_avail_b_it = available_birth.begin();//max because order by deacr assign_birth(max_avail_b); //give new birth + available_birth.erase(max_avail_b_it); //remove birth from availability + } + else { available_birth.erase(birth_it); } //birth not available anymore, do not + } //modify *chain_f_it. + //Compute the new column zzsh + \sum col_h, for col_h in chains_in_H + std::set< Simplex_key > col_bsh; + std::pair< typename std::set< Simplex_key >::iterator, bool > res_insert; + for(auto other_col : chains_in_H) + { //Compute (\sum col_h) in a set + for(auto &cell : (other_col->column())) + { + res_insert = col_bsh.insert(cell.key()); + if( !res_insert.second ) { col_bsh.erase(res_insert.first); } //1+1=0 + } + } + //Create and insert (\sum col_h) + sigma (in H, paired with chain_fp) in matrix_ + matrix_.emplace_front(cpx_.key(zzsh), chain_fp, col_bsh.begin(), col_bsh.end(), lowidx_to_matidx_); + //record that the chain with lowest index key(zzsh) is the one just created + auto chain_it = matrix_.begin(); + lowidx_to_matidx_[cpx_.key(zzsh)] = chain_it;//new row + + chain_fp->assign_paired_chain( &(*chain_it) );//pair chain_fp with the new chain + chain_fp->assign_birth(-1); //now belongs to G now -> right interval [m-1,g] + + //Update persistence diagram with left interval [fil(b_max) ; fil(m)) + persistence_diagram_.emplace_back( cpx_.dimension(zzsh)-1 + , maxb + , cpx_.key(zzsh));//-1);// + } + + //cpx_.key(zzsh) is the key of the simplex we remove, not a new one + void backward_arrow( Simplex_handle zzsh ) + { + //maintain the <=b order + birth_ordering_.add_birth_backward(num_arrow_); + //column whose key is the one of the removed simplex + auto curr_col_it = lowidx_to_matidx_.find(cpx_.key(zzsh)); + //corresponding chain + matrix_chain * curr_col = &(*(curr_col_it->second)); + //Record all columns that get affected by the transpositions, i.e., have a coeff + std::vector< matrix_chain * > modified_columns;//in the row of idx key(zzsh) + for(auto & hcell : (curr_col->row_)) { + modified_columns.push_back(hcell.self_chain_); + } + //Sort by left-to-right order in the matrix_ (no order maintained in rows) + std::stable_sort( modified_columns.begin(),modified_columns.end() + , [](matrix_chain *mc1, matrix_chain *mc2) + { return mc1->lowest_idx_ < mc2->lowest_idx_;} ); + + //Modifies the pointer curr_col, not the other one. + for(auto other_col_it = modified_columns.begin()+1; + other_col_it != modified_columns.end(); ++other_col_it) { + curr_col = arrow_transposition_case_study(curr_col, *other_col_it); + } + + //curr_col points to the column to remove by restriction of K to K-{\sigma} + if( curr_col->paired_col_ == nullptr ) { // in F + int dim_zzsh = cpx_.dimension(zzsh); + if(dim_max_ == -1 || (dim_max_ != -1 && dim_zzsh < dim_max_)) { //don't record intervals of max dim + persistence_diagram_.emplace_back( dim_zzsh + , curr_col->birth() + , num_arrow_);// -1); + } + } + else { //in H -> paired with c_g, that now belongs to F now + curr_col->paired_col_->assign_paired_chain(nullptr); + curr_col->paired_col_->assign_birth(num_arrow_); //closed interval + } + + //cannot be in G as the removed simplex is maximal + matrix_.erase(curr_col_it->second); + lowidx_to_matidx_.erase(curr_col_it); + } + + /* Exchanges members of matrix_chains, except the column_ pointer. Modify + * also the lowidx_to_matidx_ data structure, considering that the matrix chains + * also exchange their lowest_idx_. Specifically, it is called by + * arrow_transposition_case_study when: + * c_s has originally birth b_s and low idx s, and c_t has birth b_t and low idx t + * however, c_s becomes c_s <- c_s+c_t with b_t lowest_idx_); + auto it_t = lowidx_to_matidx_.find(other_col->lowest_idx_); + + std::swap(it_s->second, it_t->second);//swap matrix_chain* in lowidx_to_matidx_ + std::swap(curr_col->row_, other_col->row_);//swap associated row of lowest idx + std::swap(curr_col->lowest_idx_, other_col->lowest_idx_);//swap lowest idx. + } + + /** + * Permutes s and t, s goes up, whose insertions are adjacent, i.e., the following + * transformation (from (a) to (b)) in the filtration: + * from (a) ... \leftrightarrow K \leftarrow ... \leftarrow K' U {s,t} \leftarrow K' U {s} \leftarrow K' \leftarrow ..., + * where K' is at matrix index i, K' U {s} at matrix index i+1 and K' U {s,t} at + * matrix index i+2, + * + * to (b) ... \leftrightarrow K \leftarrow ... \leftarrow K' U {s,t} \leftarrow K' U {t} \leftarrow K' \leftarrow ..., + * + * and the chain c_t has a non-trivial coefficient for s, i.e., + * the bloc matrix gives (and becomes): + * c_t c_t + * + + + * c_s c_t c_s c_s c_s c_t + * s 1 1 t 1 0 t 1 1 + * t 0 1 --> either s 0 1 or s 0 1 + * + * By construction, s is a simplex that we want to remove in the complex K. It is + * consequently maximal in K, and all complexes between K and K' U {s} in filtration + * (a). + * + * If c_s and c_t are both cycles (in F)that, before the permutation, are carried by + * respectively the closed intervals [b_s, i+1] and [b_t, i+2], then the sum + * c_s + c_t is a cycle carried by the interval + * [maxd i (i+1 \leftarrow i backward). + * If j \leftarrow ... \leftarrow k are both birth indices on the right part of the quiver (all + * backward arrows) then systematically k inF()) + {//case F x * + if(other_col->inH()) { // case F x H + plus_equal_column( other_col, other_col->column()//c_t <- c_s+c_t still in H + , curr_col->column() );//(birth -2) and low idx t + return curr_col; + }//end case F x H + else // case F x F + { //in F x F: c_s+c_t has max<=b birth between b_t and b_s: + if(birth_ordering_.birth_order(curr_col->birth(), other_col->birth())) + { //max<=b is the birth of other_col i.e., b_s column()//c_t <- c_s+c_t of birth + , curr_col->column() );//b_t and lowest idx t. (same) + //c_s still has birth b_s (minimal) and lowest idx s + return curr_col;//continue with c_s of smaller birth b_s and lowest idx s + }//endif + else + { //max<=b is the birth of curr_col, i.e., b_t column()//c_s <- c_s+c_t of birth + , other_col->column() );//b_s and of NEW lowest idx t + //now c_t has (same) birth b_t (minimal) but NEW lowest idx s, so + //exchange lowest_idx, the rows, and update lowidx_to_matidx structure + exchange_lowest_indices_chains(curr_col, other_col); + return other_col;//continue with c_t of (smaller) birth b_t and low idx s + }//end else + }//end case F x F + }//end case F x * + else {//curr_col->inH() == true, case H x * + if(other_col->inH()) {// case H x H + //Case H x H, c_s+c_t paired w/ c_gs+c_gt, of death + //maxpaired_col_; //c_s paired with c_gs, death d_gs + auto other_p_col = other_col->paired_col_;//c_t paired with c_gt, death d_gt + if( curr_p_col->lowest_idx_ < other_p_col->lowest_idx_)//<=> d_gs column()//c_gt <- c_gs+c_gt, + , curr_p_col->column() );//of death d_gt, low idx d_gt + //(same because bigger), paired with c_s+c_t (now &c_t, updated below) + plus_equal_column( other_col, other_col->column()//c_t <- c_t+c_s, still + , curr_col->column() );//in H, low idx t (same) + return curr_col;//continue with c_s, paired with c_gs of min death d_gs + } + else + {// d_gt column()//c_gs <- c_gs+c_gt, + , other_p_col->column() );//of death d_gs, low idx d_gs + //(same because bigger), paired with c_s+c_t (now &c_s, updated below) + plus_equal_column( curr_col, curr_col->column()//c_s <- c_s+c_t, of NEW + , other_col->column());//low idx t (still in H) + //now c_s is still in H (birth -2) but has NEW lowest idx t, and c_t has + //low idx s after transposition. + //exchange lowest_idx, the rows, and update lowidx_to_matidx structure + exchange_lowest_indices_chains(curr_col, other_col); + return other_col; //continue with c_t, paired w. c_g' of min death g' + } + }//end case H x H + else {//other_col->inF() == true, case H x F + plus_equal_column( curr_col, curr_col->column() //c_s <- c_s+c_t still in H, + , other_col->column()); //(birth -2) and NEW low idx t + //now c_t, still in F, has (same) birth b_t but NEW lowest idx s, so + //exchange lowest_idx, the rows, and update lowidx_to_matidx structure + exchange_lowest_indices_chains(curr_col, other_col); + return other_col; //continue with c_t, still in F, of birth b_t and low idx s + } + } + } + + +public: + /** \brief Returns the index persistence diagram as an std::list of intervals.*/ + const std::list< interval_index > & index_persistence_diagram() const + { + return persistence_diagram_; + } + + /** \brief Returns the filtration values \f$[f(b),f(d)]\f$ (generally real-valued) + * associated to the indices \f$[b,d]\f$ (integer valued) of the insertion or + * deletion of a simplex in the zigzag filtration. + * + * \details Used to convert a persistent interval \f$[b,d]\f$, computed by the + * persistent homology algorithm, into its filtration valued version + * \f$[f(b),f(d)]\f$ used in the persistence barcode. The information + * index->filtration is stored in the member filtration_values_ of + * the class Zigzag_persistence. + * + * @param[in] b_key, d_key The indices of birth and death of a persistent + * interval. + * + * @param[out] std::pair A pair of real values \f$(f(b),f(d))\f$. + */ + std::pair index_to_filtration( + Simplex_key b_key, Simplex_key d_key) { + // filtration_values_ must be sorted by increasing keys. + auto it_b = //lower_bound(x) returns leftmost y s.t. x <= y + std::lower_bound( filtration_values_.begin(), filtration_values_.end() + , std::pair(b_key + , std::numeric_limits::infinity() ) + , []( std::pair p1 + , std::pair p2) + { return p1.first < p2.first; } + ); + if(it_b == filtration_values_.end() || it_b->first > b_key) { --it_b; } + //it points to the rightmost z such that z <= x + + auto it_d = // + std::lower_bound( filtration_values_.begin(), filtration_values_.end() + , std::pair(d_key + , std::numeric_limits::infinity() ) + , []( std::pair p1 + , std::pair p2) + { return p1.first < p2.first; } + ); + if(it_d == filtration_values_.end() || it_d->first > d_key) { --it_d; } + + return std::make_pair(it_b->second, it_d->second); + } + + /** \brief Writes the persistence diagram in a file. + * + * \details The filtration values are given by the zigzag persistence iterator, that assigns + * to any insertion or deletion of a simplex a filtration value ; we say that an + * arrow has an index \f$i\f$, and a corresponding filtration value \f$f(i)\f$. + * Reading a zigzag filtration from left to right, indices are strictly + * monotonically increasing, and the associated filtration values are monotonous + * (not necessarily + * strictly, either increasing or decreasing). + * + * Consider two consecutive arrows (insertion or deletion): + * + * \f$$K_1 \leftrightarrow K_2 \leftrightarrow K_3\f$$ + * + * with respectively indices \f$i\f$ (left) and \f$i+1\f$ (right), and associated + * filtration values \f$f(i)\f$ and \f$f(i+1)\f$ respectively. + * + * If, the arrow \f$K_2 \leftrightarrow K_3\f$ leads to the creation of a new + * homology feature in \f$K_3\f$, it creates an (indexed) persistent interval + * \f$[\f$i+1; \cdot\f$, and a corresponding (filtration) persistent interval + * \f$[f(i+1); \cdot]\f$ in the persistence diagram. + * + * If a homology feature in \f$K_2\f$ is destroyed by the arrow \f$K_2 \leftrightarrow K_3\f$, it closes an (indexed) + * interval \f$[\cdot ; i+1]\f$, and a corresponding (filtration) persistent + * interval \f$[\cdot ; f(i+1)]\f$ in the persistence diagram. + * + * For example, in an oscillating Rips zigzag filtration, if, in the following + * chunk of filtration: + * + * \f$R_{\eta \varepsilon_i}(P_i) \rightarrow \cdots \leftarrow R_{\eta \varepsilon_{i+1}}(P_{i+1}),\f$ + * + * if anything is created by any of the arrows above, it leads to an interval + * \f$[\varepsilon_{i+1}; \cdot]\f$. If anything is destroyed by any of the arrows + * above, if leads to an interval \f$[\cdot;\varepsilon_i]\f$. Note that we may + * have \f$\varepsilon_i > \varepsilon_{i+1}\f$. + * + * The bars are ordered by decreasing length. + * + * @param[in] os the output stream in which the diagram is written. + * @param[in] shortest_interval all intervals of lenght smaller or equal to + * this value are ignore. Default is 0. + */ + void persistence_diagram( std::ostream& os + , Filtration_value shortest_interval = 0.) { + + std::stable_sort(filtration_values_.begin(), filtration_values_.end(), + []( std::pair< Simplex_key, Filtration_value > p1 + , std::pair< Simplex_key, Filtration_value > p2 ) + { return p1.first < p2.first; } + ); + + std::vector< interval_filtration > tmp_diag; + tmp_diag.reserve(persistence_diagram_.size()); + for(auto bar : persistence_diagram_) + { + Filtration_value birth,death; + std::tie(birth,death) = index_to_filtration(bar.birth(), bar.death()); + + if( std::abs(birth - death) > shortest_interval ) { + tmp_diag.emplace_back(bar.dim(), birth, death ); + } + } + // cmp_intervals_by_length cmp; + std::stable_sort(tmp_diag.begin(), tmp_diag.end(), cmp_intervals_by_length()); + + os << "# dim birth death [length]\n"; + for(auto bar : tmp_diag) { + if(bar.birth() > bar.death()) { bar.swap_birth_death(); } + os << bar.dim() << " " << bar.birth() << " " << bar.death() << + " - [" << bar.length() << "] \n"; + } + } + + /** \brief Returns the persistence diagram as a vector of real-valued intervals. */ + std::vector< interval_filtration > + persistence_diagram(Filtration_value shortest_interval = 0., bool include_infinit_bars = false) + { + std::stable_sort(filtration_values_.begin(), filtration_values_.end(), + []( std::pair< Simplex_key, Filtration_value > p1 + , std::pair< Simplex_key, Filtration_value > p2 ) + { return p1.first < p2.first; } + ); + + std::vector< interval_filtration > diag; + diag.reserve(persistence_diagram_.size()); + for(auto bar : persistence_diagram_) + { + Filtration_value birth,death; + std::tie(birth,death) = index_to_filtration(bar.birth(), bar.death()); + + if( std::abs(birth - death) > shortest_interval ) { + diag.emplace_back(bar.dim(), birth, death ); + } + } + //put lower value as birth + for(auto &bar : diag) { + if( bar.birth() > bar.death() ) { bar.swap_birth_death(); } + } + std::stable_sort(diag.begin(), diag.end(), cmp_intervals_by_length()); + + auto birth = + [this](Simplex_key b_key) { + auto it_b = // lower_bound(x) returns leftmost y s.t. x <= y + std::lower_bound(filtration_values_.begin(), filtration_values_.end(), + std::pair( + b_key, std::numeric_limits::infinity()), + [](std::pair p1, + std::pair p2) { return p1.first < p2.first; }); + if (it_b == filtration_values_.end() || it_b->first > b_key) { + --it_b; + } + return it_b->second; + }; + + //TODO: dimension value + if (include_infinit_bars) { + for (const matrix_chain &col : matrix_) { + if (col.inF()) + diag.emplace_back(-1, birth(col.birth()), std::numeric_limits::infinity()); + } + } + + return diag; + } + +private: + Complex cpx_; // complex + int dim_max_;//max dim complex + //idx -> chain with lowest element at index idx in matrix_ + std::map< Simplex_key, typename std::list::iterator > + lowidx_to_matidx_; + //arbitrary order for the matrix chains + std::list< matrix_chain > matrix_; // 0 ... m-1 + // birth_vector birth_vector_; //<=b order + birth_ordering birth_ordering_; + std::list< interval_index > persistence_diagram_; + Simplex_key num_arrow_; //current index + Filtration_value previous_filtration_value_; + // filtration_values stores consecutive pairs (i,f) , (j,f') with f != f', + // meaning that all inserted simplices with key in [i;j-1] have filtration value f + //i is the smallest simplex index whose simplex has filtration value f. + std::vector< std::pair< Simplex_key, Filtration_value > > filtration_values_; +};//end class Zigzag_persistence + + +/** ZigzagPersistenceOptions, represents matrix columns by intrusive lists.*/ +struct Zigzag_persistence_collist { + static const bool searchable_column = false; +}; +/** ZigzagPersistenceOptions, represents matrix columns by intrusive sets.*/ +struct Zigzag_persistence_colset { + static const bool searchable_column = true; +}; + +} //namespace zigzag_persistence + +} //namespace Gudhi + +#endif //ZIGZAG_PERSISTENCE_H_ + diff --git a/src/Zigzag_persistence/test/CMakeLists.txt b/src/Zigzag_persistence/test/CMakeLists.txt new file mode 100644 index 0000000000..74dac45186 --- /dev/null +++ b/src/Zigzag_persistence/test/CMakeLists.txt @@ -0,0 +1,16 @@ +project(Zigzag_persistence_tests) + +include(GUDHI_boost_test) + +add_executable ( Zigzag_persistence_unit_test zigzag_persistence_unit_test.cpp ) +if(TARGET TBB::tbb) + target_link_libraries(Zigzag_persistence_unit_test TBB::tbb) +endif() + +# Do not forget to copy test results files in current binary dir +#file(COPY "${CMAKE_SOURCE_DIR}/src/Persistent_cohomology/test/simplex_tree_file_for_unit_test.txt" +# DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) + +# Unitary tests +gudhi_add_boost_test(Zigzag_persistence_unit_test) + diff --git a/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp b/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp new file mode 100644 index 0000000000..49bb97b5a9 --- /dev/null +++ b/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp @@ -0,0 +1,468 @@ +#include +#include +#include +#include + +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MODULE "zigzag_persistence" +#include + +#include +#include + +using namespace Gudhi; +using namespace boost::unit_test; +using ST = Gudhi::Simplex_tree; +using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; +using Vertex_handle = ST::Vertex_handle; +using Filtration_value = ST::Filtration_value; +using interval_index = ZP::interval_index; +using interval_filtration = ZP::interval_filtration; + +// TODO: +// void persistence_diagram(std::ostream& os, Filtration_value shortest_interval = 0.); + +struct cmp_intervals_by_length { + cmp_intervals_by_length() {} + bool operator()(interval_filtration p, interval_filtration q) { + if (p.length() != q.length()) { + return p.length() > q.length(); + } + if (p.dim() != q.dim()) { + return p.dim() < q.dim(); + } + if (p.birth() != q.birth()) { + return p.birth() < q.birth(); + } + return p.death() < q.death(); + } +}; + +BOOST_AUTO_TEST_CASE(constructor) { + BOOST_CHECK_NO_THROW(ZP zp); + BOOST_CHECK_NO_THROW(ZP zp(28)); + BOOST_CHECK_NO_THROW(ZP zp(28,2)); + ZP zp; + BOOST_CHECK(zp.persistence_diagram(0).empty()); +} + +void test_barcode(ZP& zp, std::vector& barcode) +{ + std::stable_sort(barcode.begin(), barcode.end(), cmp_intervals_by_length()); + auto it = barcode.begin(); + for (const auto& interval : zp.persistence_diagram()){ + BOOST_CHECK_EQUAL(interval.dim(), it->dim()); + BOOST_CHECK_EQUAL(interval.birth(), it->birth()); + BOOST_CHECK_EQUAL(interval.death(), it->death()); + ++it; + } + BOOST_CHECK(it == barcode.end()); +} + +void test_indices(ZP& zp, std::vector& indices, std::vector& indexToFil) +{ + auto it = indices.begin(); + for (const auto& interval : zp.index_persistence_diagram()){ + BOOST_CHECK_EQUAL(interval.dim(), it->dim()); + BOOST_CHECK_EQUAL(interval.birth(), it->birth()); + BOOST_CHECK_EQUAL(interval.death(), it->death()); + auto p = zp.index_to_filtration(interval.birth(), interval.death()); + BOOST_CHECK_EQUAL(p.first, indexToFil[interval.birth()]); + BOOST_CHECK_EQUAL(p.second, indexToFil[interval.death()]); + ++it; + } + BOOST_CHECK(it == indices.end()); +} + +std::vector > get_simplices() +{ + return { + {0}, + {1}, + {2}, + {0,1}, + {0,2}, + {3}, + {1,2}, + {4}, + {3,4}, + {5}, + {0,1,2}, + {4,5}, + {3,5}, + {3,4,5}, + {0,1,2}, //r + {3,4,5}, //r + {1,4}, + {0,1,2}, + {2,4}, + {3,4,5}, + {0,4}, + {0,2,4}, + {1,2,4}, + {0,1,4}, + {3,4,5}, //r + {3,4}, //r + {3,5}, //r + {0,1,2,4}}; +} + +std::vector get_filtration_values() +{ + return { + 0, 0, 0, + 1, 1, 1, + 2, 2, 2, + 3, 3, 3, 3, + 4, + 5, + 6, 6, 6, + 7, 7, 7, 7, 7, 7, + 8, + 9, 9, 9 + }; +} + +BOOST_AUTO_TEST_CASE(zigzag_persistence_single) { + ZP zp(28); + std::vector realIndices; + std::vector realBarcode; + realIndices.reserve(13); + realBarcode.reserve(9); + + std::vector > simplices = get_simplices(); + std::vector filValues = get_filtration_values(); + + for (unsigned int i = 0; i < 14; ++i){ + zp.insert_simplex(simplices[i], filValues[i]); + } + + realIndices.emplace_back(0, 1, 3); + realIndices.emplace_back(0, 2, 4); + realIndices.emplace_back(0, 7, 8); + realIndices.emplace_back(1, 6, 10); + realIndices.emplace_back(0, 9, 11); + realIndices.emplace_back(1, 12, 13); + + realBarcode.emplace_back(0, 0, 1); + realBarcode.emplace_back(0, 0, 1); + realBarcode.emplace_back(1, 2, 3); + realBarcode.emplace_back(1, 3, 4); + + for (unsigned int i = 14; i < 16; ++i){ + zp.remove_simplex(simplices[i], filValues[i]); + } + + for (unsigned int i = 16; i < 24; ++i){ + zp.insert_simplex(simplices[i], filValues[i]); + } + + realIndices.emplace_back(0, 5, 16); + realIndices.emplace_back(1, 14, 17); + realIndices.emplace_back(1, 15, 19); + realIndices.emplace_back(1, 20, 21); + realIndices.emplace_back(1, 18, 22); + + realBarcode.emplace_back(0, 1, 6); + realBarcode.emplace_back(1, 5, 6); + realBarcode.emplace_back(1, 6, 7); + + for (unsigned int i = 24; i < 27; ++i){ + zp.remove_simplex(simplices[i], filValues[i]); + } + + realIndices.emplace_back(1, 24, 25); + realBarcode.emplace_back(1, 8, 9); + + zp.insert_simplex(simplices[27], filValues[27]); + + realIndices.emplace_back(2, 23, 27); + realBarcode.emplace_back(2, 7, 9); + + test_indices(zp, realIndices, filValues); + test_barcode(zp, realBarcode); +} + +BOOST_AUTO_TEST_CASE(zigzag_persistence_single_max1) { + ZP zp(28, 1); + std::vector realIndices; + std::vector indexToFil(28); + std::vector realBarcode; + realIndices.reserve(5); + realBarcode.reserve(3); + + std::vector > simplices = get_simplices(); + std::vector filValues = get_filtration_values(); + unsigned int currIndex = 0; + + for (unsigned int i = 0; i < 14; ++i){ + zp.insert_simplex(simplices[i], filValues[i]); + if (simplices[i].size() < 3){ + indexToFil[currIndex++] = filValues[i]; + } + } + + realIndices.emplace_back(0, 1, 3); + realIndices.emplace_back(0, 2, 4); + realIndices.emplace_back(0, 7, 8); + realIndices.emplace_back(0, 9, 10); + + realBarcode.emplace_back(0, 0, 1); + realBarcode.emplace_back(0, 0, 1); + + for (unsigned int i = 14; i < 16; ++i){ + zp.remove_simplex(simplices[i], filValues[i]); + if (simplices[i].size() < 3){ + indexToFil[currIndex++] = filValues[i]; + } + } + + for (unsigned int i = 16; i < 24; ++i){ + zp.insert_simplex(simplices[i], filValues[i]); + if (simplices[i].size() < 3){ + indexToFil[currIndex++] = filValues[i]; + } + } + + realIndices.emplace_back(0, 5, 12); + realBarcode.emplace_back(0, 1, 6); + + for (unsigned int i = 24; i < 27; ++i){ + zp.remove_simplex(simplices[i], filValues[i]); + if (simplices[i].size() < 3){ + indexToFil[currIndex++] = filValues[i]; + } + } + + zp.insert_simplex(simplices[27], filValues[27]); + + test_indices(zp, realIndices, indexToFil); + test_barcode(zp, realBarcode); +} + +BOOST_AUTO_TEST_CASE(zigzag_persistence_batch_with_iterators) { + ZP zp; + std::vector realIndices; + std::vector realBarcode; + realIndices.reserve(13); + realBarcode.reserve(9); + + std::vector > simplices = get_simplices(); + std::vector filValues = get_filtration_values(); + + zp.insert_simplices_contiguously(simplices.begin(), + simplices.begin() + 14, + filValues.begin()); + + realIndices.emplace_back(0, 1, 3); + realIndices.emplace_back(0, 2, 4); + realIndices.emplace_back(0, 7, 8); + realIndices.emplace_back(1, 6, 10); + realIndices.emplace_back(0, 9, 11); + realIndices.emplace_back(1, 12, 13); + + realBarcode.emplace_back(0, 0, 1); + realBarcode.emplace_back(0, 0, 1); + realBarcode.emplace_back(1, 2, 3); + realBarcode.emplace_back(1, 3, 4); + + zp.remove_simplices_contiguously(simplices.begin() + 14, + simplices.begin() + 16, + filValues.begin() + 14); + zp.insert_simplices_contiguously(simplices.begin() + 16, + simplices.begin() + 24, + filValues.begin() + 16); + + realIndices.emplace_back(0, 5, 16); + realIndices.emplace_back(1, 14, 17); + realIndices.emplace_back(1, 15, 19); + realIndices.emplace_back(1, 20, 21); + realIndices.emplace_back(1, 18, 22); + + realBarcode.emplace_back(0, 1, 6); + realBarcode.emplace_back(1, 5, 6); + realBarcode.emplace_back(1, 6, 7); + + zp.remove_simplices_contiguously(simplices.begin() + 24, + simplices.begin() + 27, + filValues.begin() + 24); + + realIndices.emplace_back(1, 24, 25); + realBarcode.emplace_back(1, 8, 9); + + zp.insert_simplices_contiguously(simplices.begin() + 27, + simplices.begin() + 28, + filValues.begin() + 27); + + realIndices.emplace_back(2, 23, 27); + realBarcode.emplace_back(2, 7, 9); + + test_indices(zp, realIndices, filValues); + test_barcode(zp, realBarcode); +} + +BOOST_AUTO_TEST_CASE(zigzag_persistence_batch_with_iterators_max1) { + ZP zp(28, 1); + std::vector realIndices; + std::vector indexToFil(28); + std::vector realBarcode; + realIndices.reserve(5); + realBarcode.reserve(3); + + std::vector > simplices = get_simplices(); + std::vector filValues = get_filtration_values(); + unsigned int currIndex = 0; + + for (unsigned int i = 0; i < 28; ++i){ + if (simplices[i].size() < 3){ + indexToFil[currIndex++] = filValues[i]; + } + } + + zp.insert_simplices_contiguously(simplices.begin(), + simplices.begin() + 14, + filValues.begin()); + + realIndices.emplace_back(0, 1, 3); + realIndices.emplace_back(0, 2, 4); + realIndices.emplace_back(0, 7, 8); + realIndices.emplace_back(0, 9, 10); + + realBarcode.emplace_back(0, 0, 1); + realBarcode.emplace_back(0, 0, 1); + + zp.remove_simplices_contiguously(simplices.begin() + 14, + simplices.begin() + 16, + filValues.begin() + 14); + zp.insert_simplices_contiguously(simplices.begin() + 16, + simplices.begin() + 24, + filValues.begin() + 16); + + realIndices.emplace_back(0, 5, 12); + realBarcode.emplace_back(0, 1, 6); + + zp.remove_simplices_contiguously(simplices.begin() + 24, + simplices.begin() + 27, + filValues.begin() + 24); + zp.insert_simplices_contiguously(simplices.begin() + 27, + simplices.begin() + 28, + filValues.begin() + 27); + + test_indices(zp, realIndices, indexToFil); + test_barcode(zp, realBarcode); +} + +BOOST_AUTO_TEST_CASE(zigzag_persistence_batch) { + ZP zp; + std::vector realIndices; + std::vector realBarcode; + realIndices.reserve(13); + realBarcode.reserve(9); + + std::vector > simplices = get_simplices(); + std::vector filValues = get_filtration_values(); + + std::vector > subSimplices(simplices.begin(), simplices.begin() + 14); + std::vector subFilValues(filValues.begin(), filValues.begin() + 14); + zp.insert_simplices_contiguously(subSimplices, subFilValues); + + realIndices.emplace_back(0, 1, 3); + realIndices.emplace_back(0, 2, 4); + realIndices.emplace_back(0, 7, 8); + realIndices.emplace_back(1, 6, 10); + realIndices.emplace_back(0, 9, 11); + realIndices.emplace_back(1, 12, 13); + + realBarcode.emplace_back(0, 0, 1); + realBarcode.emplace_back(0, 0, 1); + realBarcode.emplace_back(1, 2, 3); + realBarcode.emplace_back(1, 3, 4); + + subSimplices = std::vector >(simplices.begin() + 14, simplices.begin() + 16); + subFilValues = std::vector(filValues.begin() + 14, filValues.begin() + 16); + zp.remove_simplices_contiguously(subSimplices, subFilValues); + + subSimplices = std::vector >(simplices.begin() + 16, simplices.begin() + 24); + subFilValues = std::vector(filValues.begin() + 16, filValues.begin() + 24); + zp.insert_simplices_contiguously(subSimplices, subFilValues); + + realIndices.emplace_back(0, 5, 16); + realIndices.emplace_back(1, 14, 17); + realIndices.emplace_back(1, 15, 19); + realIndices.emplace_back(1, 20, 21); + realIndices.emplace_back(1, 18, 22); + + realBarcode.emplace_back(0, 1, 6); + realBarcode.emplace_back(1, 5, 6); + realBarcode.emplace_back(1, 6, 7); + + subSimplices = std::vector >(simplices.begin() + 24, simplices.begin() + 27); + subFilValues = std::vector(filValues.begin() + 24, filValues.begin() + 27); + zp.remove_simplices_contiguously(subSimplices, subFilValues); + + realIndices.emplace_back(1, 24, 25); + realBarcode.emplace_back(1, 8, 9); + + subSimplices = std::vector >(simplices.begin() + 27, simplices.begin() + 28); + subFilValues = std::vector(filValues.begin() + 27, filValues.begin() + 28); + zp.insert_simplices_contiguously(subSimplices, subFilValues); + + realIndices.emplace_back(2, 23, 27); + realBarcode.emplace_back(2, 7, 9); + + test_indices(zp, realIndices, filValues); + test_barcode(zp, realBarcode); +} + +BOOST_AUTO_TEST_CASE(zigzag_persistence_batch_max1) { + ZP zp(28, 1); + std::vector realIndices; + std::vector indexToFil(28); + std::vector realBarcode; + realIndices.reserve(5); + realBarcode.reserve(3); + + std::vector > simplices = get_simplices(); + std::vector filValues = get_filtration_values(); + unsigned int currIndex = 0; + + for (unsigned int i = 0; i < 28; ++i){ + if (simplices[i].size() < 3){ + indexToFil[currIndex++] = filValues[i]; + } + } + + std::vector > subSimplices(simplices.begin(), simplices.begin() + 14); + std::vector subFilValues(filValues.begin(), filValues.begin() + 14); + zp.insert_simplices_contiguously(subSimplices, subFilValues); + + realIndices.emplace_back(0, 1, 3); + realIndices.emplace_back(0, 2, 4); + realIndices.emplace_back(0, 7, 8); + realIndices.emplace_back(0, 9, 10); + + realBarcode.emplace_back(0, 0, 1); + realBarcode.emplace_back(0, 0, 1); + + subSimplices = std::vector >(simplices.begin() + 14, simplices.begin() + 16); + subFilValues = std::vector(filValues.begin() + 14, filValues.begin() + 16); + zp.remove_simplices_contiguously(subSimplices, subFilValues); + + subSimplices = std::vector >(simplices.begin() + 16, simplices.begin() + 24); + subFilValues = std::vector(filValues.begin() + 16, filValues.begin() + 24); + zp.insert_simplices_contiguously(subSimplices, subFilValues); + + realIndices.emplace_back(0, 5, 12); + realBarcode.emplace_back(0, 1, 6); + + subSimplices = std::vector >(simplices.begin() + 24, simplices.begin() + 27); + subFilValues = std::vector(filValues.begin() + 24, filValues.begin() + 27); + zp.remove_simplices_contiguously(subSimplices, subFilValues); + + subSimplices = std::vector >(simplices.begin() + 27, simplices.begin() + 28); + subFilValues = std::vector(filValues.begin() + 27, filValues.begin() + 28); + zp.insert_simplices_contiguously(subSimplices, subFilValues); + + test_indices(zp, realIndices, indexToFil); + test_barcode(zp, realBarcode); +} From 3f8409f7d6f757f4e10739ecc9e9a92574d6f558 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Fri, 21 Jul 2023 14:26:25 +0200 Subject: [PATCH 02/51] doc and concepts --- .../gudhi/chain_matrix/chain_rep_cycles.h | 1 + .../chain_matrix/custom_chain_vine_swap.h | 2 +- .../include/gudhi/options.h | 1 - .../benchmark/Zigzag_benchmark.cpp | 3 +- .../benchmark/Zigzag_old_benchmark.cpp | 4 +- .../concept/ZigzagFilteredComplex.h | 129 ++ .../concept/ZigzagPersistenceOptions.h | 90 ++ src/Zigzag_persistence/example/CMakeLists.txt | 23 +- .../example/comparison_for_tests.cpp | 6 +- .../example_rips_zigzag_filtration.cpp | 6 +- .../example_simple_zigzag_filtration.cpp | 245 +-- .../example_zzfiltration_from_file.cpp | 130 ++ .../example/zigzag_filtration_example.txt | 46 + .../include/gudhi/Zigzag_persistence.h | 1393 +++++++++-------- .../include/gudhi/Zigzag_persistence_old.h | 34 +- src/Zigzag_persistence/test/CMakeLists.txt | 5 - .../test/zigzag_persistence_unit_test.cpp | 854 +++++----- 17 files changed, 1734 insertions(+), 1238 deletions(-) create mode 100644 src/Zigzag_persistence/concept/ZigzagFilteredComplex.h create mode 100644 src/Zigzag_persistence/concept/ZigzagPersistenceOptions.h create mode 100644 src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp create mode 100644 src/Zigzag_persistence/example/zigzag_filtration_example.txt diff --git a/src/Persistence_matrix/include/gudhi/chain_matrix/chain_rep_cycles.h b/src/Persistence_matrix/include/gudhi/chain_matrix/chain_rep_cycles.h index 143ee6beb3..909896ca7c 100644 --- a/src/Persistence_matrix/include/gudhi/chain_matrix/chain_rep_cycles.h +++ b/src/Persistence_matrix/include/gudhi/chain_matrix/chain_rep_cycles.h @@ -13,6 +13,7 @@ #include #include +#include #include "../utilities/utilities.h" //type definitions #include "../options.h" diff --git a/src/Persistence_matrix/include/gudhi/chain_matrix/custom_chain_vine_swap.h b/src/Persistence_matrix/include/gudhi/chain_matrix/custom_chain_vine_swap.h index 276f04c551..88a85f8151 100644 --- a/src/Persistence_matrix/include/gudhi/chain_matrix/custom_chain_vine_swap.h +++ b/src/Persistence_matrix/include/gudhi/chain_matrix/custom_chain_vine_swap.h @@ -61,7 +61,7 @@ class Custom_chain_vine_swap index _negative_positive_vine_swap(index columnIndex1, index columnIndex2); index _negative_vine_swap(index columnIndex1, index columnIndex2); - std::function birthComp_; // for F x F & H x H + std::function birthComp_; // for F x F std::function deathComp_; // for G x G }; diff --git a/src/Persistence_matrix/include/gudhi/options.h b/src/Persistence_matrix/include/gudhi/options.h index c4b9f9473d..3e4ea6843f 100644 --- a/src/Persistence_matrix/include/gudhi/options.h +++ b/src/Persistence_matrix/include/gudhi/options.h @@ -57,7 +57,6 @@ struct Default_options{ template struct Zigzag_options : Default_options{ static const bool has_row_access = true; - static const bool has_column_pairings = false; static const bool has_vine_update = true; static const bool is_of_boundary_type = false; static const bool is_indexed_by_position = false; diff --git a/src/Zigzag_persistence/benchmark/Zigzag_benchmark.cpp b/src/Zigzag_persistence/benchmark/Zigzag_benchmark.cpp index 3edac39c8d..37b4edfb24 100644 --- a/src/Zigzag_persistence/benchmark/Zigzag_benchmark.cpp +++ b/src/Zigzag_persistence/benchmark/Zigzag_benchmark.cpp @@ -29,7 +29,6 @@ using ST = Gudhi::Simplex_tree; // using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; using Vertex_handle = ST::Vertex_handle; using Filtration_value = ST::Filtration_value; -// using interval_filtration = ZP::interval_filtration; using Gudhi::persistence_matrix::Zigzag_options; using CT = Gudhi::persistence_matrix::Column_types; @@ -43,7 +42,7 @@ std::vector< std::pair > print_indices(ZP& zp, unsig essentials.insert(essentials.end(), i); } - for (auto& bar : zp.index_persistence_diagram()){ + for (auto& bar : zp.get_index_persistence_diagram()){ res.emplace_back(bar.birth(), bar.death()); essentials.erase(bar.birth()); essentials.erase(bar.death()); diff --git a/src/Zigzag_persistence/benchmark/Zigzag_old_benchmark.cpp b/src/Zigzag_persistence/benchmark/Zigzag_old_benchmark.cpp index bb24be2942..ca079443c3 100644 --- a/src/Zigzag_persistence/benchmark/Zigzag_old_benchmark.cpp +++ b/src/Zigzag_persistence/benchmark/Zigzag_old_benchmark.cpp @@ -30,7 +30,7 @@ using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; // using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; using Vertex_handle = ST::Vertex_handle; using Filtration_value = ST::Filtration_value; -using interval_filtration = ZP::interval_filtration; +using interval_filtration = ZP::fil_interval; std::vector< std::pair > print_indices(ZP& zp, unsigned int numberOfSimplices){ std::set essentials; @@ -40,7 +40,7 @@ std::vector< std::pair > print_indices(ZP& zp, unsig essentials.insert(essentials.end(), i); } - for (auto& bar : zp.index_persistence_diagram()){ + for (auto& bar : zp.get_index_persistence_diagram()){ res.emplace_back(bar.birth(), bar.death()); essentials.erase(bar.birth()); essentials.erase(bar.death()); diff --git a/src/Zigzag_persistence/concept/ZigzagFilteredComplex.h b/src/Zigzag_persistence/concept/ZigzagFilteredComplex.h new file mode 100644 index 0000000000..7a6e416f1b --- /dev/null +++ b/src/Zigzag_persistence/concept/ZigzagFilteredComplex.h @@ -0,0 +1,129 @@ +/* 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): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#ifndef CONCEPT_ZZ_COMPLEX_TYPE_H_ +#define CONCEPT_ZZ_COMPLEX_TYPE_H_ + +/** @file ZigzagFilteredComplex.h + * @brief Contains @ref Gudhi::zigzag_persistence::ZigzagFilteredComplex concept. + */ + +namespace Gudhi { +namespace zigzag_persistence { + +/** + * @brief Data structure storing the simplices in the current complex. + */ +class ZigzagFilteredComplex { + public: + /** + * @brief Signed integer type that needs to be long enough to store the numbers of arrows in the zigzag filtration. + */ + typename Simplex_key; + + /** + * @brief Handle to specify a simplex. + */ + typename Simplex_handle; + + /** + * @brief Handle to specify a vertex. Should be an integer type. + */ + typename Vertex_handle; + + /** + * @brief Type for filtration values. Usually 'double'. + */ + typename Filtration_value; + + /** + * @brief Range of simplex handles over the boundary of a simplex + */ + typename Boundary_simplex_range; + + /** + * @brief Constructor + */ + ZigzagFilteredComplex(); + + /** + * @brief Inserts the given simplex in the complex. + * + * @tparam VertexRange Range over the vertices of a simplex. + * @param simplex Simplex to insert represented by its vertices. + * @param filtration Filtration value at the insertion. + * @return A pair of a simplex handle and a boolean. + * The simplex handle represents the inserted simplex and + * the boolean if simplex was already contained in the complex or not. + */ + template + std::pair insert_simplex(const VertexRange& simplex, Filtration_value filtration); + + /** + * @brief Removes the given simplex. Assumes that the simplex is maximal and can be safely removed. + * + * @param sh Simplex handle representing the simplex to remove. + */ + void remove_maximal_simplex(Simplex_handle sh); + + /** + * @brief Returns the dimension of the given simplex. + * + * @param sh Simplex handle representing the simplex. + * @return Dimension of @a sh. + */ + int dimension(Simplex_handle sh); + + /** + * @brief Returns the key associated to the given simplex. + * + * @param sh Simplex handle representing the simplex. + * @return The key. + */ + Simplex_key key(Simplex_handle sh); + + /** + * @brief Assignes the given value to the given simplex as a key. + * + * @param sh Simplex handle representing the simplex. + * @param key Values to associate as key. + */ + void assign_key(Simplex_handle sh, Simplex_key key); + + /** + * @brief Finds the given simplex in the complex and returns the associated simplex handle. + * + * @tparam VertexRange Range over the vertices of a simplex. + * @param simplex Simplex to find represented by its vertices. + * @return The simplex handle associated to @a simplex if the simplex is found, @ref null_simplex() otherwise. + */ + template + Simplex_handle find(const VertexRange& simplex); + + /** + * @brief Returns a range of simplex handles representing the boundary of the given simplex. + * + * @param sh Simplex handle representing the simplex. + * @return Range of simplex handles. + */ + Boundary_simplex_range boundary_simplex_range(Simplex_handle sh); + + /** + * @brief Returns a simplex handle representing a non existing simplex. + * + * @return A simplex handle. + */ + Simplex_handle null_simplex(); +}; + +} // namespace zigzag_persistence +} // namespace Gudhi + +#endif // CONCEPT_ZZ_COMPLEX_TYPE_H_ diff --git a/src/Zigzag_persistence/concept/ZigzagPersistenceOptions.h b/src/Zigzag_persistence/concept/ZigzagPersistenceOptions.h new file mode 100644 index 0000000000..7ee9d9be7d --- /dev/null +++ b/src/Zigzag_persistence/concept/ZigzagPersistenceOptions.h @@ -0,0 +1,90 @@ +/* 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): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#ifndef CONCEPT_ZZ_OPTIONS_TYPE_H_ +#define CONCEPT_ZZ_OPTIONS_TYPE_H_ + +/** @file ZigzagPersistenceOptions.h + * @brief Contains @ref Gudhi::zigzag_persistence::ZigzagPersistenceOptions concept. + */ + +namespace Gudhi { +namespace zigzag_persistence { + +/** + * @brief List of options used for the matrix maintained for the zigzag persistence computation. + */ +struct ZigzagPersistenceOptions { + /** + * @brief Type for the coefficient field type. Has to support \f$Z_2\f$. + */ + typename field_coeff_type; + + /** + * @brief Has to be set to true. Indicates that the computation will be made with \f$Z_2\f$ coefficients. + */ + static const bool is_z2 = true; + /** + * @brief Type of the columns in the matrix. + * The available column types are given by @ref Gudhi::persistence_matrix::Column_types. + * The column type has to support row access. + */ + static const Column_types column_type; + + /** + * @brief Has to be set to true. Indicates that the rows should be directly accessible in the matrix. + */ + static const bool has_row_access = true; + /** + * @brief Set to true, if the rows should be intrusive lists or to false if they should be sets. True is recommended. + * Note that intrusive rows are not compatible with certain column types. + */ + static const bool has_intrusive_rows; + /** + * @brief Has to set to true. Indicates that the rows of the matrix can be removed. + */ + static const bool has_removable_rows = true; + /** + * @brief Has to be set to false. Indicates that the matrix should not store birth/death pairs of its columns. + */ + static const bool has_column_pairings = false; + /** + * @brief Has to be set to true. Enables maintaining the matrix while switching columns. + */ + static const bool has_vine_update = true; + /** + * @brief If set to true, the matrix can retrieve the representative cycles for the cycle classes. + * This option is useless for zigzag computation and therefore it is recommended to set it to false. + */ + static const bool can_retrieve_representative_cycles; + /** + * @brief This value has to be defined but will be ignored. + */ + static const bool has_column_compression; + /** + * @brief Has to be set to false. + * Indicates that the matrix should represent the base of the chain complex and not of the boundary group. + */ + static const bool is_of_boundary_type = false; + /** + * @brief Has to be set to true. Indicates that the columns of the matrix can be removed. + */ + static const bool has_removable_columns = true; + /** + * @brief Has to be set to false. + * Indicates that the access to the columns will be done through simplex IDs instead of column positions. + */ + static const bool is_indexed_by_position = false; +}; + +} // namespace zigzag_persistence +} // namespace Gudhi + +#endif // CONCEPT_ZZ_OPTIONS_TYPE_H_ diff --git a/src/Zigzag_persistence/example/CMakeLists.txt b/src/Zigzag_persistence/example/CMakeLists.txt index 1a981ce998..c9814e2768 100644 --- a/src/Zigzag_persistence/example/CMakeLists.txt +++ b/src/Zigzag_persistence/example/CMakeLists.txt @@ -6,20 +6,27 @@ if(TARGET TBB::tbb) endif() add_test(NAME Zigzag_persistence_example_simple_zigzag_filtration COMMAND $) -add_executable ( comp comparison_for_tests.cpp ./ext_zz/fzz/fzz.cpp ) -target_include_directories(comp PUBLIC ./ext_zz) -target_compile_options(comp PUBLIC "-fopenmp") -target_link_options(comp PUBLIC "-fopenmp") +add_executable ( Zigzag_persistence_example_zzfiltration_from_file example_zzfiltration_from_file.cpp ) +if(TARGET TBB::tbb) + target_link_libraries(Zigzag_persistence_example_zzfiltration_from_file TBB::tbb) +endif() +file(COPY "zigzag_filtration_example.txt" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) +add_test(NAME Zigzag_persistence_example_zzfiltration_from_file COMMAND $ "${CMAKE_CURRENT_BINARY_DIR}/zigzag_filtration_example.txt") + +# add_executable ( comp comparison_for_tests.cpp ./ext_zz/fzz/fzz.cpp ) +# target_include_directories(comp PUBLIC ./ext_zz) +# target_compile_options(comp PUBLIC "-fopenmp") +# target_link_options(comp PUBLIC "-fopenmp") # add_executable ( Zigzag_persistence_example_rips_zigzag_filtration example_rips_zigzag_filtration.cpp ) # target_include_directories(Zigzag_persistence_example_rips_zigzag_filtration PUBLIC ./ext_zz) # target_compile_options(Zigzag_persistence_example_rips_zigzag_filtration PUBLIC "-fopenmp") # target_link_options(Zigzag_persistence_example_rips_zigzag_filtration PUBLIC "-fopenmp") -add_executable ( rips example_rips_zigzag_filtration.cpp ) -target_include_directories(rips PUBLIC ./ext_zz) -target_compile_options(rips PUBLIC "-fopenmp") -target_link_options(rips PUBLIC "-fopenmp") +# add_executable ( rips example_rips_zigzag_filtration.cpp ) +# target_include_directories(rips PUBLIC ./ext_zz) +# target_compile_options(rips PUBLIC "-fopenmp") +# target_link_options(rips PUBLIC "-fopenmp") # add_executable ( rips_old example_rips_zigzag_filtration.cpp ) # target_include_directories(rips_old PUBLIC ./ext_zz) diff --git a/src/Zigzag_persistence/example/comparison_for_tests.cpp b/src/Zigzag_persistence/example/comparison_for_tests.cpp index 2beded4910..a2f972966c 100644 --- a/src/Zigzag_persistence/example/comparison_for_tests.cpp +++ b/src/Zigzag_persistence/example/comparison_for_tests.cpp @@ -2,7 +2,7 @@ * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. * Author(s): Hannah Schreiber * - * Copyright (C) 2014 Inria + * Copyright (C) 2023 Inria * * Modification(s): * - YYYY/MM Author: Description of the modification @@ -36,7 +36,7 @@ using CT = Gudhi::persistence_matrix::Column_types; using ZP = Gudhi::zigzag_persistence::Zigzag_persistence >; using Vertex_handle = ST::Vertex_handle; using Filtration_value = ST::Filtration_value; -using interval_filtration = ZP::interval_filtration; +using interval_filtration = ZP::filtration_value_interval; using DField = dionysus::Z2Field; using Simplex = dionysus::Simplex<>; @@ -72,7 +72,7 @@ std::vector< std::pair > print_indices(ZP& zp, unsig essentials.insert(essentials.end(), i); } - for (auto& bar : zp.index_persistence_diagram()){ + for (auto& bar : zp.get_index_persistence_diagram()){ // std::clog << bar.birth() << " - "; // std::clog << bar.death(); // std::clog << " (" << bar.dim() << ")\n"; diff --git a/src/Zigzag_persistence/example/example_rips_zigzag_filtration.cpp b/src/Zigzag_persistence/example/example_rips_zigzag_filtration.cpp index ed021faecc..88298890c7 100644 --- a/src/Zigzag_persistence/example/example_rips_zigzag_filtration.cpp +++ b/src/Zigzag_persistence/example/example_rips_zigzag_filtration.cpp @@ -2,7 +2,7 @@ * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. * Author(s): Hannah Schreiber * - * Copyright (C) 2014 Inria + * Copyright (C) 2023 Inria * * Modification(s): * - YYYY/MM Author: Description of the modification @@ -34,7 +34,7 @@ using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; using Vertex_handle = ST::Vertex_handle; using Filtration_value = ST::Filtration_value; -using interval_filtration = ZP::interval_filtration; +using interval_filtration = ZP::filtration_value_interval; std::vector< std::pair > print_indices(ZP& zp, unsigned int numberOfSimplices){ std::set essentials; @@ -44,7 +44,7 @@ std::vector< std::pair > print_indices(ZP& zp, unsig essentials.insert(essentials.end(), i); } - for (auto& bar : zp.index_persistence_diagram()){ + for (auto& bar : zp.get_index_persistence_diagram()){ res.emplace_back(bar.birth(), bar.death()); essentials.erase(bar.birth()); essentials.erase(bar.death()); diff --git a/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp b/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp index 4750eb5358..82e20bba63 100644 --- a/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp +++ b/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp @@ -2,117 +2,176 @@ * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. * Author(s): Hannah Schreiber * - * Copyright (C) 2014 Inria + * Copyright (C) 2023 Inria * * Modification(s): * - YYYY/MM Author: Description of the modification */ -#include -#include - #include -#include // for pair +#include #include +#include +#include + using ST = Gudhi::Simplex_tree; using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; using Vertex_handle = ST::Vertex_handle; using Filtration_value = ST::Filtration_value; -using interval_filtration = ZP::interval_filtration; +using interval_filtration = ZP::filtration_value_interval; + +// void print_complex(ZP& zp) { +// std::clog << std::endl << "Current complex:" << std::endl; +// const auto& cpx = zp.get_complex(); +// for (const auto& sh : cpx.complex_simplex_range()) { +// for (auto v : cpx.simplex_vertex_range(sh)) { +// std::cout << v << " "; +// } +// std::cout << " - " << cpx.filtration(sh) << "" << std::endl; +// } +// } + +void print_barcode(ZP& zp) { + std::clog << std::endl << "Current barcode:" << std::endl; + for (auto& bar : zp.get_persistence_diagram(0, true)) { + std::clog << std::floor(bar.birth()) << " - "; + if (bar.death() == std::numeric_limits::infinity()) { + std::clog << "inf"; + } else { + std::clog << std::floor(bar.death()); + } + std::clog << " (" << bar.dim() << ")" << std::endl; + } +} + +void print_indices(ZP& zp) { + std::clog << std::endl << "Current pairs:" << std::endl; + for (auto& bar : zp.get_index_persistence_diagram()) { + std::clog << bar.birth() << " - "; + std::clog << bar.death(); + std::clog << " (" << bar.dim() << ")" << std::endl; + } +} -void print_complex(ZP& zp){ - std::clog << std::endl << "Current complex:" << std::endl; - zp.print_current_complex(); +std::vector > get_simplices() { + return {{0}, + {1}, + {2}, + {0, 1}, + {0, 2}, + {3}, + {1, 2}, + {4}, + {3, 4}, + {5}, + {0, 1, 2}, + {4, 5}, + {3, 5}, + {3, 4, 5}, + {0, 1, 2}, // remove + {3, 4, 5}, // remove + {1, 4}, + {0, 1, 2}, + {2, 4}, + {3, 4, 5}, + {0, 4}, + {0, 2, 4}, + {1, 2, 4}, + {0, 1, 4}, + {3, 4, 5}, // remove + {3, 4}, // remove + {3, 5}, // remove + {0, 1, 2, 4}, + {0, 1, 2, 4}}; // remove } -void print_barcode(ZP& zp){ - std::clog << std::endl << "Current barcode:" << std::endl; - for (auto& bar : zp.persistence_diagram()){ - std::clog << std::floor(bar.birth()) << " - "; - if (bar.death() == std::numeric_limits::infinity()){ - std::clog << "inf"; - } else { - std::clog << std::floor(bar.death()); - } - std::clog << " (" << bar.dim() << ")\n"; - } +std::vector get_filtration_values() { + return {0, 0, 0, + 1, 1, 1, + 2, 2, 2, + 3, 3, 3, 3, + 4, + 5, + 6, 6, 6, + 7, 7, 7, 7, 7, 7, + 8, + 9, 9, 9, + 10}; } -void print_indices(ZP& zp){ - std::clog << std::endl << "Current pairs:" << std::endl; - for (auto& bar : zp.index_persistence_diagram()){ - std::clog << bar.birth() << " - "; - std::clog << bar.death(); - std::clog << " (" << bar.dim() << ")\n"; - } +std::vector get_directions() { + return {true, true, true, true, true, true, true, true, true, true, true, true, true, true, + false, false, + true, true, true, true, true, true, true, true, + false, false, false, + true, + false}; +} + +std::vector get_batch_sizes() { + return {14, 2, 8, 3, 1, 1}; +} + +void one_by_one() { + ZP zp; + + std::vector > simplices = get_simplices(); + std::vector fils = get_filtration_values(); + std::vector dirs = get_directions(); + + for (unsigned int i = 0; i < simplices.size(); ++i) { + if (i > 0 && dirs[i] != dirs[i - 1]) { + // print_complex(zp); + print_barcode(zp); + print_indices(zp); + } + if (dirs[i]) { + zp.insert_simplex(simplices[i], fils[i]); + } else { + zp.remove_simplex(simplices[i], fils[i]); + } + } + // print_complex(zp); + print_barcode(zp); + print_indices(zp); +} + +void in_batches() { + ZP zp; + + std::vector > simplices = get_simplices(); + std::vector fils = get_filtration_values(); + std::vector sizes = get_batch_sizes(); + + unsigned int start; + unsigned int end = 0; + bool dir = true; //first operation has to be an insertion + for (auto s : sizes){ + start = end; + end += s; + if (dir){ + zp.insert_simplices_contiguously(simplices.begin() + start, + simplices.begin() + end, + fils.begin() + start); + } else { + zp.remove_simplices_contiguously(simplices.begin() + start, + simplices.begin() + end, + fils.begin() + start); + } + dir = !dir; + // print_complex(zp); + print_barcode(zp); + print_indices(zp); + } } int main(int argc, char* const argv[]) { - ZP zp; - - std::vector > simplices{ - {0},{1},{2}, - {0,1},{0,2},{3}, - {1,2},{4},{3,4}, - {5},{0,1,2},{4,5},{3,5}}; - std::vector fils{0,0,0,1,1,1,2,2,2,3,3,3,3}; - zp.insert_simplices_contiguously(simplices, fils); - - print_complex(zp); - print_barcode(zp); - print_indices(zp); - - std::vector simplex{3,4,5}; - zp.insert_simplex(simplex, 4); - - print_complex(zp); - print_barcode(zp); - print_indices(zp); - - simplex[0] = 0; - simplex[1] = 1; - simplex[2] = 2; - zp.remove_simplex(simplex, 5); - - print_complex(zp); - print_barcode(zp); - print_indices(zp); - - simplex[0] = 3; - simplex[1] = 4; - simplex[2] = 5; - zp.remove_simplex(simplex, 6); - - print_complex(zp); - print_barcode(zp); - print_indices(zp); - - simplices = {{1,4},{0,1,2},{2,4},{3,4,5},{0,4},{0,2,4},{1,2,4},{0,1,4}}; - fils = {6,6,7,7,7,7,7,7}; - zp.insert_simplices_contiguously(simplices, fils); - - print_complex(zp); - print_barcode(zp); - print_indices(zp); - - simplices = {{3,4,5},{3,4},{3,5}}; - fils = {8,9,9}; - zp.remove_simplices_contiguously(simplices, fils); - - print_complex(zp); - print_barcode(zp); - print_indices(zp); - - simplex[0] = 0; - simplex[1] = 1; - simplex[2] = 2; - simplex.push_back(4); - zp.insert_simplex(simplex, 9); - - print_complex(zp); - print_barcode(zp); - print_indices(zp); - - return 0; + std::clog << "********** Example one_by_one **********" << std::endl; + one_by_one(); + + std::clog << std::endl << "********** Example in_batches **********" << std::endl; + in_batches(); + + return 0; } diff --git a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp new file mode 100644 index 0000000000..21bcfc1fde --- /dev/null +++ b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp @@ -0,0 +1,130 @@ +/* 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): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#include +#include +#include + +#include +#include + +using ST = Gudhi::Simplex_tree; +using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; +using Vertex_handle = ST::Vertex_handle; +using Filtration_value = ST::Filtration_value; +using interval_filtration = ZP::filtration_value_interval; + +enum lineType : int { INCLUSION, REMOVAL, COMMENT }; + +void print_barcode(ZP& zp) { + std::clog << std::endl << "Current barcode:" << std::endl; + for (auto& bar : zp.get_persistence_diagram(0, true)) { + std::clog << std::floor(bar.birth()) << " - "; + if (bar.death() == std::numeric_limits::infinity()) { + std::clog << "inf"; + } else { + std::clog << std::floor(bar.death()); + } + std::clog << " (" << bar.dim() << ")" << std::endl; + } + std::clog << std::endl; +} + +lineType read_operation(std::string& line, std::vector& vertices, double& timestamp) { + lineType type; + vertices.clear(); + Vertex_handle num; + + size_t current = line.find_first_not_of(' ', 0); + if (current == std::string::npos) return COMMENT; + + if (line[current] == 'i') + type = INCLUSION; + else if (line[current] == 'r') + type = REMOVAL; + else if (line[current] == '#') + return COMMENT; + else { + std::clog << "Syntaxe error in file." << std::endl; + exit(0); + } + + current = line.find_first_not_of(' ', current + 1); + if (current == std::string::npos) { + std::clog << "Syntaxe error in file." << std::endl; + exit(0); + } + size_t next = line.find_first_of(' ', current); + timestamp = std::stod(line.substr(current, next - current)); + + current = line.find_first_not_of(' ', next); + if (current == std::string::npos) { + std::clog << "Syntaxe error in file." << std::endl; + exit(0); + } + + do { + next = line.find_first_of(' ', current); + num = std::stoi(line.substr(current, next - current)); + vertices.push_back(num); + current = line.find_first_not_of(' ', next); + } while (current != std::string::npos); + + return type; +} + +int main(int argc, char* const argv[]) { + if (argc != 2) { + if (argc < 2) + std::clog << "Missing argument: input file name is needed." << std::endl; + else + std::clog << "Too many arguments: only input file name is needed." << std::endl; + return 0; + } + + std::string line; + std::ifstream file(argv[1]); + ZP zp; + + if (file.is_open()) { + std::vector vertices; + double timestamp; + lineType type; + + while (getline(file, line, '\n') && read_operation(line, vertices, timestamp) == COMMENT); + double lastTimestamp = timestamp; + // first operation has to be an insertion. + zp.insert_simplex(vertices, timestamp); + std::cout << line << std::endl; + + while (getline(file, line, '\n')) { + type = read_operation(line, vertices, timestamp); + if (type != COMMENT && lastTimestamp != timestamp) { + print_barcode(zp); + lastTimestamp = timestamp; + } + if (type != COMMENT) std::cout << line << std::endl; + + if (type == INCLUSION) { + zp.insert_simplex(vertices, timestamp); + } else if (type == REMOVAL) { + zp.remove_simplex(vertices, timestamp); + } + } + print_barcode(zp); + + file.close(); + } else { + std::clog << "Unable to open input file." << std::endl; + file.setstate(std::ios::failbit); + } + + return 0; +} \ No newline at end of file diff --git a/src/Zigzag_persistence/example/zigzag_filtration_example.txt b/src/Zigzag_persistence/example/zigzag_filtration_example.txt new file mode 100644 index 0000000000..db02d5b2d8 --- /dev/null +++ b/src/Zigzag_persistence/example/zigzag_filtration_example.txt @@ -0,0 +1,46 @@ +# simple example of zigzag filtration +# i: inclusion +# r: removal +# first value: filtration value +# remaining values: vertices of the simplex to include/remove in ascending order +# #: comment line + +i 0 0 +i 0 1 +i 0 2 + +i 1 0 1 +i 1 0 2 +i 1 3 + +i 2 1 2 +i 2 4 +i 2 3 4 + +i 3 5 +i 3 0 1 2 +i 3 4 5 +i 3 3 5 + +i 4 3 4 5 + +r 5 0 1 2 + +r 6 3 4 5 +i 6 1 4 +i 6 0 1 2 + +i 7 2 4 +i 7 3 4 5 +i 7 0 4 +i 7 0 2 4 +i 7 1 2 4 +i 7 0 1 4 + +r 8 3 4 5 + +r 9 3 4 +r 9 3 5 +i 9 0 1 2 4 + +r 10 0 1 2 4 \ No newline at end of file diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h index bccf73ab03..51fd7f1c54 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h @@ -10,6 +10,12 @@ * - YYYY/MM Author: Description of the modification */ +/** + * @file Zigzag_persistence.h + * @author Clément Maria, Hannah Schreiber + * @brief Contains the implementation of the @ref Gudhi::zigzag_persistence::Zigzag_persistence class. + */ + #ifndef ZIGZAG_PERSISTENCE_H_ #define ZIGZAG_PERSISTENCE_H_ @@ -21,6 +27,7 @@ #include #include +#include #include #include #include @@ -29,6 +36,7 @@ #include #include #include +#include #include #include @@ -37,689 +45,704 @@ namespace Gudhi { namespace zigzag_persistence { -//---------------------------------------------------------------------------------- -/** \class Zigzag_persistence Zigzag_persistence.h gudhi/Zigzag_persistence.h - * \brief Computation of the zigzag persistent homology of a zigzag - * filtered complex. - * - * \details The type ZigzagFilteredComplex::Simplex_key counts the number of - * insertions and - * deletions of simplices, which may be large in zigzag persistence and require - * more than 32 bits of storage. The type used (int, long, etc) should be chosen in - * consequence. Simplex_key must be signed. - * - * Over all insertions, the Simplex_key must be positive and strictly increasing - * when forward iterating along the zigzag filtration. - */ -template < typename ZigzagFilteredComplex - , typename ZigzagPersistenceOptions = Gudhi::persistence_matrix::Zigzag_options<> > -class Zigzag_persistence { -public: - typedef ZigzagFilteredComplex Complex; - typedef ZigzagPersistenceOptions Options; - /*** Types defined in the complex ***/ - // Data attached to each simplex to interface with a Property Map. - typedef typename Complex::Simplex_key Simplex_key;//must be signed - typedef typename Complex::Simplex_handle Simplex_handle; - typedef typename Complex::Vertex_handle Vertex_handle; - typedef typename Complex::Filtration_value Filtration_value; - // - - /** \brief Structure to store persistence intervals by their filtration values. - * - * \details By convention, interval \f$[b;d]\f$ are - * closed for finite indices b and d, and open for left-infinite and/or - * right-infinite endpoints.*/ - struct interval_filtration { - interval_filtration() {} - interval_filtration(int dim, Filtration_value b, Filtration_value d) : dim_(dim), b_(b), d_(d) {} - /** Returns the absolute length of the interval \f$|d-b|\f$. */ - Filtration_value length() { - if(b_ == d_) { return 0; } //otherwise inf - inf would return nan. - return std::abs(b_ - d_); - } - /** Returns the absolute length of the log values of birth and death, i.e. \f$|\log d - \log b|\f$.. */ - Filtration_value log_length() {//return the log-length - if(b_ == d_) { return 0; } //otherwise inf - inf would return nan. - return std::abs(log2((double)b_) - log2((double)d_)); - } - /** Returns the dimension of the homological feature corresponding to the - * interval. */ - int dim() const { return dim_; }//return the homological dimension of the interval - /** Returns the birth of the interval.*/ - Filtration_value birth() const { return b_; }//return the birth value - /** Returns the death of the interval.*/ - Filtration_value death() const { return d_; }//return the death value - /** Swaps the values of birth and death.*/ - void swap_birth_death() { std::swap(b_,d_); } - - private://note that we don't assume b_ <= d_ - int dim_; //homological dimension - Filtration_value b_; //filtration value associated to birth index - Filtration_value d_; //filtration value associated to death index - }; - - /** \brief Structure to store persistence intervals by their index values. - * - * \details By convention, interval [b;d] are - * closed for finite indices b and d, and open for left-infinite and/or - * right-infinite endpoints. - */ - struct interval_index { - interval_index() {} - interval_index(int dim, Simplex_key b, Simplex_key d) : dim_(dim), b_(b), d_(d) {} - /** Returns the dimension of the homological feature corresponding to the - * interval. */ - int dim() const { return dim_; }//return the homological dimension of the interval - /** Returns the birth index of the interval.*/ - Filtration_value birth() const { return b_; }//return the birth value - /** Returns the death index of the interval.*/ - Filtration_value death() const { return d_; }//return the death value - - private://note that we don't assume b_ <= d_ - int dim_; //homological dimension - Simplex_key b_; //filtration value associated to birth index - Simplex_key d_; //filtration value associated to death index - }; - -private: - /* Comparison function to sort intervals by decreasing log-length in the - * output persistence diagram, i.e., - * [f(b),f(d)]<[f(b'),f(d')] iff |log2(f(b))-log2(f(d))|> |log2(f(b'))-log2(f(d'))| - */ - struct cmp_intervals_by_log_length { - cmp_intervals_by_log_length(){} - bool operator()( interval_filtration p, interval_filtration q) - { - if(p.dim() != q.dim()) {return p.dim() < q.dim();}//lower dimension first - if(p.log_length() != q.log_length()) {return p.log_length() > q.log_length();} - if(p.birth() != q.birth()) {return p.birth() < q.birth();}//lex order - return p.death() < q.death(); - } - }; - /* Comparison function to sort intervals by decreasing length in the - * output persistence diagram, i.e., - * [f(b),f(d)]<[f(b'),f(d')] iff |f(b)-f(d)| > |f(b')-f(d')| - */ - struct cmp_intervals_by_length { - cmp_intervals_by_length(){} - bool operator()( interval_filtration p, interval_filtration q) - { - if(p.length() != q.length()) { return p.length() > q.length(); }//longest 1st - if(p.dim() != q.dim()) {return p.dim() < q.dim();}//lower dimension first - if(p.birth() != q.birth()) {return p.birth() < q.birth();}//lex order - return p.death() < q.death(); - } - }; - - using matrix_type = Gudhi::persistence_matrix::Matrix; - using index = Gudhi::persistence_matrix::index; - -public: - /** \brief Initialization of the Zigzag_persistence class. - * - * \param[in] cpx A model of ZigzagFilteredComplex. - * */ - Zigzag_persistence(unsigned int min_number_of_simplices = 0, int ignore_cycles_above_dim = -1) - : cpx_() - , dim_max_(ignore_cycles_above_dim) - , matrix_(min_number_of_simplices, - [this](index columnIndex1, index columnIndex2){ - return birth_ordering_.birth_order(births_.at(columnIndex1), births_.at(columnIndex2)); - }) - , birth_ordering_() - , persistence_diagram_() - , num_arrow_(-1) - , previous_filtration_value_(std::numeric_limits::infinity()) - , filtration_values_() {} - -private: - - /** Maintains the birth ordering <=b. Contains an std::map of size the number of - * non-zero rows of the homology matrix, at any time during the computation of - * zigzag persistence. - * - * By construction, we maintain the map satisfying - * 'birth_to_pos_[i] < birth_to_pos_[j]', - * with 0 <= i,j <= k indices in the quiver '0 \leftrightarrow ... \leftrightarrow i \leftrightarrow .. \leftrightarrow k' - * visited at time k of the algorithm (prefix of length k of the full zigzag - * filtration '0 \leftrightarrow ... \leftrightarrow i \leftrightarrow .. \leftrightarrow k \leftrightarrow ... \leftrightarrow n' that is studied), - * iff i k+1 forward, then j 0 -> 1 -> 2 <- 3 <- 4 -> 5 <- 6 etc - birth_ordering() : birth_to_pos_(), max_birth_pos_(0), min_birth_pos_(-1) {} - - //when the arrow key-1 -> key is forward, key is larger than any other index - //i < key in the birth ordering b k2 - bool reverse_birth_order(Simplex_key k1, Simplex_key k2) { - return birth_to_pos_[k1] > birth_to_pos_[k2]; - } - - private: - //birth_to_pos_[i] < birth_to_pos_[j] iff i birth_to_pos_; - //by construction, max_birth_pos_ (resp. min_birth_pos_) is strictly larger - //(resp. strictly smaller) than any value assigned to a key so far. - Simplex_key max_birth_pos_; - Simplex_key min_birth_pos_; - }; - -public: - /** \brief Computes the zigzag persistent homology of a zigzag filtered complex, - * using the reflection and transposition algorithm of \cite zigzag_reflection. - * - * \details After computation, the persistence diagram can be accessed via - * member method persistence_diagram, for the diagram with filtration - * values, or member method index_persistence_diagram, for the - * diagram with - * indices of paired simplices. - * - * - * matrix_, originally empty, maintains the set of chains, with a - * partition \f$ F \sqcup G \sqcup H\f$ - * representing a compatible homology basis as in \cite zigzag_reflection. - * - * Each simplex in the complex stores a key field that stores the index of - * its insertion in the zigzag filtration. - * - * The algorithm maintains a compatible homology basis for the zigzag filtration. - * - * \f$$\emptyset = K_0 \leftrightarrow (...) \leftrightarrow K_i \leftarrow ... \leftarrow \emptyset\f$$ - * - * where the prefix from \f$K_0\f$ to \f$K_i\f$ is equal to the i-th prefix of - * the input zigzag - * filtration given by cpx_.filtration_simplex_range(), and - * the suffix - * (from \f$K_i\f$ - * to the right) is a sequence of simplex removals. Due to the structure of - * reflection diamonds, the removals are in reverse order of the insertions, to - * reduce the amount of transposition diamonds. - * - * Consequently, using cpx_.key(zzsh) as indexing for the matrix - * rows/cells, - * with the natural order on integers, makes our homology matrix matrix_ upper - * triangular for the suffix \f$K_i \leftarrow ... \leftarrow 0\f$, seen as a - * standard persistence - * filtration. At \f$K_i\f$, the natural order on integers is also equivalent to the - * death-order \f$\leq_d\f$ (because all arrows in the suffix are backward). - * - * Insertion: cpx_.key(*zzit) is a strictly increasing sequence - * for zzit - * insertion of cells (does not need to be contiguous). However, for every forward - * arrow, we have cpx_.key(*zzit) == num_arrows_. - * Removal: cpx_.key(*zzit) gives the assigned key (during past - * insertion) of a - * cell == *zzit during a removal. We use num_arrows_ - * to record the deaths in the - * persistence diagram. - * Insertion and Removal: zzit.filtration() is totally monotone. - * Note that the - * iterator encodes the filtration, and not the cells within the complex structure. - */ - - template> - void insert_simplex(const VertexRange& simplex, Filtration_value filtration_value) - { - if (dim_max_ != -1 && simplex.size() > static_cast(dim_max_) + 1) return; - - ++num_arrow_; - - if (filtration_value != previous_filtration_value_) //check whether the filt value has changed - { //consecutive pairs (i,f), (j,f') mean simplices of index k in [i,j-1] have - previous_filtration_value_ = filtration_value; //filtration value f - filtration_values_.emplace_back(num_arrow_, previous_filtration_value_); - } - - std::pair res = cpx_.insert_simplex(simplex, filtration_value); - GUDHI_CHECK(res.second, "Zigzag_persistence::insert_simplex - insertion of a simplex already in the complex"); - cpx_.assign_key(res.first, num_arrow_); - forward_arrow(res.first); - } - - template> - void remove_simplex(const VertexRange& simplex, Filtration_value filtration_value) - { - if (dim_max_ != -1 && simplex.size() > static_cast(dim_max_) + 1) return; - - ++num_arrow_; - - Simplex_handle sh = cpx_.find(simplex); - GUDHI_CHECK(sh != cpx_.null_simplex(), "Zigzag_persistence::remove_simplex - removal of a simplex not in the complex"); - - if (filtration_value != previous_filtration_value_) //check whether the filt value has changed - { //consecutive pairs (i,f), (j,f') mean simplices of index k in [i,j-1] have - previous_filtration_value_ = filtration_value; //filtration value f - filtration_values_.emplace_back(num_arrow_, previous_filtration_value_); - } - - backward_arrow(sh); - cpx_.remove_maximal_simplex(sh); - } - - template>, - class FiltrationRange = std::initializer_list> - void insert_simplices_contiguously(const SimplexRange& simplices, const FiltrationRange& filtration_values) - { - auto simplexIt = simplices.begin(); - auto filIt = filtration_values.begin(); - for (; simplexIt != simplices.end(); ++simplexIt, ++filIt) { - insert_simplex(*simplexIt, *filIt); - } - } - - template>, - class FiltrationRange = std::initializer_list> - void remove_simplices_contiguously(const SimplexRange& simplices, const FiltrationRange& filtration_values) - { - auto simplexIt = simplices.begin(); - auto filIt = filtration_values.begin(); - for (; simplexIt != simplices.end(); ++simplexIt, ++filIt) { - remove_simplex(*simplexIt, *filIt); - } - } - - template - void insert_simplices_contiguously(SimplexRangeIterators simplex_range_start, - SimplexRangeIterators simplex_range_end, - FiltrationRangeIterators filtration_range_start) - { - for (; simplex_range_start != simplex_range_end; ++simplex_range_start, ++filtration_range_start) { - insert_simplex(*simplex_range_start, *filtration_range_start); - } - } - - template - void remove_simplices_contiguously(SimplexRangeIterators simplex_range_start, - SimplexRangeIterators simplex_range_end, - FiltrationRangeIterators filtration_range_start) - { - for (; simplex_range_start != simplex_range_end; ++simplex_range_start, ++filtration_range_start) { - remove_simplex(*simplex_range_start, *filtration_range_start); - } - } - - void print_current_complex(){ - for (auto& sh : cpx_.complex_simplex_range()){ - for (auto v : cpx_.simplex_vertex_range(sh)){ - std::cout << v << " "; - } - std::cout << " - " << cpx_.filtration(sh) << "\n"; - } - } - -private: - /** \brief Computes the boundary cycle of the new simplex zzsh, and express it as a - * sum of cycles. If all cycles are boundary cycles, i.e., columns with G-index - * in the matrix, then [\partial zzsh] = 0 and we apply an injective diamond to - * the zigzag module. Otherwise, we keep reducing with boundary- and live- cycles, - * i.e., columns with (F \cup G)-indices, and then apply a surjective diamond to - * the zigzag module. - */ - void forward_arrow( Simplex_handle zzsh ) - { //maintain the <=b order - birth_ordering_.add_birth_forward(num_arrow_); - - //Reduce the boundary of zzsh in the basis of cycles. - //Compute the simplex keys of the simplices of the boundary of zzsh. - std::set< Simplex_key > col_bsh; //set maintains the natural order on indices - for( auto b_sh : cpx_.boundary_simplex_range(zzsh) ) - { col_bsh.insert(cpx_.key(b_sh)); } - - std::vector chains_in_F; - matrix_.insert_boundary(num_arrow_, col_bsh, chains_in_F); - - if (!chains_in_F.empty()){ - births_.try_emplace(matrix_.get_column_with_pivot(num_arrow_), -2); - surjective_reflection_diamond(zzsh, chains_in_F); - } else { - births_.try_emplace(matrix_.get_column_with_pivot(num_arrow_), num_arrow_); - } - } - - /** The vector chains_in_F is sorted by decreasing lowest index values in the - * columns corresponding to the chains, due to its computation in the reduction of - * \partial zzsh in forward_arrow(...). It is equivalent to decreasing death index - * order w.r.t. the & chains_in_F) - { //fp is the largest death index for <=d - //Set col_fp: col_fp <- col_f1+...+col_fp (now in G); preserves lowest idx - auto chain_fp = chains_in_F.front(); //col_fp, with largest death bool - { return birth_ordering_.reverse_birth_order(k1,k2); };//true iff b(k1) >b b(k2) - - //available_birth: for all i by >d value of the d_i, - //contains at step i all b_j, j > i, and maybe b_i if not stolen - std::set< Simplex_key, decltype(cmp_birth) > available_birth(cmp_birth); - //for f1 to f_{p} (i by <=d), insertion in available_birth_to_fidx sorts by >=b - for(auto &chain_f : chains_in_F) { available_birth.insert(births_.at(chain_f)); } - - auto maxb_it = available_birth.begin();//max birth cycle - auto maxb = *maxb_it; //max birth value, for persistence diagram - available_birth.erase(maxb_it); //remove max birth cycle (stolen) - - auto last_modified_chain_it = chains_in_F.rbegin(); - - //consider all death indices by increasing the maximal availabe death. - //Let c_1 ... c_f be the chains s.t. <[c_1+...+c_f]> is the kernel and - // death(c_i) >d death(c_i-1). If the birth of c_i is not available, we set - //c_i <- c_i + c_i-1 + ... + c_1, which is [c_i + c_i-1 + ... + c_1] on - //the right (of death the maximali <=> the maxj>k, are indeed c_j - //set c_i <- c_i + (c_i-1) + ... + (c_k+1) + (c_k + ... + c_1) - for(auto chain_passed_it = last_modified_chain_it;//all with smaller modified_columns; - const auto& row = matrix_.get_row(cpx_.key(zzsh)); - modified_columns.reserve(row.size()); - std::transform(row.begin(), - row.end(), - std::back_inserter(modified_columns), - [](const auto& cell) { return cell.get_column_index(); }); - //Sort by left-to-right order in the matrix_ (no order maintained in rows) - std::stable_sort(modified_columns.begin(),modified_columns.end(), - [this](index i1, index i2){ - return matrix_.get_pivot(i1) < matrix_.get_pivot(i2); - }); - - //Modifies the pointer curr_col, not the other one. - for(auto other_col_it = std::next(modified_columns.begin()); - other_col_it != modified_columns.end(); ++other_col_it) - { - // index ci = other_col_it->get_column_index(); - // ++other_col_it; //vine swap unvalidates iterator - curr_col = matrix_.vine_swap_with_z_eq_1_case(curr_col, *other_col_it); - } - - //curr_col points to the column to remove by restriction of K to K-{\sigma} - if(!matrix_.get_column(curr_col).is_paired()) { // in F - int dim_zzsh = cpx_.dimension(zzsh); - if(dim_max_ == -1 || (dim_max_ != -1 && dim_zzsh < dim_max_)) { //don't record intervals of max dim - persistence_diagram_.emplace_back( dim_zzsh - , births_.at(curr_col) - , num_arrow_);// -1); - } - } - else { //in H -> paired with c_g, that now belongs to F now - births_.at(matrix_.get_column(curr_col).get_paired_chain_index()) = num_arrow_; - } - - //cannot be in G as the removed simplex is maximal - matrix_.remove_maximal_simplex(cpx_.key(zzsh)); - } - -public: - /** \brief Returns the index persistence diagram as an std::list of intervals.*/ - const std::list< interval_index > & index_persistence_diagram() const - { - return persistence_diagram_; - } - - /** \brief Returns the filtration values \f$[f(b),f(d)]\f$ (generally real-valued) - * associated to the indices \f$[b,d]\f$ (integer valued) of the insertion or - * deletion of a simplex in the zigzag filtration. - * - * \details Used to convert a persistent interval \f$[b,d]\f$, computed by the - * persistent homology algorithm, into its filtration valued version - * \f$[f(b),f(d)]\f$ used in the persistence barcode. The information - * index->filtration is stored in the member filtration_values_ of - * the class Zigzag_persistence. - * - * @param[in] b_key, d_key The indices of birth and death of a persistent - * interval. - * - * @param[out] std::pair A pair of real values \f$(f(b),f(d))\f$. - */ - std::pair index_to_filtration( - Simplex_key b_key, Simplex_key d_key) { - // filtration_values_ must be sorted by increasing keys. - auto it_b = //lower_bound(x) returns leftmost y s.t. x <= y - std::lower_bound( filtration_values_.begin(), filtration_values_.end() - , std::pair(b_key - , std::numeric_limits::infinity() ) - , []( std::pair p1 - , std::pair p2) - { return p1.first < p2.first; } - ); - if(it_b == filtration_values_.end() || it_b->first > b_key) { --it_b; } - //it points to the rightmost z such that z <= x - - auto it_d = // - std::lower_bound( filtration_values_.begin(), filtration_values_.end() - , std::pair(d_key - , std::numeric_limits::infinity() ) - , []( std::pair p1 - , std::pair p2) - { return p1.first < p2.first; } - ); - if(it_d == filtration_values_.end() || it_d->first > d_key) { --it_d; } - - return std::make_pair(it_b->second, it_d->second); - } - - /** \brief Writes the persistence diagram in a file. - * - * \details The filtration values are given by the zigzag persistence iterator, that assigns - * to any insertion or deletion of a simplex a filtration value ; we say that an - * arrow has an index \f$i\f$, and a corresponding filtration value \f$f(i)\f$. - * Reading a zigzag filtration from left to right, indices are strictly - * monotonically increasing, and the associated filtration values are monotonous - * (not necessarily - * strictly, either increasing or decreasing). - * - * Consider two consecutive arrows (insertion or deletion): - * - * \f$$K_1 \leftrightarrow K_2 \leftrightarrow K_3\f$$ - * - * with respectively indices \f$i\f$ (left) and \f$i+1\f$ (right), and associated - * filtration values \f$f(i)\f$ and \f$f(i+1)\f$ respectively. - * - * If, the arrow \f$K_2 \leftrightarrow K_3\f$ leads to the creation of a new - * homology feature in \f$K_3\f$, it creates an (indexed) persistent interval - * \f$[\f$i+1; \cdot\f$, and a corresponding (filtration) persistent interval - * \f$[f(i+1); \cdot]\f$ in the persistence diagram. - * - * If a homology feature in \f$K_2\f$ is destroyed by the arrow \f$K_2 \leftrightarrow K_3\f$, it closes an (indexed) - * interval \f$[\cdot ; i+1]\f$, and a corresponding (filtration) persistent - * interval \f$[\cdot ; f(i+1)]\f$ in the persistence diagram. - * - * For example, in an oscillating Rips zigzag filtration, if, in the following - * chunk of filtration: - * - * \f$R_{\eta \varepsilon_i}(P_i) \rightarrow \cdots \leftarrow R_{\eta \varepsilon_{i+1}}(P_{i+1}),\f$ - * - * if anything is created by any of the arrows above, it leads to an interval - * \f$[\varepsilon_{i+1}; \cdot]\f$. If anything is destroyed by any of the arrows - * above, if leads to an interval \f$[\cdot;\varepsilon_i]\f$. Note that we may - * have \f$\varepsilon_i > \varepsilon_{i+1}\f$. - * - * The bars are ordered by decreasing length. - * - * @param[in] os the output stream in which the diagram is written. - * @param[in] shortest_interval all intervals of lenght smaller or equal to - * this value are ignore. Default is 0. - */ - void persistence_diagram( std::ostream& os - , Filtration_value shortest_interval = 0.) { - - std::stable_sort(filtration_values_.begin(), filtration_values_.end(), - []( std::pair< Simplex_key, Filtration_value > p1 - , std::pair< Simplex_key, Filtration_value > p2 ) - { return p1.first < p2.first; } - ); - - std::vector< interval_filtration > tmp_diag; - tmp_diag.reserve(persistence_diagram_.size()); - for(auto bar : persistence_diagram_) - { - Filtration_value birth,death; - std::tie(birth,death) = index_to_filtration(bar.birth(), bar.death()); - - if( std::abs(birth - death) > shortest_interval ) { - tmp_diag.emplace_back(bar.dim(), birth, death ); - } - } - // cmp_intervals_by_length cmp; - std::stable_sort(tmp_diag.begin(), tmp_diag.end(), cmp_intervals_by_length()); - - os << "# dim birth death [length]\n"; - for(auto bar : tmp_diag) { - if(bar.birth() > bar.death()) { bar.swap_birth_death(); } - os << bar.dim() << " " << bar.birth() << " " << bar.death() << - " - [" << bar.length() << "] \n"; - } - } - - /** \brief Returns the persistence diagram as a vector of real-valued intervals. */ - std::vector< interval_filtration > - persistence_diagram(Filtration_value shortest_interval = 0., bool include_infinit_bars = false) - { - std::stable_sort(filtration_values_.begin(), filtration_values_.end(), - []( std::pair< Simplex_key, Filtration_value > p1 - , std::pair< Simplex_key, Filtration_value > p2 ) - { return p1.first < p2.first; } - ); - - std::vector< interval_filtration > diag; - diag.reserve(persistence_diagram_.size()); - for(auto bar : persistence_diagram_) - { - Filtration_value birth,death; - std::tie(birth,death) = index_to_filtration(bar.birth(), bar.death()); - - if( std::abs(birth - death) > shortest_interval ) { - diag.emplace_back(bar.dim(), birth, death ); - } - } - //put lower value as birth - for(auto &bar : diag) { - if( bar.birth() > bar.death() ) { bar.swap_birth_death(); } - } - std::stable_sort(diag.begin(), diag.end(), cmp_intervals_by_length()); - - auto birth = - [this](Simplex_key b_key) { - auto it_b = // lower_bound(x) returns leftmost y s.t. x <= y - std::lower_bound(filtration_values_.begin(), filtration_values_.end(), - std::pair( - b_key, std::numeric_limits::infinity()), - [](std::pair p1, - std::pair p2) { return p1.first < p2.first; }); - if (it_b == filtration_values_.end() || it_b->first > b_key) { - --it_b; - } - return it_b->second; - }; - - //TODO: better recording? - if (include_infinit_bars) { - for (unsigned int i = 0; i < matrix_.get_number_of_columns(); ++i) { - const auto& col = matrix_.get_column(i); - if (!col.is_paired()) - diag.emplace_back(col.get_dimension(), birth(col.get_pivot()), std::numeric_limits::infinity()); - } - } - - return diag; - } - -private: - Complex cpx_; // complex - int dim_max_;//max dim complex - matrix_type matrix_; // 0 ... m-1 - std::unordered_map births_; - birth_ordering birth_ordering_; - std::list< interval_index > persistence_diagram_; - Simplex_key num_arrow_; //current index - Filtration_value previous_filtration_value_; - // filtration_values stores consecutive pairs (i,f) , (j,f') with f != f', - // meaning that all inserted simplices with key in [i;j-1] have filtration value f - //i is the smallest simplex index whose simplex has filtration value f. - std::vector< std::pair< Simplex_key, Filtration_value > > filtration_values_; -};//end class Zigzag_persistence - -} //namespace zigzag_persistence - -} //namespace Gudhi - -#endif //ZIGZAG_PERSISTENCE_H_ +/** \class Zigzag_persistence Zigzag_persistence.h gudhi/Zigzag_persistence.h + * \brief Class computating the zigzag persistent homology of a zigzag + * filtration. Algorithm based on \cite zigzag_reflection. + * + * \details The type ZigzagFilteredComplex::Simplex_key counts the number of + * insertions and + * deletions of simplices, which may be large in zigzag persistence and require + * more than 32 bits of storage. The type used (int, long, etc) should be chosen in + * consequence. Simplex_key must be signed. + * + * Over all insertions, the Simplex_key must be positive and strictly increasing + * when forward iterating along the zigzag filtration. + * + * \tparam ZigzagFilteredComplex Complex storing the current simplices. + * \tparam ZigzagPersistenceOptions Options for the matrix used to compute the persistence. + */ +template > +class Zigzag_persistence +{ + public: + using Complex = ZigzagFilteredComplex; /**< Complex type. */ + using Options = ZigzagPersistenceOptions; /**< Matrix options */ + /*** Types defined in the complex ***/ + using Simplex_key = typename Complex::Simplex_key; /**< Key type, must be signed. */ + using Simplex_handle = typename Complex::Simplex_handle; /**< Simplex ID type in the complex. */ + using Vertex_handle = typename Complex::Vertex_handle; /**< Vertex ID type in the complex. */ + using Filtration_value = typename Complex::Filtration_value; /**< Filtration value type. */ + + /** \brief Structure to store persistence intervals by their index values. + * + * \details By convention, interval [b;d] are + * closed for finite indices b and d, and open for left-infinite and/or + * right-infinite endpoints. + */ + template + struct interval { + interval() {} + interval(int dim, value_type b, value_type d) : dim_(dim), b_(b), d_(d) {} + /** Returns the dimension of the homological feature corresponding to the + * interval. */ + int dim() const { return dim_; } // return the homological dimension of the interval + /** Returns the birth index of the interval.*/ + value_type birth() const { return b_; } // return the birth value + /** Returns the death index of the interval.*/ + value_type death() const { return d_; } // return the death value + + protected: // note that we don't assume b_ <= d_ + int dim_; // homological dimension + value_type b_; // filtration value associated to birth index + value_type d_; // filtration value associated to death index + }; + using index_interval = interval; + + /** \brief Structure to store persistence intervals by their filtration values. + * + * \details By convention, interval \f$[b;d]\f$ are + * closed for finite indices b and d, and open for left-infinite and/or + * right-infinite endpoints. + */ + struct filtration_value_interval : interval + { + private: + using Base = interval; + + public: + /** + * @brief Default constructor + */ + filtration_value_interval() : Base() {} + /** + * @brief Construct a new interval with given parameters + * + * @param dim Dimension of the interval. + * @param b Start value of the interval. + * @param d End value of the interval. + */ + filtration_value_interval(int dim, Filtration_value b, Filtration_value d) + : Base(dim, b, d) {} + + /** + * @brief Returns the absolute length of the interval \f$|d-b|\f$. + */ + Filtration_value length() const { + if (Base::b_ == Base::d_) { + return 0; + } // otherwise inf - inf would return nan. + return std::abs(Base::b_ - Base::d_); + } + /** + * @brief Returns the absolute length of the log values of birth and death, i.e. \f$|\log d - \log b|\f$. + */ + Filtration_value log_length() const { + if (Base::b_ == Base::d_) { + return 0; + } // otherwise inf - inf would return nan. + return std::abs(std::log2(static_cast(Base::b_)) - std::log2(static_cast(Base::d_))); + } + }; + + private: + /** \brief Maintains the birth ordering \f$\leq_b\f$. + * + * \details Contains an std::map of size the number of + * non-zero rows of the homology matrix, at any time during the computation of + * zigzag persistence. + * + * By construction, we maintain the map satisfying + * 'birth_to_pos_[i] < birth_to_pos_[j]', with \f$0 <= i,j <= k\f$ indices in the quiver + * '\f$0 \leftrightarrow ... \leftrightarrow i \leftrightarrow ... \leftrightarrow k\f$' visited at time + * \f$k\f$ of the algorithm (prefix of length \f$k\f$ of the full zigzag filtration + * '\f$0 \leftrightarrow ... \leftrightarrow i \leftrightarrow ... + * \leftrightarrow k \leftrightarrow ... \leftrightarrow n\f$' + * that is studied), iff \f$i <_b j\f$ for the birth ordering. + * + * By construction, when adding index \f$k+1\f$ to '\f$0 \leftrightarrow ... \leftrightarrow i \leftrightarrow .. + * \leftrightarrow k \leftrightarrow k+1'\f$, we have: + * - if \f$k \rightarrow k+1\f$ forward, then \f$j <_b k+1\f$ for all indices \f$j < k+1\f$, otherwise + * - if \f$k \leftarrow k+1\f$ backward, then \f$k+1 <_b j\f$ for all indices \f$j < k+1\f$. + */ + struct birth_ordering { + /** + * @brief Default constructor + */ + birth_ordering() : birth_to_pos_(), max_birth_pos_(0), min_birth_pos_(-1) {} + + /** + * @brief Inserts arrow number in the ordering after an insertion. + * When the arrow key-1 -> key is forward, key is larger than any other index + * i < key in the birth ordering b k2. + * + * @param k1 + * @param k2 + * @return true if k1 >b k2, false otherwise. + */ + bool reverse_birth_order(Simplex_key k1, Simplex_key k2) const { return birth_to_pos_.at(k1) > birth_to_pos_.at(k2); } + + private: + std::unordered_map birth_to_pos_; /**< birth_to_pos_[i] < birth_to_pos_[j] iff i ; + using index = Gudhi::persistence_matrix::index; + + public: + /** + * @brief Constructor of the Zigzag_persistence class. + * @details After construction of the class, the zigzag filtration should be given in a streaming like way, i.e., + * call @ref insert_simplex or @ref remove_simplex for each step of the filtration in order of the filtration. + * If simplices are added (resp. removed) continuously, they can be inserted (resp. removed) in batches by using + * @ref insert_simplices_contiguously (resp. @ref remove_simplices_contiguously). + * To retrieve the current persistence diagram at any moment of the filtration, + * use @ref get_persistence_diagram or @ref get_index_persistence_diagram. + * + * @param min_number_of_simplices Minimum number of simplices that will be inserted at some point in the filtration. + * If the total number of simplices is known in advance, the memory allocation can be better optimized. + * Default value: 0. + * @param ignore_cycles_above_dim Ignores cycles in dimension larger or equal in the final diagram. + * If -1, no cycles are ignored. Default value: -1. + */ + Zigzag_persistence(unsigned int min_number_of_simplices = 0, int ignore_cycles_above_dim = -1) + : dim_max_(ignore_cycles_above_dim), + matrix_(min_number_of_simplices, + [this](index columnIndex1, index columnIndex2) { + return birth_ordering_.birth_order(births_.at(columnIndex1), births_.at(columnIndex2)); + }), + num_arrow_(-1), + previous_filtration_value_(std::numeric_limits::infinity()) {} + + /** + * @brief Updates the zigzag persistence diagram after the insertion of the given simplex. + * + * @tparam VertexRange Range type needing begin and end members. + * @param simplex Simplex to insert, represented by its vertices. + * @param filtration_value Filtration value associated to the simplex. + * Assumed to be larger or equal to previously used filtration values. + */ + template > + void insert_simplex(const VertexRange& simplex, Filtration_value filtration_value) { + if (dim_max_ != -1 && simplex.size() > static_cast(dim_max_) + 1) return; + + ++num_arrow_; + + if (filtration_value != previous_filtration_value_) // check whether the filt value has changed + { // consecutive pairs (i,f), (j,f') mean simplices of index k in [i,j-1] have + previous_filtration_value_ = filtration_value; // filtration value f + filtration_values_.emplace_back(num_arrow_, previous_filtration_value_); + } + + std::pair res = cpx_.insert_simplex(simplex, filtration_value); + GUDHI_CHECK(res.second, "Zigzag_persistence::insert_simplex - insertion of a simplex already in the complex"); + cpx_.assign_key(res.first, num_arrow_); + _process_forward_arrow(res.first); + } + + /** + * @brief Updates the zigzag persistence diagram after the removal of the given simplex. + * + * @tparam VertexRange Range type needing begin and end members. + * @param simplex Simplex to remove, represented by its vertices. + * @param filtration_value Filtration value associated to the removal. + * Assumed to be larger or equal to previously used filtration values. + */ + template > + void remove_simplex(const VertexRange& simplex, Filtration_value filtration_value) { + if (dim_max_ != -1 && simplex.size() > static_cast(dim_max_) + 1) return; + + ++num_arrow_; + + Simplex_handle sh = cpx_.find(simplex); + GUDHI_CHECK(sh != cpx_.null_simplex(), + "Zigzag_persistence::remove_simplex - removal of a simplex not in the complex"); + + if (filtration_value != previous_filtration_value_) // check whether the filt value has changed + { // consecutive pairs (i,f), (j,f') mean simplices of index k in [i,j-1] have + previous_filtration_value_ = filtration_value; // filtration value f + filtration_values_.emplace_back(num_arrow_, previous_filtration_value_); + } + + _process_backward_arrow(sh); + cpx_.remove_maximal_simplex(sh); + } + + /** + * @brief Updates the zigzag persistence diagram after the insertion of the given simplices. + * + * @tparam SimplexRange Range type needing begin and end members. + * @tparam FiltrationRange Range type needing begin and end members. + * @param simplices Simplices which are inserted, represented by their vertices. They have to be in the order they + * are inserted in the filtration and ``contiguous'' in the filtration, that is, no other simplex + * which is not in the range is inserted or removed between two simplices in the range. + * @param filtration_values Filtration values associated to the insertion of the given simplices. + * The order has to correspond to the order in @a simplices. Their values have to ascending in this order and + * they are assumed to be larger or equal to previously used filtration values. + */ + template >, + class FiltrationRange = std::initializer_list> + void insert_simplices_contiguously(const SimplexRange& simplices, const FiltrationRange& filtration_values) { + auto simplexIt = simplices.begin(); + auto filIt = filtration_values.begin(); + for (; simplexIt != simplices.end(); ++simplexIt, ++filIt) { + insert_simplex(*simplexIt, *filIt); + } + } + + /** + * @brief Updates the zigzag persistence diagram after the removal of the given simplices. + * + * @tparam SimplexRange Range type needing begin and end members. + * @tparam FiltrationRange Range type needing begin and end members. + * @param simplices Simplices which are removed, represented by their vertices. They have to be in the order they + * are removed in the filtration and ``contiguous'' in the filtration, that is, no other simplex + * which is not in the range is inserted or removed between two simplices in the range. + * @param filtration_values Filtration values associated to the removal of the given simplices. Has therefore the + * same size as @a simplices. The order has to correspond to the order in @a simplices. Their values have to + * ascending in this order and they are assumed to be larger or equal to previously used filtration values. + */ + template >, + class FiltrationRange = std::initializer_list> + void remove_simplices_contiguously(const SimplexRange& simplices, const FiltrationRange& filtration_values) { + auto simplexIt = simplices.begin(); + auto filIt = filtration_values.begin(); + for (; simplexIt != simplices.end(); ++simplexIt, ++filIt) { + remove_simplex(*simplexIt, *filIt); + } + } + + /** + * @brief Updates the zigzag persistence diagram after the insertion of the given simplices. + * + * @tparam SimplexRangeIterators Forward iterator of a range. + * @tparam FiltrationRangeIterators Forward iterator of a range. + * @param simplex_range_start Iterator pointing to the begining of the range of simplices to insert. + * The simplices should be represented by their vertices. They have to be in the order they + * are inserted in the filtration and ``contiguous'' in the filtration, that is, no other simplex + * which is not in the range is inserted or removed between two simplices in the range. + * @param simplex_range_end Iterator pointing to the end of the range of simplices to insert. + * @param filtration_range_start Iterator pointing to the begining of the range of filtration values. The range is + * assumed to end at the same time than the simplices range and has the same order. The filtration values should be + * ascending in this order and they are assumed to be larger or equal to previously used filtration values. + */ + template + void insert_simplices_contiguously(SimplexRangeIterators simplex_range_start, SimplexRangeIterators simplex_range_end, + FiltrationRangeIterators filtration_range_start) { + for (; simplex_range_start != simplex_range_end; ++simplex_range_start, ++filtration_range_start) { + insert_simplex(*simplex_range_start, *filtration_range_start); + } + } + + /** + * @brief Updates the zigzag persistence diagram after the removal of the given simplices. + * + * @tparam SimplexRangeIterators Forward iterator of a range. + * @tparam FiltrationRangeIterators Forward iterator of a range. + * @param simplex_range_start Iterator pointing to the begining of the range of simplices to remove. + * The simplices should be represented by their vertices. They have to be in the order they + * are removed in the filtration and ``contiguous'' in the filtration, that is, no other simplex + * which is not in the range is inserted or removed between two simplices in the range. + * @param simplex_range_end Iterator pointing to the end of the range of simplices to remove. + * @param filtration_range_start Iterator pointing to the begining of the range of filtration values. The range is + * assumed to end at the same time than the simplices range and has the same order. The filtration values should be + * ascending in this order and they are assumed to be larger or equal to previously used filtration values. + */ + template + void remove_simplices_contiguously(SimplexRangeIterators simplex_range_start, SimplexRangeIterators simplex_range_end, + FiltrationRangeIterators filtration_range_start) { + for (; simplex_range_start != simplex_range_end; ++simplex_range_start, ++filtration_range_start) { + remove_simplex(*simplex_range_start, *filtration_range_start); + } + } + + /** + * @brief Returns the ``index persistence diagram'' of the current filtration, that is, the pairs of atomic arrow + * numbers corresponding to a birth-death pair. Does not contain points at infinity, only the cycle classes which + * already died are represented. + * + * @return Reference to the list of intervals. + */ + const std::list& get_index_persistence_diagram() const { return persistence_diagram_; } + + /** + * @brief Returns the filtration values \f$[f(b),f(d)]\f$ associated to the indices \f$[b,d]\f$ which are retrieved + * by @ref get_index_persistence_diagram. + * + * @param b_key Birth index + * @param d_key Death index + * @return A pair of filtration values associated to the given indices. + */ + std::pair map_index_to_filtration_value( + Simplex_key b_key, Simplex_key d_key) const + { + // filtration_values_ must be sorted by increasing keys. + auto it_b = // lower_bound(x) returns leftmost y s.t. x <= y + std::lower_bound( + filtration_values_.begin(), filtration_values_.end(), + std::pair(b_key, std::numeric_limits::infinity()), + [](std::pair p1, std::pair p2) { + return p1.first < p2.first; + }); + if (it_b == filtration_values_.end() || it_b->first > b_key) { + --it_b; + } + // it points to the rightmost z such that z <= x + + auto it_d = // + std::lower_bound( + filtration_values_.begin(), filtration_values_.end(), + std::pair(d_key, std::numeric_limits::infinity()), + [](std::pair p1, std::pair p2) { + return p1.first < p2.first; + }); + if (it_d == filtration_values_.end() || it_d->first > d_key) { + --it_d; + } + + return std::make_pair(it_b->second, it_d->second); + } + + /** + * @brief Returns the current persistence diagram ordered first by length, than by dimension, + * than by birth value and finally by death value. + * + * @param shortest_interval Threshold. Every bar shorter than the given value will be ignored. Default value: 0. + * @param include_infinit_bars If set to true, infinit bars are included in the diagram. Default value: false. + * @return A vector of pairs of filtration values representing the persistence diagram. + */ + std::vector get_persistence_diagram(Filtration_value shortest_interval = 0., + bool include_infinit_bars = false) { + auto comp = [](filtration_value_interval p, filtration_value_interval q) { + if (p.length() != q.length()) { + return p.length() > q.length(); + } // longest 1st + if (p.dim() != q.dim()) { + return p.dim() < q.dim(); + } // lower dimension first + if (p.birth() != q.birth()) { + return p.birth() < q.birth(); + } // lex order + return p.death() < q.death(); + }; + + std::vector diag = _get_persistence_diagram(shortest_interval); + + if (include_infinit_bars) { + _retrieve_infinit_bars(diag); + } + + std::stable_sort(diag.begin(), diag.end(), comp); + + return diag; + } + + /** + * @brief Returns a reference to the complex storing the simplices. + * A simplex is added in a call of @ref insert_simplex and is removed in a call of @ref remove_simplex. + * + * @return Const reference to the complex. + */ +// const ZigzagFilteredComplex& get_complex() const{ +// return cpx_; +// } + + /** + * @brief Returns a reference to the complex storing the simplices. + * A simplex is added in a call of @ref insert_simplex and is removed in a call of @ref remove_simplex. + * @warning The complex is not const for now for technical reasons, but DO NOT modify it. + * + * @return Reference to the complex. + */ + ZigzagFilteredComplex& get_complex() const{ + return cpx_; + } + + /** + * @brief For debug purposes, to remove. + */ + void print_current_complex() const { + for (auto& sh : cpx_.complex_simplex_range()) { + for (auto v : cpx_.simplex_vertex_range(sh)) { + std::cout << v << " "; + } + std::cout << " - " << cpx_.filtration(sh) << "\n"; + } + } + + private: + /** + * @brief Computes the boundary cycle of the new simplex zzsh, and express it as a + * sum of cycles in a matrix. If some cycles are not boundary cycles, i.e., columns with F-index + * in the matrix, it applies a surjective diamond to the zigzag module. + * + * @param zzsh Simplex handle of the inserted simplex. + */ + void _process_forward_arrow(Simplex_handle zzsh) { // maintain the <=b order + // Reduce the boundary of zzsh in the basis of cycles. + // Compute the simplex keys of the simplices of the boundary of zzsh. + std::set col_bsh; // set maintains the natural order on indices + for (auto b_sh : cpx_.boundary_simplex_range(zzsh)) { + col_bsh.insert(cpx_.key(b_sh)); + } + + std::vector chains_in_F = matrix_.insert_boundary(num_arrow_, col_bsh); + + if (!chains_in_F.empty()) { + _apply_surjective_reflection_diamond(zzsh, chains_in_F); + } else { + birth_ordering_.add_birth_forward(num_arrow_); + births_.emplace_hint(births_.end(), matrix_.get_column_with_pivot(num_arrow_), num_arrow_); + } + } + + /** + * @brief Applies the surjective reflection diamond principle to the current filtration. + * + * @details The vector chains_in_F is sorted by decreasing lowest index values in the + * columns corresponding to the chains, due to its computation in the reduction of + * \partial zzsh in _process_forward_arrow(...). It is equivalent to decreasing death index + * order w.r.t. the & chains_in_F) { + // fp is the largest death index for <=d + // Set col_fp: col_fp <- col_f1+...+col_fp (now in G); preserves lowest idx + auto chain_fp = chains_in_F[0]; // col_fp, with largest death bool { + return birth_ordering_.reverse_birth_order(k1, k2); + }; // true iff b(k1) >b b(k2) + + // available_birth: for all i by >d value of the d_i, + // contains at step i all b_j, j > i, and maybe b_i if not stolen + std::set available_birth(cmp_birth); + // for f1 to f_{p} (i by <=d), insertion in available_birth_to_fidx sorts by >=b + for (auto& chain_f : chains_in_F) { + available_birth.insert(births_.at(chain_f)); + } + + auto maxb_it = available_birth.begin(); // max birth cycle + auto maxb = *maxb_it; // max birth value, for persistence diagram + available_birth.erase(maxb_it); // remove max birth cycle (stolen) + + auto last_modified_chain_it = chains_in_F.rbegin(); + + // consider all death indices by increasing the maximal availabe death. + // Let c_1 ... c_f be the chains s.t. <[c_1+...+c_f]> is the kernel and + // death(c_i) >d death(c_i-1). If the birth of c_i is not available, we set + // c_i <- c_i + c_i-1 + ... + c_1, which is [c_i + c_i-1 + ... + c_1] on + // the right (of death the maximali <=> the maxj>k, are indeed c_j + // set c_i <- c_i + (c_i-1) + ... + (c_k+1) + (c_k + ... + c_1) + for (auto chain_passed_it = last_modified_chain_it; // all with smaller modified_columns; + const auto& row = matrix_.get_row(simplexIndex); + modified_columns.reserve(row.size()); + std::transform(row.begin(), row.end(), std::back_inserter(modified_columns), + [](const auto& cell) { return cell.get_column_index(); }); + // Sort by left-to-right order in the matrix_ (no order maintained in rows) + std::stable_sort(modified_columns.begin(), modified_columns.end(), + [this](index i1, index i2) { return matrix_.get_pivot(i1) < matrix_.get_pivot(i2); }); + + // Modifies curr_col, not the other one. + for (auto other_col_it = std::next(modified_columns.begin()); other_col_it != modified_columns.end(); + ++other_col_it) { + curr_col = matrix_.vine_swap_with_z_eq_1_case(curr_col, *other_col_it); + } + + // curr_col points to the column to remove by restriction of K to K-{\sigma} + if (!matrix_.get_column(curr_col).is_paired()) { // in F + int dim_zzsh = cpx_.dimension(zzsh); + auto it = births_.find(curr_col); + if (dim_max_ == -1 || (dim_max_ != -1 && dim_zzsh < dim_max_)) { // don't record intervals of max dim + persistence_diagram_.emplace_back(dim_zzsh, it->second, num_arrow_); // -1); + } + //Following value can be erased, but it slowes the process down a bit, so I keep it as a remainder for now: + // birth_ordering_.remove_birth(it->second); + births_.erase(it); + } else { // in H -> paired with c_g, that now belongs to F now + // maintain the <=b order + birth_ordering_.add_birth_backward(num_arrow_); + births_.try_emplace(matrix_.get_column(curr_col).get_paired_chain_index(), num_arrow_); + } + + // cannot be in G as the removed simplex is maximal + matrix_.remove_maximal_simplex(simplexIndex); + } + + /** + * @brief Returns the current persistence diagram ordered by length without infinit bars. + * + * @param shortest_interval Intervals shorter than the given value are ignored. + * @return Vector of intervals. + */ + std::vector _get_persistence_diagram(Filtration_value shortest_interval) { + std::vector diag; + diag.reserve(persistence_diagram_.size()); + + std::stable_sort(filtration_values_.begin(), filtration_values_.end(), + [](std::pair p1, std::pair p2) { + return p1.first < p2.first; + }); + + for (auto bar : persistence_diagram_) { + Filtration_value birth, death; + std::tie(birth, death) = map_index_to_filtration_value(bar.birth(), bar.death()); + if (birth > death) { + std::swap(birth, death); + } + + if (death - birth > shortest_interval) { + diag.emplace_back(bar.dim(), birth, death); + } + } + + return diag; + } + + /** + * @brief Computes the births of the current essential cycles. + * + * @param diag Reference to vector where to store the infinit bars. + */ + void _retrieve_infinit_bars(std::vector& diag) const { + auto birth = [this](Simplex_key b_key) { + auto it_b = // lower_bound(x) returns leftmost y s.t. x <= y + std::lower_bound( + filtration_values_.begin(), filtration_values_.end(), + std::pair(b_key, std::numeric_limits::infinity()), + [](std::pair p1, std::pair p2) { + return p1.first < p2.first; + }); + if (it_b == filtration_values_.end() || it_b->first > b_key) { + --it_b; + } + return it_b->second; + }; + + for (auto& p : births_) { + auto dim = matrix_.get_column(matrix_.get_column_with_pivot(p.first)).get_dimension(); + if (dim_max_ == -1 || (dim_max_ != -1 && dim < dim_max_)) + diag.emplace_back(dim, birth(p.second), std::numeric_limits::infinity()); + } + } + + private: + Complex cpx_; /**< Complex in which the current simplices are stored. */ + int dim_max_; /**< Maximal dimension of a bar to record. */ + matrix_type matrix_; /**< Matrix storing a base of the current chain complex. */ + std::unordered_map births_; /**< Map simplex index in F to corresponding birth. */ + birth_ordering birth_ordering_; /**< Maintains persistence_diagram_; /**< Stores current closed persistence intervals. */ + Simplex_key num_arrow_; /**< Current arrow number. */ + Filtration_value previous_filtration_value_; /**< Filtration value of the previous arrow. */ + /** + * @brief filtration_values_ stores consecutive pairs (i,f) , (j,f') with f != f', + * meaning that all inserted simplices with key in [i;j-1] have filtration value f + * i is the smallest simplex index whose simplex has filtration value f. + */ + std::vector> filtration_values_; +}; // end class Zigzag_persistence + +} // namespace zigzag_persistence + +} // namespace Gudhi + +#endif // ZIGZAG_PERSISTENCE_H_ diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence_old.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence_old.h index c364fc10c2..aa93d75a21 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence_old.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence_old.h @@ -300,9 +300,9 @@ class Zigzag_persistence { * \details By convention, interval \f$[b;d]\f$ are * closed for finite indices b and d, and open for left-infinite and/or * right-infinite endpoints.*/ - struct interval_filtration { - interval_filtration() {} - interval_filtration(int dim, Filtration_value b, Filtration_value d) : dim_(dim), b_(b), d_(d) {} + struct fil_interval { + fil_interval() {} + fil_interval(int dim, Filtration_value b, Filtration_value d) : dim_(dim), b_(b), d_(d) {} /** Returns the absolute length of the interval \f$|d-b|\f$. */ Filtration_value length() { if(b_ == d_) { return 0; } //otherwise inf - inf would return nan. @@ -359,7 +359,7 @@ class Zigzag_persistence { */ struct cmp_intervals_by_log_length { cmp_intervals_by_log_length(){} - bool operator()( interval_filtration p, interval_filtration q) + bool operator()( fil_interval p, fil_interval q) { if(p.dim() != q.dim()) {return p.dim() < q.dim();}//lower dimension first if(p.log_length() != q.log_length()) {return p.log_length() > q.log_length();} @@ -373,7 +373,7 @@ class Zigzag_persistence { */ struct cmp_intervals_by_length { cmp_intervals_by_length(){} - bool operator()( interval_filtration p, interval_filtration q) + bool operator()( fil_interval p, fil_interval q) { if(p.length() != q.length()) { return p.length() > q.length(); }//longest 1st if(p.dim() != q.dim()) {return p.dim() < q.dim();}//lower dimension first @@ -617,7 +617,7 @@ class Zigzag_persistence { std::pair res = cpx_.insert_simplex(simplex, filtration_value); GUDHI_CHECK(res.second, "Zigzag_persistence::insert_simplex - insertion of a simplex already in the complex"); cpx_.assign_key(res.first, num_arrow_); - forward_arrow(res.first); + _process_forward_arrow(res.first); } template> @@ -636,7 +636,7 @@ class Zigzag_persistence { filtration_values_.emplace_back(num_arrow_, previous_filtration_value_); } - backward_arrow(sh); + _process_backward_arrow(sh); cpx_.remove_maximal_simplex(sh); } @@ -699,7 +699,7 @@ class Zigzag_persistence { * i.e., columns with (F \cup G)-indices, and then apply a surjective diamond to * the zigzag module. */ - void forward_arrow( Simplex_handle zzsh ) + void _process_forward_arrow( Simplex_handle zzsh ) { //maintain the <=b order birth_ordering_.add_birth_forward(num_arrow_); @@ -899,7 +899,7 @@ class Zigzag_persistence { } //cpx_.key(zzsh) is the key of the simplex we remove, not a new one - void backward_arrow( Simplex_handle zzsh ) + void _process_backward_arrow( Simplex_handle zzsh ) { //maintain the <=b order birth_ordering_.add_birth_backward(num_arrow_); @@ -1117,7 +1117,7 @@ class Zigzag_persistence { public: /** \brief Returns the index persistence diagram as an std::list of intervals.*/ - const std::list< interval_index > & index_persistence_diagram() const + const std::list< interval_index > & get_index_persistence_diagram() const { return persistence_diagram_; } @@ -1137,7 +1137,7 @@ class Zigzag_persistence { * * @param[out] std::pair A pair of real values \f$(f(b),f(d))\f$. */ - std::pair index_to_filtration( + std::pair map_index_to_filtration_value( Simplex_key b_key, Simplex_key d_key) { // filtration_values_ must be sorted by increasing keys. auto it_b = //lower_bound(x) returns leftmost y s.t. x <= y @@ -1215,12 +1215,12 @@ class Zigzag_persistence { { return p1.first < p2.first; } ); - std::vector< interval_filtration > tmp_diag; + std::vector< fil_interval > tmp_diag; tmp_diag.reserve(persistence_diagram_.size()); for(auto bar : persistence_diagram_) { Filtration_value birth,death; - std::tie(birth,death) = index_to_filtration(bar.birth(), bar.death()); + std::tie(birth,death) = map_index_to_filtration_value(bar.birth(), bar.death()); if( std::abs(birth - death) > shortest_interval ) { tmp_diag.emplace_back(bar.dim(), birth, death ); @@ -1238,8 +1238,8 @@ class Zigzag_persistence { } /** \brief Returns the persistence diagram as a vector of real-valued intervals. */ - std::vector< interval_filtration > - persistence_diagram(Filtration_value shortest_interval = 0., bool include_infinit_bars = false) + std::vector< fil_interval > + get_persistence_diagram(Filtration_value shortest_interval = 0., bool include_infinit_bars = false) { std::stable_sort(filtration_values_.begin(), filtration_values_.end(), []( std::pair< Simplex_key, Filtration_value > p1 @@ -1247,12 +1247,12 @@ class Zigzag_persistence { { return p1.first < p2.first; } ); - std::vector< interval_filtration > diag; + std::vector< fil_interval > diag; diag.reserve(persistence_diagram_.size()); for(auto bar : persistence_diagram_) { Filtration_value birth,death; - std::tie(birth,death) = index_to_filtration(bar.birth(), bar.death()); + std::tie(birth,death) = map_index_to_filtration_value(bar.birth(), bar.death()); if( std::abs(birth - death) > shortest_interval ) { diag.emplace_back(bar.dim(), birth, death ); diff --git a/src/Zigzag_persistence/test/CMakeLists.txt b/src/Zigzag_persistence/test/CMakeLists.txt index 74dac45186..c5be1da0a9 100644 --- a/src/Zigzag_persistence/test/CMakeLists.txt +++ b/src/Zigzag_persistence/test/CMakeLists.txt @@ -7,10 +7,5 @@ if(TARGET TBB::tbb) target_link_libraries(Zigzag_persistence_unit_test TBB::tbb) endif() -# Do not forget to copy test results files in current binary dir -#file(COPY "${CMAKE_SOURCE_DIR}/src/Persistent_cohomology/test/simplex_tree_file_for_unit_test.txt" -# DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) - -# Unitary tests gudhi_add_boost_test(Zigzag_persistence_unit_test) diff --git a/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp b/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp index 49bb97b5a9..050bcd93b7 100644 --- a/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp +++ b/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp @@ -1,7 +1,18 @@ +/* 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): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + #include #include #include #include +#include #define BOOST_TEST_DYN_LINK #define BOOST_TEST_MODULE "zigzag_persistence" @@ -16,453 +27,460 @@ using ST = Gudhi::Simplex_tree; using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; using Vertex_handle = ST::Vertex_handle; using Filtration_value = ST::Filtration_value; -using interval_index = ZP::interval_index; -using interval_filtration = ZP::interval_filtration; - -// TODO: -// void persistence_diagram(std::ostream& os, Filtration_value shortest_interval = 0.); +using interval_index = ZP::index_interval; +using interval_filtration = ZP::filtration_value_interval; struct cmp_intervals_by_length { - cmp_intervals_by_length() {} - bool operator()(interval_filtration p, interval_filtration q) { - if (p.length() != q.length()) { - return p.length() > q.length(); - } - if (p.dim() != q.dim()) { - return p.dim() < q.dim(); - } - if (p.birth() != q.birth()) { - return p.birth() < q.birth(); - } - return p.death() < q.death(); - } + cmp_intervals_by_length() {} + bool operator()(interval_filtration p, interval_filtration q) { + if (p.length() != q.length()) { + return p.length() > q.length(); + } + if (p.dim() != q.dim()) { + return p.dim() < q.dim(); + } + if (p.birth() != q.birth()) { + return p.birth() < q.birth(); + } + return p.death() < q.death(); + } }; BOOST_AUTO_TEST_CASE(constructor) { - BOOST_CHECK_NO_THROW(ZP zp); - BOOST_CHECK_NO_THROW(ZP zp(28)); - BOOST_CHECK_NO_THROW(ZP zp(28,2)); - ZP zp; - BOOST_CHECK(zp.persistence_diagram(0).empty()); + BOOST_CHECK_NO_THROW(ZP zp); + BOOST_CHECK_NO_THROW(ZP zp(28)); + BOOST_CHECK_NO_THROW(ZP zp(28, 2)); + ZP zp; + BOOST_CHECK(zp.get_persistence_diagram(0).empty()); } -void test_barcode(ZP& zp, std::vector& barcode) -{ - std::stable_sort(barcode.begin(), barcode.end(), cmp_intervals_by_length()); - auto it = barcode.begin(); - for (const auto& interval : zp.persistence_diagram()){ - BOOST_CHECK_EQUAL(interval.dim(), it->dim()); - BOOST_CHECK_EQUAL(interval.birth(), it->birth()); - BOOST_CHECK_EQUAL(interval.death(), it->death()); - ++it; - } - BOOST_CHECK(it == barcode.end()); +void test_barcode(ZP& zp, std::vector& barcode) { + std::stable_sort(barcode.begin(), barcode.end(), cmp_intervals_by_length()); + auto it = barcode.begin(); + for (const auto& interval : zp.get_persistence_diagram(0, true)) { + BOOST_CHECK_EQUAL(interval.dim(), it->dim()); + BOOST_CHECK_EQUAL(interval.birth(), it->birth()); + BOOST_CHECK_EQUAL(interval.death(), it->death()); + ++it; + } + BOOST_CHECK(it == barcode.end()); } -void test_indices(ZP& zp, std::vector& indices, std::vector& indexToFil) -{ - auto it = indices.begin(); - for (const auto& interval : zp.index_persistence_diagram()){ - BOOST_CHECK_EQUAL(interval.dim(), it->dim()); - BOOST_CHECK_EQUAL(interval.birth(), it->birth()); - BOOST_CHECK_EQUAL(interval.death(), it->death()); - auto p = zp.index_to_filtration(interval.birth(), interval.death()); - BOOST_CHECK_EQUAL(p.first, indexToFil[interval.birth()]); - BOOST_CHECK_EQUAL(p.second, indexToFil[interval.death()]); - ++it; - } - BOOST_CHECK(it == indices.end()); +void test_indices(ZP& zp, std::vector& indices, std::vector& indexToFil) { + auto it = indices.begin(); + for (const auto& interval : zp.get_index_persistence_diagram()) { + BOOST_CHECK_EQUAL(interval.dim(), it->dim()); + BOOST_CHECK_EQUAL(interval.birth(), it->birth()); + BOOST_CHECK_EQUAL(interval.death(), it->death()); + auto p = zp.map_index_to_filtration_value(interval.birth(), interval.death()); + BOOST_CHECK_EQUAL(p.first, indexToFil[interval.birth()]); + BOOST_CHECK_EQUAL(p.second, indexToFil[interval.death()]); + ++it; + } + BOOST_CHECK(it == indices.end()); } -std::vector > get_simplices() -{ - return { - {0}, - {1}, - {2}, - {0,1}, - {0,2}, - {3}, - {1,2}, - {4}, - {3,4}, - {5}, - {0,1,2}, - {4,5}, - {3,5}, - {3,4,5}, - {0,1,2}, //r - {3,4,5}, //r - {1,4}, - {0,1,2}, - {2,4}, - {3,4,5}, - {0,4}, - {0,2,4}, - {1,2,4}, - {0,1,4}, - {3,4,5}, //r - {3,4}, //r - {3,5}, //r - {0,1,2,4}}; +std::vector > get_simplices() { + return {{0}, + {1}, + {2}, + {0, 1}, + {0, 2}, + {3}, + {1, 2}, + {4}, + {3, 4}, + {5}, + {0, 1, 2}, + {4, 5}, + {3, 5}, + {3, 4, 5}, + {0, 1, 2}, // remove + {3, 4, 5}, // remove + {1, 4}, + {0, 1, 2}, + {2, 4}, + {3, 4, 5}, + {0, 4}, + {0, 2, 4}, + {1, 2, 4}, + {0, 1, 4}, + {3, 4, 5}, // remove + {3, 4}, // remove + {3, 5}, // remove + {0, 1, 2, 4}, + {0, 1, 2, 4}}; // remove } -std::vector get_filtration_values() -{ - return { - 0, 0, 0, - 1, 1, 1, - 2, 2, 2, - 3, 3, 3, 3, - 4, - 5, - 6, 6, 6, - 7, 7, 7, 7, 7, 7, - 8, - 9, 9, 9 - }; +std::vector get_filtration_values() { + return {0, 0, 0, + 1, 1, 1, + 2, 2, 2, + 3, 3, 3, 3, + 4, + 5, + 6, 6, 6, + 7, 7, 7, 7, 7, 7, + 8, + 9, 9, 9, + 10}; } BOOST_AUTO_TEST_CASE(zigzag_persistence_single) { - ZP zp(28); - std::vector realIndices; - std::vector realBarcode; - realIndices.reserve(13); - realBarcode.reserve(9); - - std::vector > simplices = get_simplices(); - std::vector filValues = get_filtration_values(); - - for (unsigned int i = 0; i < 14; ++i){ - zp.insert_simplex(simplices[i], filValues[i]); - } - - realIndices.emplace_back(0, 1, 3); - realIndices.emplace_back(0, 2, 4); - realIndices.emplace_back(0, 7, 8); - realIndices.emplace_back(1, 6, 10); - realIndices.emplace_back(0, 9, 11); - realIndices.emplace_back(1, 12, 13); - - realBarcode.emplace_back(0, 0, 1); - realBarcode.emplace_back(0, 0, 1); - realBarcode.emplace_back(1, 2, 3); - realBarcode.emplace_back(1, 3, 4); - - for (unsigned int i = 14; i < 16; ++i){ - zp.remove_simplex(simplices[i], filValues[i]); - } - - for (unsigned int i = 16; i < 24; ++i){ - zp.insert_simplex(simplices[i], filValues[i]); - } - - realIndices.emplace_back(0, 5, 16); - realIndices.emplace_back(1, 14, 17); - realIndices.emplace_back(1, 15, 19); - realIndices.emplace_back(1, 20, 21); - realIndices.emplace_back(1, 18, 22); - - realBarcode.emplace_back(0, 1, 6); - realBarcode.emplace_back(1, 5, 6); - realBarcode.emplace_back(1, 6, 7); - - for (unsigned int i = 24; i < 27; ++i){ - zp.remove_simplex(simplices[i], filValues[i]); - } - - realIndices.emplace_back(1, 24, 25); - realBarcode.emplace_back(1, 8, 9); - - zp.insert_simplex(simplices[27], filValues[27]); - - realIndices.emplace_back(2, 23, 27); - realBarcode.emplace_back(2, 7, 9); - - test_indices(zp, realIndices, filValues); - test_barcode(zp, realBarcode); + ZP zp(28); + std::vector realIndices; + std::vector realBarcode; + realIndices.reserve(13); + realBarcode.reserve(9); + + std::vector > simplices = get_simplices(); + std::vector filValues = get_filtration_values(); + + for (unsigned int i = 0; i < 14; ++i) { + zp.insert_simplex(simplices[i], filValues[i]); + } + + realIndices.emplace_back(0, 1, 3); + realIndices.emplace_back(0, 2, 4); + realIndices.emplace_back(0, 7, 8); + realIndices.emplace_back(1, 6, 10); + realIndices.emplace_back(0, 9, 11); + realIndices.emplace_back(1, 12, 13); + + realBarcode.emplace_back(0, 0, 1); + realBarcode.emplace_back(0, 0, 1); + realBarcode.emplace_back(1, 2, 3); + realBarcode.emplace_back(1, 3, 4); + + for (unsigned int i = 14; i < 16; ++i) { + zp.remove_simplex(simplices[i], filValues[i]); + } + + for (unsigned int i = 16; i < 24; ++i) { + zp.insert_simplex(simplices[i], filValues[i]); + } + + realIndices.emplace_back(0, 5, 16); + realIndices.emplace_back(1, 14, 17); + realIndices.emplace_back(1, 15, 19); + realIndices.emplace_back(1, 20, 21); + realIndices.emplace_back(1, 18, 22); + + realBarcode.emplace_back(0, 1, 6); + realBarcode.emplace_back(1, 5, 6); + realBarcode.emplace_back(1, 6, 7); + + for (unsigned int i = 24; i < 27; ++i) { + zp.remove_simplex(simplices[i], filValues[i]); + } + + realIndices.emplace_back(1, 24, 25); + realBarcode.emplace_back(1, 8, 9); + + zp.insert_simplex(simplices[27], filValues[27]); + + realIndices.emplace_back(2, 23, 27); + realBarcode.emplace_back(2, 7, 9); + + zp.remove_simplex(simplices[28], filValues[28]); + + realBarcode.emplace_back(0, 0, std::numeric_limits::infinity()); + realBarcode.emplace_back(0, 9, std::numeric_limits::infinity()); + realBarcode.emplace_back(2, 10, std::numeric_limits::infinity()); + + test_indices(zp, realIndices, filValues); + test_barcode(zp, realBarcode); } BOOST_AUTO_TEST_CASE(zigzag_persistence_single_max1) { - ZP zp(28, 1); - std::vector realIndices; - std::vector indexToFil(28); - std::vector realBarcode; - realIndices.reserve(5); - realBarcode.reserve(3); - - std::vector > simplices = get_simplices(); - std::vector filValues = get_filtration_values(); - unsigned int currIndex = 0; - - for (unsigned int i = 0; i < 14; ++i){ - zp.insert_simplex(simplices[i], filValues[i]); - if (simplices[i].size() < 3){ - indexToFil[currIndex++] = filValues[i]; - } - } - - realIndices.emplace_back(0, 1, 3); - realIndices.emplace_back(0, 2, 4); - realIndices.emplace_back(0, 7, 8); - realIndices.emplace_back(0, 9, 10); - - realBarcode.emplace_back(0, 0, 1); - realBarcode.emplace_back(0, 0, 1); - - for (unsigned int i = 14; i < 16; ++i){ - zp.remove_simplex(simplices[i], filValues[i]); - if (simplices[i].size() < 3){ - indexToFil[currIndex++] = filValues[i]; - } - } - - for (unsigned int i = 16; i < 24; ++i){ - zp.insert_simplex(simplices[i], filValues[i]); - if (simplices[i].size() < 3){ - indexToFil[currIndex++] = filValues[i]; - } - } - - realIndices.emplace_back(0, 5, 12); - realBarcode.emplace_back(0, 1, 6); - - for (unsigned int i = 24; i < 27; ++i){ - zp.remove_simplex(simplices[i], filValues[i]); - if (simplices[i].size() < 3){ - indexToFil[currIndex++] = filValues[i]; - } - } - - zp.insert_simplex(simplices[27], filValues[27]); - - test_indices(zp, realIndices, indexToFil); - test_barcode(zp, realBarcode); + ZP zp(28, 1); + std::vector realIndices; + std::vector indexToFil(28); + std::vector realBarcode; + realIndices.reserve(5); + realBarcode.reserve(3); + + std::vector > simplices = get_simplices(); + std::vector filValues = get_filtration_values(); + unsigned int currIndex = 0; + + for (unsigned int i = 0; i < 14; ++i) { + zp.insert_simplex(simplices[i], filValues[i]); + if (simplices[i].size() < 3) { + indexToFil[currIndex++] = filValues[i]; + } + } + + realIndices.emplace_back(0, 1, 3); + realIndices.emplace_back(0, 2, 4); + realIndices.emplace_back(0, 7, 8); + realIndices.emplace_back(0, 9, 10); + + realBarcode.emplace_back(0, 0, 1); + realBarcode.emplace_back(0, 0, 1); + + for (unsigned int i = 14; i < 16; ++i) { + zp.remove_simplex(simplices[i], filValues[i]); + if (simplices[i].size() < 3) { + indexToFil[currIndex++] = filValues[i]; + } + } + + for (unsigned int i = 16; i < 24; ++i) { + zp.insert_simplex(simplices[i], filValues[i]); + if (simplices[i].size() < 3) { + indexToFil[currIndex++] = filValues[i]; + } + } + + realIndices.emplace_back(0, 5, 12); + realBarcode.emplace_back(0, 1, 6); + + for (unsigned int i = 24; i < 27; ++i) { + zp.remove_simplex(simplices[i], filValues[i]); + if (simplices[i].size() < 3) { + indexToFil[currIndex++] = filValues[i]; + } + } + + zp.insert_simplex(simplices[27], filValues[27]); + zp.remove_simplex(simplices[28], filValues[28]); + + realBarcode.emplace_back(0, 0, std::numeric_limits::infinity()); + realBarcode.emplace_back(0, 9, std::numeric_limits::infinity()); + + test_indices(zp, realIndices, indexToFil); + test_barcode(zp, realBarcode); } BOOST_AUTO_TEST_CASE(zigzag_persistence_batch_with_iterators) { - ZP zp; - std::vector realIndices; - std::vector realBarcode; - realIndices.reserve(13); - realBarcode.reserve(9); - - std::vector > simplices = get_simplices(); - std::vector filValues = get_filtration_values(); - - zp.insert_simplices_contiguously(simplices.begin(), - simplices.begin() + 14, - filValues.begin()); - - realIndices.emplace_back(0, 1, 3); - realIndices.emplace_back(0, 2, 4); - realIndices.emplace_back(0, 7, 8); - realIndices.emplace_back(1, 6, 10); - realIndices.emplace_back(0, 9, 11); - realIndices.emplace_back(1, 12, 13); - - realBarcode.emplace_back(0, 0, 1); - realBarcode.emplace_back(0, 0, 1); - realBarcode.emplace_back(1, 2, 3); - realBarcode.emplace_back(1, 3, 4); - - zp.remove_simplices_contiguously(simplices.begin() + 14, - simplices.begin() + 16, - filValues.begin() + 14); - zp.insert_simplices_contiguously(simplices.begin() + 16, - simplices.begin() + 24, - filValues.begin() + 16); - - realIndices.emplace_back(0, 5, 16); - realIndices.emplace_back(1, 14, 17); - realIndices.emplace_back(1, 15, 19); - realIndices.emplace_back(1, 20, 21); - realIndices.emplace_back(1, 18, 22); - - realBarcode.emplace_back(0, 1, 6); - realBarcode.emplace_back(1, 5, 6); - realBarcode.emplace_back(1, 6, 7); - - zp.remove_simplices_contiguously(simplices.begin() + 24, - simplices.begin() + 27, - filValues.begin() + 24); - - realIndices.emplace_back(1, 24, 25); - realBarcode.emplace_back(1, 8, 9); - - zp.insert_simplices_contiguously(simplices.begin() + 27, - simplices.begin() + 28, - filValues.begin() + 27); - - realIndices.emplace_back(2, 23, 27); - realBarcode.emplace_back(2, 7, 9); - - test_indices(zp, realIndices, filValues); - test_barcode(zp, realBarcode); + ZP zp; + std::vector realIndices; + std::vector realBarcode; + realIndices.reserve(13); + realBarcode.reserve(9); + + std::vector > simplices = get_simplices(); + std::vector filValues = get_filtration_values(); + + zp.insert_simplices_contiguously(simplices.begin(), simplices.begin() + 14, filValues.begin()); + + realIndices.emplace_back(0, 1, 3); + realIndices.emplace_back(0, 2, 4); + realIndices.emplace_back(0, 7, 8); + realIndices.emplace_back(1, 6, 10); + realIndices.emplace_back(0, 9, 11); + realIndices.emplace_back(1, 12, 13); + + realBarcode.emplace_back(0, 0, 1); + realBarcode.emplace_back(0, 0, 1); + realBarcode.emplace_back(1, 2, 3); + realBarcode.emplace_back(1, 3, 4); + + zp.remove_simplices_contiguously(simplices.begin() + 14, simplices.begin() + 16, filValues.begin() + 14); + zp.insert_simplices_contiguously(simplices.begin() + 16, simplices.begin() + 24, filValues.begin() + 16); + + realIndices.emplace_back(0, 5, 16); + realIndices.emplace_back(1, 14, 17); + realIndices.emplace_back(1, 15, 19); + realIndices.emplace_back(1, 20, 21); + realIndices.emplace_back(1, 18, 22); + + realBarcode.emplace_back(0, 1, 6); + realBarcode.emplace_back(1, 5, 6); + realBarcode.emplace_back(1, 6, 7); + + zp.remove_simplices_contiguously(simplices.begin() + 24, simplices.begin() + 27, filValues.begin() + 24); + + realIndices.emplace_back(1, 24, 25); + realBarcode.emplace_back(1, 8, 9); + + zp.insert_simplices_contiguously(simplices.begin() + 27, simplices.begin() + 28, filValues.begin() + 27); + + realIndices.emplace_back(2, 23, 27); + realBarcode.emplace_back(2, 7, 9); + + zp.remove_simplices_contiguously(simplices.begin() + 28, simplices.begin() + 29, filValues.begin() + 28); + + realBarcode.emplace_back(0, 0, std::numeric_limits::infinity()); + realBarcode.emplace_back(0, 9, std::numeric_limits::infinity()); + realBarcode.emplace_back(2, 10, std::numeric_limits::infinity()); + + test_indices(zp, realIndices, filValues); + test_barcode(zp, realBarcode); } BOOST_AUTO_TEST_CASE(zigzag_persistence_batch_with_iterators_max1) { - ZP zp(28, 1); - std::vector realIndices; - std::vector indexToFil(28); - std::vector realBarcode; - realIndices.reserve(5); - realBarcode.reserve(3); - - std::vector > simplices = get_simplices(); - std::vector filValues = get_filtration_values(); - unsigned int currIndex = 0; - - for (unsigned int i = 0; i < 28; ++i){ - if (simplices[i].size() < 3){ - indexToFil[currIndex++] = filValues[i]; - } - } - - zp.insert_simplices_contiguously(simplices.begin(), - simplices.begin() + 14, - filValues.begin()); - - realIndices.emplace_back(0, 1, 3); - realIndices.emplace_back(0, 2, 4); - realIndices.emplace_back(0, 7, 8); - realIndices.emplace_back(0, 9, 10); - - realBarcode.emplace_back(0, 0, 1); - realBarcode.emplace_back(0, 0, 1); - - zp.remove_simplices_contiguously(simplices.begin() + 14, - simplices.begin() + 16, - filValues.begin() + 14); - zp.insert_simplices_contiguously(simplices.begin() + 16, - simplices.begin() + 24, - filValues.begin() + 16); - - realIndices.emplace_back(0, 5, 12); - realBarcode.emplace_back(0, 1, 6); - - zp.remove_simplices_contiguously(simplices.begin() + 24, - simplices.begin() + 27, - filValues.begin() + 24); - zp.insert_simplices_contiguously(simplices.begin() + 27, - simplices.begin() + 28, - filValues.begin() + 27); - - test_indices(zp, realIndices, indexToFil); - test_barcode(zp, realBarcode); + ZP zp(28, 1); + std::vector realIndices; + std::vector indexToFil(28); + std::vector realBarcode; + realIndices.reserve(5); + realBarcode.reserve(3); + + std::vector > simplices = get_simplices(); + std::vector filValues = get_filtration_values(); + unsigned int currIndex = 0; + + for (unsigned int i = 0; i < 28; ++i) { + if (simplices[i].size() < 3) { + indexToFil[currIndex++] = filValues[i]; + } + } + + zp.insert_simplices_contiguously(simplices.begin(), simplices.begin() + 14, filValues.begin()); + + realIndices.emplace_back(0, 1, 3); + realIndices.emplace_back(0, 2, 4); + realIndices.emplace_back(0, 7, 8); + realIndices.emplace_back(0, 9, 10); + + realBarcode.emplace_back(0, 0, 1); + realBarcode.emplace_back(0, 0, 1); + + zp.remove_simplices_contiguously(simplices.begin() + 14, simplices.begin() + 16, filValues.begin() + 14); + zp.insert_simplices_contiguously(simplices.begin() + 16, simplices.begin() + 24, filValues.begin() + 16); + + realIndices.emplace_back(0, 5, 12); + realBarcode.emplace_back(0, 1, 6); + + zp.remove_simplices_contiguously(simplices.begin() + 24, simplices.begin() + 27, filValues.begin() + 24); + zp.insert_simplices_contiguously(simplices.begin() + 27, simplices.begin() + 28, filValues.begin() + 27); + zp.remove_simplices_contiguously(simplices.begin() + 28, simplices.begin() + 29, filValues.begin() + 28); + + realBarcode.emplace_back(0, 0, std::numeric_limits::infinity()); + realBarcode.emplace_back(0, 9, std::numeric_limits::infinity()); + + test_indices(zp, realIndices, indexToFil); + test_barcode(zp, realBarcode); } BOOST_AUTO_TEST_CASE(zigzag_persistence_batch) { - ZP zp; - std::vector realIndices; - std::vector realBarcode; - realIndices.reserve(13); - realBarcode.reserve(9); - - std::vector > simplices = get_simplices(); - std::vector filValues = get_filtration_values(); - - std::vector > subSimplices(simplices.begin(), simplices.begin() + 14); - std::vector subFilValues(filValues.begin(), filValues.begin() + 14); - zp.insert_simplices_contiguously(subSimplices, subFilValues); - - realIndices.emplace_back(0, 1, 3); - realIndices.emplace_back(0, 2, 4); - realIndices.emplace_back(0, 7, 8); - realIndices.emplace_back(1, 6, 10); - realIndices.emplace_back(0, 9, 11); - realIndices.emplace_back(1, 12, 13); - - realBarcode.emplace_back(0, 0, 1); - realBarcode.emplace_back(0, 0, 1); - realBarcode.emplace_back(1, 2, 3); - realBarcode.emplace_back(1, 3, 4); - - subSimplices = std::vector >(simplices.begin() + 14, simplices.begin() + 16); - subFilValues = std::vector(filValues.begin() + 14, filValues.begin() + 16); - zp.remove_simplices_contiguously(subSimplices, subFilValues); - - subSimplices = std::vector >(simplices.begin() + 16, simplices.begin() + 24); - subFilValues = std::vector(filValues.begin() + 16, filValues.begin() + 24); - zp.insert_simplices_contiguously(subSimplices, subFilValues); - - realIndices.emplace_back(0, 5, 16); - realIndices.emplace_back(1, 14, 17); - realIndices.emplace_back(1, 15, 19); - realIndices.emplace_back(1, 20, 21); - realIndices.emplace_back(1, 18, 22); - - realBarcode.emplace_back(0, 1, 6); - realBarcode.emplace_back(1, 5, 6); - realBarcode.emplace_back(1, 6, 7); - - subSimplices = std::vector >(simplices.begin() + 24, simplices.begin() + 27); - subFilValues = std::vector(filValues.begin() + 24, filValues.begin() + 27); - zp.remove_simplices_contiguously(subSimplices, subFilValues); - - realIndices.emplace_back(1, 24, 25); - realBarcode.emplace_back(1, 8, 9); - - subSimplices = std::vector >(simplices.begin() + 27, simplices.begin() + 28); - subFilValues = std::vector(filValues.begin() + 27, filValues.begin() + 28); - zp.insert_simplices_contiguously(subSimplices, subFilValues); - - realIndices.emplace_back(2, 23, 27); - realBarcode.emplace_back(2, 7, 9); - - test_indices(zp, realIndices, filValues); - test_barcode(zp, realBarcode); + ZP zp; + std::vector realIndices; + std::vector realBarcode; + realIndices.reserve(13); + realBarcode.reserve(9); + + std::vector > simplices = get_simplices(); + std::vector filValues = get_filtration_values(); + + std::vector > subSimplices(simplices.begin(), simplices.begin() + 14); + std::vector subFilValues(filValues.begin(), filValues.begin() + 14); + zp.insert_simplices_contiguously(subSimplices, subFilValues); + + realIndices.emplace_back(0, 1, 3); + realIndices.emplace_back(0, 2, 4); + realIndices.emplace_back(0, 7, 8); + realIndices.emplace_back(1, 6, 10); + realIndices.emplace_back(0, 9, 11); + realIndices.emplace_back(1, 12, 13); + + realBarcode.emplace_back(0, 0, 1); + realBarcode.emplace_back(0, 0, 1); + realBarcode.emplace_back(1, 2, 3); + realBarcode.emplace_back(1, 3, 4); + + subSimplices = std::vector >(simplices.begin() + 14, simplices.begin() + 16); + subFilValues = std::vector(filValues.begin() + 14, filValues.begin() + 16); + zp.remove_simplices_contiguously(subSimplices, subFilValues); + + subSimplices = std::vector >(simplices.begin() + 16, simplices.begin() + 24); + subFilValues = std::vector(filValues.begin() + 16, filValues.begin() + 24); + zp.insert_simplices_contiguously(subSimplices, subFilValues); + + realIndices.emplace_back(0, 5, 16); + realIndices.emplace_back(1, 14, 17); + realIndices.emplace_back(1, 15, 19); + realIndices.emplace_back(1, 20, 21); + realIndices.emplace_back(1, 18, 22); + + realBarcode.emplace_back(0, 1, 6); + realBarcode.emplace_back(1, 5, 6); + realBarcode.emplace_back(1, 6, 7); + + subSimplices = std::vector >(simplices.begin() + 24, simplices.begin() + 27); + subFilValues = std::vector(filValues.begin() + 24, filValues.begin() + 27); + zp.remove_simplices_contiguously(subSimplices, subFilValues); + + realIndices.emplace_back(1, 24, 25); + realBarcode.emplace_back(1, 8, 9); + + subSimplices = std::vector >(simplices.begin() + 27, simplices.begin() + 28); + subFilValues = std::vector(filValues.begin() + 27, filValues.begin() + 28); + zp.insert_simplices_contiguously(subSimplices, subFilValues); + + realIndices.emplace_back(2, 23, 27); + realBarcode.emplace_back(2, 7, 9); + + subSimplices = std::vector >(simplices.begin() + 28, simplices.begin() + 29); + subFilValues = std::vector(filValues.begin() + 28, filValues.begin() + 29); + zp.remove_simplices_contiguously(subSimplices, subFilValues); + + realBarcode.emplace_back(0, 0, std::numeric_limits::infinity()); + realBarcode.emplace_back(0, 9, std::numeric_limits::infinity()); + realBarcode.emplace_back(2, 10, std::numeric_limits::infinity()); + + test_indices(zp, realIndices, filValues); + test_barcode(zp, realBarcode); } BOOST_AUTO_TEST_CASE(zigzag_persistence_batch_max1) { - ZP zp(28, 1); - std::vector realIndices; - std::vector indexToFil(28); - std::vector realBarcode; - realIndices.reserve(5); - realBarcode.reserve(3); - - std::vector > simplices = get_simplices(); - std::vector filValues = get_filtration_values(); - unsigned int currIndex = 0; - - for (unsigned int i = 0; i < 28; ++i){ - if (simplices[i].size() < 3){ - indexToFil[currIndex++] = filValues[i]; - } - } - - std::vector > subSimplices(simplices.begin(), simplices.begin() + 14); - std::vector subFilValues(filValues.begin(), filValues.begin() + 14); - zp.insert_simplices_contiguously(subSimplices, subFilValues); - - realIndices.emplace_back(0, 1, 3); - realIndices.emplace_back(0, 2, 4); - realIndices.emplace_back(0, 7, 8); - realIndices.emplace_back(0, 9, 10); - - realBarcode.emplace_back(0, 0, 1); - realBarcode.emplace_back(0, 0, 1); - - subSimplices = std::vector >(simplices.begin() + 14, simplices.begin() + 16); - subFilValues = std::vector(filValues.begin() + 14, filValues.begin() + 16); - zp.remove_simplices_contiguously(subSimplices, subFilValues); - - subSimplices = std::vector >(simplices.begin() + 16, simplices.begin() + 24); - subFilValues = std::vector(filValues.begin() + 16, filValues.begin() + 24); - zp.insert_simplices_contiguously(subSimplices, subFilValues); - - realIndices.emplace_back(0, 5, 12); - realBarcode.emplace_back(0, 1, 6); - - subSimplices = std::vector >(simplices.begin() + 24, simplices.begin() + 27); - subFilValues = std::vector(filValues.begin() + 24, filValues.begin() + 27); - zp.remove_simplices_contiguously(subSimplices, subFilValues); - - subSimplices = std::vector >(simplices.begin() + 27, simplices.begin() + 28); - subFilValues = std::vector(filValues.begin() + 27, filValues.begin() + 28); - zp.insert_simplices_contiguously(subSimplices, subFilValues); - - test_indices(zp, realIndices, indexToFil); - test_barcode(zp, realBarcode); + ZP zp(28, 1); + std::vector realIndices; + std::vector indexToFil(28); + std::vector realBarcode; + realIndices.reserve(5); + realBarcode.reserve(3); + + std::vector > simplices = get_simplices(); + std::vector filValues = get_filtration_values(); + unsigned int currIndex = 0; + + for (unsigned int i = 0; i < 28; ++i) { + if (simplices[i].size() < 3) { + indexToFil[currIndex++] = filValues[i]; + } + } + + std::vector > subSimplices(simplices.begin(), simplices.begin() + 14); + std::vector subFilValues(filValues.begin(), filValues.begin() + 14); + zp.insert_simplices_contiguously(subSimplices, subFilValues); + + realIndices.emplace_back(0, 1, 3); + realIndices.emplace_back(0, 2, 4); + realIndices.emplace_back(0, 7, 8); + realIndices.emplace_back(0, 9, 10); + + realBarcode.emplace_back(0, 0, 1); + realBarcode.emplace_back(0, 0, 1); + + subSimplices = std::vector >(simplices.begin() + 14, simplices.begin() + 16); + subFilValues = std::vector(filValues.begin() + 14, filValues.begin() + 16); + zp.remove_simplices_contiguously(subSimplices, subFilValues); + + subSimplices = std::vector >(simplices.begin() + 16, simplices.begin() + 24); + subFilValues = std::vector(filValues.begin() + 16, filValues.begin() + 24); + zp.insert_simplices_contiguously(subSimplices, subFilValues); + + realIndices.emplace_back(0, 5, 12); + realBarcode.emplace_back(0, 1, 6); + + subSimplices = std::vector >(simplices.begin() + 24, simplices.begin() + 27); + subFilValues = std::vector(filValues.begin() + 24, filValues.begin() + 27); + zp.remove_simplices_contiguously(subSimplices, subFilValues); + + subSimplices = std::vector >(simplices.begin() + 27, simplices.begin() + 28); + subFilValues = std::vector(filValues.begin() + 27, filValues.begin() + 28); + zp.insert_simplices_contiguously(subSimplices, subFilValues); + + subSimplices = std::vector >(simplices.begin() + 27, simplices.begin() + 28); + subFilValues = std::vector(filValues.begin() + 27, filValues.begin() + 28); + zp.remove_simplices_contiguously(subSimplices, subFilValues); + + realBarcode.emplace_back(0, 0, std::numeric_limits::infinity()); + realBarcode.emplace_back(0, 9, std::numeric_limits::infinity()); + + test_indices(zp, realIndices, indexToFil); + test_barcode(zp, realBarcode); } From 2e5a20bfcc35e9602f90d56599fca85660754860 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Fri, 21 Jul 2023 14:44:06 +0200 Subject: [PATCH 03/51] deleting files not wanted for the PR --- .../benchmark/CMakeLists.txt | 17 - .../benchmark/Zigzag_benchmark.cpp | 197 -- .../benchmark/Zigzag_old_benchmark.cpp | 112 - .../benchmark/Zigzag_other_benchmark.cpp | 144 - .../benchmark/rips-zigzag-dionysus.h | 211 -- src/Zigzag_persistence/example/CMakeLists.txt | 18 - .../example/comparison_for_tests.cpp | 440 --- .../example_rips_zigzag_filtration.cpp | 158 - .../example/ext_zz/dionysus/chain.h | 153 - .../example/ext_zz/dionysus/chain.hpp | 188 -- .../ext_zz/dionysus/clearing-reduction.h | 45 - .../ext_zz/dionysus/clearing-reduction.hpp | 60 - .../example/ext_zz/dionysus/cnpy.h | 241 -- .../ext_zz/dionysus/cohomology-persistence.h | 116 - .../dionysus/cohomology-persistence.hpp | 61 - .../example/ext_zz/dionysus/common.h | 25 - .../example/ext_zz/dionysus/diagram.h | 114 - .../example/ext_zz/dionysus/distances.h | 93 - .../example/ext_zz/dionysus/distances.hpp | 30 - .../example/ext_zz/dionysus/dlog/progress.h | 57 - .../example/ext_zz/dionysus/fields/q.h | 63 - .../example/ext_zz/dionysus/fields/z2.h | 31 - .../example/ext_zz/dionysus/fields/zp.h | 55 - .../example/ext_zz/dionysus/filtration.h | 124 - .../example/ext_zz/dionysus/format.h | 8 - .../example/ext_zz/dionysus/format/format.cc | 1156 -------- .../example/ext_zz/dionysus/format/format.h | 2546 ----------------- .../example/ext_zz/dionysus/grid/box.h | 136 - .../example/ext_zz/dionysus/grid/box.hpp | 141 - .../example/ext_zz/dionysus/grid/grid.h | 143 - .../example/ext_zz/dionysus/grid/point.h | 132 - .../example/ext_zz/dionysus/grid/vertices.h | 86 - .../ext_zz/dionysus/matrix-filtration.h | 120 - .../ext_zz/dionysus/omni-field-persistence.h | 145 - .../dionysus/omni-field-persistence.hpp | 250 -- .../example/ext_zz/dionysus/opts/opts.h | 499 ---- .../ext_zz/dionysus/ordinary-persistence.h | 64 - .../example/ext_zz/dionysus/pair-recorder.h | 78 - .../example/ext_zz/dionysus/reduced-matrix.h | 170 -- .../ext_zz/dionysus/reduced-matrix.hpp | 78 - .../example/ext_zz/dionysus/reduction.h | 109 - .../dionysus/relative-homology-zigzag.h | 84 - .../dionysus/relative-homology-zigzag.hpp | 122 - .../example/ext_zz/dionysus/rips.h | 147 - .../example/ext_zz/dionysus/rips.hpp | 162 -- .../example/ext_zz/dionysus/row-reduction.h | 54 - .../example/ext_zz/dionysus/row-reduction.hpp | 103 - .../example/ext_zz/dionysus/simplex.h | 280 -- .../ext_zz/dionysus/sparse-row-matrix.h | 184 -- .../ext_zz/dionysus/sparse-row-matrix.hpp | 103 - .../ext_zz/dionysus/standard-reduction.h | 44 - .../ext_zz/dionysus/standard-reduction.hpp | 47 - .../example/ext_zz/dionysus/trails-chains.h | 17 - .../ext_zz/dionysus/zigzag-persistence.h | 142 - .../ext_zz/dionysus/zigzag-persistence.hpp | 541 ---- .../example/ext_zz/fzz/fzz.cpp | 206 -- .../example/ext_zz/fzz/fzz.h | 87 - .../ext_zz/phat/algorithms/chunk_reduction.h | 223 -- .../ext_zz/phat/algorithms/row_reduction.h | 56 - .../algorithms/spectral_sequence_reduction.h | 80 - .../phat/algorithms/standard_reduction.h | 47 - .../ext_zz/phat/algorithms/twist_reduction.h | 51 - .../example/ext_zz/phat/boundary_matrix.h | 343 --- .../ext_zz/phat/compute_persistence_pairs.h | 128 - .../example/ext_zz/phat/helpers/dualize.h | 74 - .../example/ext_zz/phat/helpers/misc.h | 75 - .../phat/helpers/thread_local_storage.h | 52 - .../example/ext_zz/phat/persistence_pairs.h | 155 - .../representations/abstract_pivot_column.h | 102 - .../representations/bit_tree_pivot_column.h | 165 -- .../phat/representations/full_pivot_column.h | 100 - .../phat/representations/heap_pivot_column.h | 126 - .../representations/sparse_pivot_column.h | 79 - .../ext_zz/phat/representations/vector_heap.h | 170 -- .../ext_zz/phat/representations/vector_list.h | 101 - .../ext_zz/phat/representations/vector_set.h | 99 - .../phat/representations/vector_vector.h | 107 - .../example/rips-zigzag-dionysus.h | 211 -- .../include/gudhi/Zigzag_persistence_old.h | 1326 --------- 79 files changed, 14807 deletions(-) delete mode 100644 src/Zigzag_persistence/benchmark/CMakeLists.txt delete mode 100644 src/Zigzag_persistence/benchmark/Zigzag_benchmark.cpp delete mode 100644 src/Zigzag_persistence/benchmark/Zigzag_old_benchmark.cpp delete mode 100644 src/Zigzag_persistence/benchmark/Zigzag_other_benchmark.cpp delete mode 100644 src/Zigzag_persistence/benchmark/rips-zigzag-dionysus.h delete mode 100644 src/Zigzag_persistence/example/comparison_for_tests.cpp delete mode 100644 src/Zigzag_persistence/example/example_rips_zigzag_filtration.cpp delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/chain.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/chain.hpp delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/clearing-reduction.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/clearing-reduction.hpp delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/cnpy.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/cohomology-persistence.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/cohomology-persistence.hpp delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/common.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/diagram.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/distances.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/distances.hpp delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/dlog/progress.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/fields/q.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/fields/z2.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/fields/zp.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/filtration.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/format.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/format/format.cc delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/format/format.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/grid/box.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/grid/box.hpp delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/grid/grid.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/grid/point.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/grid/vertices.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/matrix-filtration.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/omni-field-persistence.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/omni-field-persistence.hpp delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/opts/opts.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/ordinary-persistence.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/pair-recorder.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/reduced-matrix.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/reduced-matrix.hpp delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/reduction.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/relative-homology-zigzag.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/relative-homology-zigzag.hpp delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/rips.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/rips.hpp delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/row-reduction.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/row-reduction.hpp delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/simplex.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/sparse-row-matrix.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/sparse-row-matrix.hpp delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/standard-reduction.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/standard-reduction.hpp delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/trails-chains.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/zigzag-persistence.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/dionysus/zigzag-persistence.hpp delete mode 100644 src/Zigzag_persistence/example/ext_zz/fzz/fzz.cpp delete mode 100644 src/Zigzag_persistence/example/ext_zz/fzz/fzz.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/phat/algorithms/chunk_reduction.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/phat/algorithms/row_reduction.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/phat/algorithms/spectral_sequence_reduction.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/phat/algorithms/standard_reduction.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/phat/algorithms/twist_reduction.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/phat/boundary_matrix.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/phat/compute_persistence_pairs.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/phat/helpers/dualize.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/phat/helpers/misc.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/phat/helpers/thread_local_storage.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/phat/persistence_pairs.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/phat/representations/abstract_pivot_column.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/phat/representations/bit_tree_pivot_column.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/phat/representations/full_pivot_column.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/phat/representations/heap_pivot_column.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/phat/representations/sparse_pivot_column.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/phat/representations/vector_heap.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/phat/representations/vector_list.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/phat/representations/vector_set.h delete mode 100644 src/Zigzag_persistence/example/ext_zz/phat/representations/vector_vector.h delete mode 100644 src/Zigzag_persistence/example/rips-zigzag-dionysus.h delete mode 100644 src/Zigzag_persistence/include/gudhi/Zigzag_persistence_old.h diff --git a/src/Zigzag_persistence/benchmark/CMakeLists.txt b/src/Zigzag_persistence/benchmark/CMakeLists.txt deleted file mode 100644 index 1f0faeb542..0000000000 --- a/src/Zigzag_persistence/benchmark/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -project(Zigzag_benchmark) - -find_package(benchmark REQUIRED) - -add_executable(Zigzag_benchmark Zigzag_benchmark.cpp) -target_link_libraries(Zigzag_benchmark benchmark::benchmark) -target_include_directories(Zigzag_benchmark PUBLIC . ../example/ext_zz) - -add_executable(Zigzag_old_benchmark Zigzag_old_benchmark.cpp) -target_link_libraries(Zigzag_old_benchmark benchmark::benchmark) -target_include_directories(Zigzag_old_benchmark PUBLIC . ../example/ext_zz) - -add_executable(Zigzag_other_benchmark Zigzag_other_benchmark.cpp ../example/ext_zz/fzz/fzz.cpp) -target_link_libraries(Zigzag_other_benchmark benchmark::benchmark) -target_include_directories(Zigzag_other_benchmark PUBLIC . ../example/ext_zz) -target_compile_options(Zigzag_other_benchmark PUBLIC "-fopenmp") -target_link_options(Zigzag_other_benchmark PUBLIC "-fopenmp") diff --git a/src/Zigzag_persistence/benchmark/Zigzag_benchmark.cpp b/src/Zigzag_persistence/benchmark/Zigzag_benchmark.cpp deleted file mode 100644 index 37b4edfb24..0000000000 --- a/src/Zigzag_persistence/benchmark/Zigzag_benchmark.cpp +++ /dev/null @@ -1,197 +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): Hannah Schreiber - * - * Copyright (C) 2022 Inria - * - * Modification(s): - * - YYYY/MM Author: Description of the modification - */ - -#include -#include -#include -#include -#include // for pair -#include -#include - -#include -#include - -#include -#include -#include - -#include "rips-zigzag-dionysus.h" - -using ST = Gudhi::Simplex_tree; -// using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; -using Vertex_handle = ST::Vertex_handle; -using Filtration_value = ST::Filtration_value; - -using Gudhi::persistence_matrix::Zigzag_options; -using CT = Gudhi::persistence_matrix::Column_types; - -template -std::vector< std::pair > print_indices(ZP& zp, unsigned int numberOfSimplices){ - std::set essentials; - std::vector< std::pair > res; - - for (unsigned int i = 0; i < numberOfSimplices; ++i){ - essentials.insert(essentials.end(), i); - } - - for (auto& bar : zp.get_index_persistence_diagram()){ - res.emplace_back(bar.birth(), bar.death()); - essentials.erase(bar.birth()); - essentials.erase(bar.death()); - } - - for (unsigned int v : essentials){ - res.emplace_back(v, numberOfSimplices); - } - - return res; -} - -template -std::vector< std::pair > compute_with_gudhi( - const std::vector >& simplices, - const std::vector& dirs) -{ - ZP zp(simplices.size()); - - // std::cout << "====================== Gudhi =====================\n"; - - std::vector filValues(simplices.size(), 1.0); - - auto start = simplices.begin(); - auto filIt = filValues.begin(); - unsigned int i = 0; - - while (start != simplices.end()){ - unsigned int c = 1; - auto end = start + 1; - ++i; - while (end != simplices.end() && dirs[i - 1] == dirs[i]) { - ++end; - ++i; - ++c; - } - - if (dirs[i - 1]){ - zp.insert_simplices_contiguously( - start, end, filIt); - } else { - zp.remove_simplices_contiguously( - start, end, filIt); - } - - start = end; - filIt += c; - } - - // std::cout << "==================================================\n"; - - return print_indices(zp, i); -} - -static void Compute_zigzag_with_gudhi_intrusive_list(benchmark::State& state) { - using ZP = Gudhi::zigzag_persistence::Zigzag_persistence >; - - unsigned int numberOfPoints = state.range(0); - int seed = numberOfPoints; - std::vector > simplices; - std::vector dirs; - - build_rips_zigzag_filtration(simplices, dirs, numberOfPoints, seed); - - for (auto _ : state){ - compute_with_gudhi(simplices, dirs); - } -} -BENCHMARK(Compute_zigzag_with_gudhi_intrusive_list)->RangeMultiplier(2)->Range(100, 1000)->Unit(benchmark::kMillisecond)->MinWarmUpTime(1); - -static void Compute_zigzag_with_gudhi_intrusive_set(benchmark::State& state) { - using ZP = Gudhi::zigzag_persistence::Zigzag_persistence >; - - unsigned int numberOfPoints = state.range(0); - int seed = numberOfPoints; - std::vector > simplices; - std::vector dirs; - - build_rips_zigzag_filtration(simplices, dirs, numberOfPoints, seed); - - for (auto _ : state){ - compute_with_gudhi(simplices, dirs); - } -} -BENCHMARK(Compute_zigzag_with_gudhi_intrusive_set)->RangeMultiplier(2)->Range(100, 1000)->Unit(benchmark::kMillisecond)->MinWarmUpTime(1); - -static void Compute_zigzag_with_gudhi_list(benchmark::State& state) { - using ZP = Gudhi::zigzag_persistence::Zigzag_persistence >; - - unsigned int numberOfPoints = state.range(0); - int seed = numberOfPoints; - std::vector > simplices; - std::vector dirs; - - build_rips_zigzag_filtration(simplices, dirs, numberOfPoints, seed); - - for (auto _ : state){ - compute_with_gudhi(simplices, dirs); - } -} -BENCHMARK(Compute_zigzag_with_gudhi_list)->RangeMultiplier(2)->Range(100, 1000)->Unit(benchmark::kMillisecond)->MinWarmUpTime(1); - -// static void Compute_zigzag_with_gudhi_set(benchmark::State& state) { -// using ZP = Gudhi::zigzag_persistence::Zigzag_persistence >; - -// unsigned int numberOfPoints = state.range(0); -// int seed = numberOfPoints; -// std::vector > simplices; -// std::vector dirs; - -// build_rips_zigzag_filtration(simplices, dirs, numberOfPoints, seed); - -// for (auto _ : state){ -// compute_with_gudhi(simplices, dirs); -// } -// } -// BENCHMARK(Compute_zigzag_with_gudhi_set)->RangeMultiplier(2)->Range(100, 1000)->Unit(benchmark::kMillisecond)->MinWarmUpTime(1); - -// static void Compute_zigzag_with_gudhi_unordered_set(benchmark::State& state) { -// using ZP = Gudhi::zigzag_persistence::Zigzag_persistence>; - -// unsigned int numberOfPoints = state.range(0); -// int seed = numberOfPoints; -// std::vector > simplices; -// std::vector dirs; - -// build_rips_zigzag_filtration(simplices, dirs, numberOfPoints, seed); - -// for (auto _ : state){ -// compute_with_gudhi(simplices, dirs); -// } -// } -// BENCHMARK(Compute_zigzag_with_gudhi_unordered_set)->RangeMultiplier(2)->Range(100, 1000)->Unit(benchmark::kMillisecond)->MinWarmUpTime(1); - -static void Compute_zigzag_with_gudhi_vector(benchmark::State& state) { - using ZP = Gudhi::zigzag_persistence::Zigzag_persistence>; - - unsigned int numberOfPoints = state.range(0); - int seed = numberOfPoints; - std::vector > simplices; - std::vector dirs; - - build_rips_zigzag_filtration(simplices, dirs, numberOfPoints, seed); - - for (auto _ : state){ - compute_with_gudhi(simplices, dirs); - } -} -BENCHMARK(Compute_zigzag_with_gudhi_vector)->RangeMultiplier(2)->Range(100, 1000)->Unit(benchmark::kMillisecond)->MinWarmUpTime(1); - -BENCHMARK_MAIN(); - diff --git a/src/Zigzag_persistence/benchmark/Zigzag_old_benchmark.cpp b/src/Zigzag_persistence/benchmark/Zigzag_old_benchmark.cpp deleted file mode 100644 index ca079443c3..0000000000 --- a/src/Zigzag_persistence/benchmark/Zigzag_old_benchmark.cpp +++ /dev/null @@ -1,112 +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): Hannah Schreiber - * - * Copyright (C) 2022 Inria - * - * Modification(s): - * - YYYY/MM Author: Description of the modification - */ - -#include -#include -#include -#include -#include // for pair -#include -#include - -#include -#include - -#include -#include - -#include "rips-zigzag-dionysus.h" - -using ST = Gudhi::Simplex_tree; -using coltype = Gudhi::zigzag_persistence::Zigzag_persistence_collist; -using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; -// using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; -using Vertex_handle = ST::Vertex_handle; -using Filtration_value = ST::Filtration_value; -using interval_filtration = ZP::fil_interval; - -std::vector< std::pair > print_indices(ZP& zp, unsigned int numberOfSimplices){ - std::set essentials; - std::vector< std::pair > res; - - for (unsigned int i = 0; i < numberOfSimplices; ++i){ - essentials.insert(essentials.end(), i); - } - - for (auto& bar : zp.get_index_persistence_diagram()){ - res.emplace_back(bar.birth(), bar.death()); - essentials.erase(bar.birth()); - essentials.erase(bar.death()); - } - - for (unsigned int v : essentials){ - res.emplace_back(v, numberOfSimplices); - } - - return res; -} - -std::vector< std::pair > compute_with_gudhi( - const std::vector >& simplices, - const std::vector& dirs) -{ - ZP zp(simplices.size()); - - // std::cout << "====================== Gudhi =====================\n"; - - std::vector filValues(simplices.size(), 1.0); - - auto start = simplices.begin(); - auto filIt = filValues.begin(); - unsigned int i = 0; - - while (start != simplices.end()){ - unsigned int c = 1; - auto end = start + 1; - ++i; - while (end != simplices.end() && dirs[i - 1] == dirs[i]) { - ++end; - ++i; - ++c; - } - - if (dirs[i - 1]){ - zp.insert_simplices_contiguously( - start, end, filIt); - } else { - zp.remove_simplices_contiguously( - start, end, filIt); - } - - start = end; - filIt += c; - } - - // std::cout << "==================================================\n"; - - return print_indices(zp, i); -} - -static void Compute_zigzag_with_gudhi(benchmark::State& state) { - unsigned int numberOfPoints = state.range(0);; - int seed = numberOfPoints; - std::vector > simplices; - std::vector dirs; - - build_rips_zigzag_filtration(simplices, dirs, numberOfPoints, seed); - - for (auto _ : state){ - compute_with_gudhi(simplices, dirs); - } -} -BENCHMARK(Compute_zigzag_with_gudhi)->RangeMultiplier(2)->Range(100, 1000)->Unit(benchmark::kMillisecond)->MinWarmUpTime(1); - -BENCHMARK_MAIN(); - diff --git a/src/Zigzag_persistence/benchmark/Zigzag_other_benchmark.cpp b/src/Zigzag_persistence/benchmark/Zigzag_other_benchmark.cpp deleted file mode 100644 index 6e51854338..0000000000 --- a/src/Zigzag_persistence/benchmark/Zigzag_other_benchmark.cpp +++ /dev/null @@ -1,144 +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): Hannah Schreiber - * - * Copyright (C) 2022 Inria - * - * Modification(s): - * - YYYY/MM Author: Description of the modification - */ - -#include -#include -#include -#include -#include // for pair -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "rips-zigzag-dionysus.h" - -using DField = dionysus::Z2Field; -using Simplex = dionysus::Simplex<>; -using DFiltration = dionysus::Filtration; -using DZZ = dionysus::ZigzagPersistence; -using DIndex = typename DZZ::Index; -using DChain = dionysus::ChainEntry; -using DIChain = dionysus::ChainEntry; -using Vertex_handle = int; - -std::vector< std::pair > compute_with_dionysus( - const std::vector >& simplices, - const std::vector& dirs) -{ - DField k; - std::unordered_map indices; - DZZ persistence(k); - std::vector< std::pair > res; - - // std::cout << "==================== Dionysus ====================\n"; - - std::set essentials; - - unsigned int op = 0; - unsigned int idx = 0; - - for (const std::vector& simplex : simplices){ - Simplex c(simplex); - DIndex pair; - if (dirs[op]) { - indices.try_emplace(c, idx++); - pair = persistence.add(c.boundary(persistence.field()) | - boost::adaptors::transformed([&indices](const DChain& e) { - return DIChain(e.element(), indices.find(e.index())->second); - })); - } else { - auto idxIt = indices.find(c); - pair = persistence.remove(idxIt->second); - indices.erase(idxIt); - } - - if (pair != DZZ::unpaired()) { - res.emplace_back(pair, op); - essentials.erase(pair); - } else { - essentials.insert(essentials.end(), op); - } - op++; - } - - for (unsigned int v : essentials){ - res.emplace_back(v, op); - } - - // std::cout << "==================================================\n"; - - return res; -} - -std::vector< std::tuple > compute_with_fzz( - const std::vector >& simplices, - const std::vector& dirs) -{ - std::vector< std::tuple > persistence; - FZZ::FastZigzag fzz; - - // std::cout << "======================= FZZ ======================\n"; - - fzz.compute(simplices, dirs, &persistence); - - std::sort(persistence.begin(), persistence.end(), - [](const std::tuple& p1, const std::tuple& p2){ - if (std::get<1>(p1) == std::get<1>(p2)){ - return std::get<0>(p1) < std::get<0>(p2); - } - - return std::get<1>(p1) < std::get<1>(p2); - }); - - // std::cout << "==================================================\n"; - - return persistence; -} - -static void Compute_zigzag_with_dionysus(benchmark::State& state) { - unsigned int numberOfPoints = state.range(0);; - int seed = numberOfPoints; - std::vector > simplices; - std::vector dirs; - - build_rips_zigzag_filtration(simplices, dirs, numberOfPoints, seed); - - for (auto _ : state){ - compute_with_dionysus(simplices, dirs); - } -} -BENCHMARK(Compute_zigzag_with_dionysus)->RangeMultiplier(2)->Range(100, 1000)->Unit(benchmark::kMillisecond)->MinWarmUpTime(1); - -static void Compute_zigzag_with_fzz(benchmark::State& state) { - unsigned int numberOfPoints = state.range(0);; - int seed = numberOfPoints; - std::vector > simplices; - std::vector dirs; - - build_rips_zigzag_filtration(simplices, dirs, numberOfPoints, seed); - - for (auto _ : state){ - compute_with_fzz(simplices, dirs); - } -} -BENCHMARK(Compute_zigzag_with_fzz)->RangeMultiplier(2)->Range(100, 1000)->Unit(benchmark::kMillisecond)->MinWarmUpTime(1); - -BENCHMARK_MAIN(); - diff --git a/src/Zigzag_persistence/benchmark/rips-zigzag-dionysus.h b/src/Zigzag_persistence/benchmark/rips-zigzag-dionysus.h deleted file mode 100644 index 5cd2c585c4..0000000000 --- a/src/Zigzag_persistence/benchmark/rips-zigzag-dionysus.h +++ /dev/null @@ -1,211 +0,0 @@ -#include -#include -#include -#include - -#include -namespace ba = boost::adaptors; - -#include -#include -#include -namespace d = dionysus; - -#include - -typedef std::vector Point; -typedef std::vector PointContainer; - -typedef d::PairwiseDistances> PairDistances; -typedef PairDistances::DistanceType DistanceType; -typedef PairDistances::IndexType Vertex; - -typedef d::Rips Generator; -typedef Generator::Simplex Simplex; -typedef std::set SimplexSet; - -typedef std::vector VertexVector; -typedef std::vector EpsilonVector; -typedef std::tuple Edge; -typedef std::vector EdgeVector; - -inline PointContainer compute_points(unsigned int numberOfPoints, int seed = -1) -{ - PointContainer finalPoints; - std::set points; - std::random_device dev; - std::mt19937 rng(dev()); - if (seed > -1) rng.seed(seed); - std::uniform_real_distribution dist(0,10); - - for (unsigned int i = 0; i < numberOfPoints; ++i){ - auto res = points.insert({dist(rng), dist(rng)}); - while(!res.second){ - res = points.insert({dist(rng), dist(rng)}); - } - finalPoints.push_back(*res.first); - } - - return finalPoints; -} - -inline void compute_vertices_and_epsilons(const PairDistances& distances, - VertexVector& vertices, - EpsilonVector& epsilons) -{ - DistanceType inf = std::numeric_limits::infinity(); - EpsilonVector dist(distances.size(), inf); - - vertices.push_back(distances.begin()); - // epsilons.push_back(inf); - while (vertices.size() < distances.size()) { - for (Vertex v = distances.begin(); v != distances.end(); ++v) - dist[v] = std::min(dist[v], distances(v, vertices.back())); - auto max = std::max_element(dist.begin(), dist.end()); - vertices.push_back(max - dist.begin()); - epsilons.push_back(*max); - } - epsilons.push_back(0); -} - -inline void compute_edges(const PairDistances& distances, - const VertexVector& vertices, - const EpsilonVector& epsilons, - const DistanceType& multiplier, - EdgeVector& edges) -{ - for (unsigned i = 0; i != vertices.size(); ++i) - for (unsigned j = i + 1; j != vertices.size(); ++j) { - Vertex u = vertices[i]; - Vertex v = vertices[j]; - if (distances(u, v) <= multiplier * epsilons[j - 1]) edges.emplace_back(u, v); - } - std::sort(edges.begin(), edges.end(), - [&distances](const Edge& e1, const Edge& e2) { - return distances(std::get<0>(e1), std::get<1>(e1)) < - distances(std::get<0>(e2), std::get<1>(e2)); - }); -} - -inline void compute_positive_cofaces( - const PairDistances& distances, - const VertexVector& vertices, - const EdgeVector& edges, - const EpsilonVector& epsilons, - const DistanceType& multiplier, - Generator& rips, - short unsigned& skeleton, - unsigned& ce, - unsigned& i, - SimplexSet& cofaces) -{ - cofaces.clear(); - - // Add anything else that needs to be inserted into the complex - while (ce < edges.size()) { - Vertex u, v; - std::tie(u, v) = edges[ce]; - if (distances(u, v) <= multiplier * epsilons[i - 1]) - ++ce; - else - break; - // std::cout << "Adding cofaces of " << u << ' ' << v << std::endl; - rips.edge_cofaces( - u, v, - skeleton, - multiplier * epsilons[i - 1], - [&cofaces](Simplex&& s) { cofaces.insert(s); }, - vertices.begin(), - vertices.begin() + i + 1); - } -} - -inline void compute_negative_cofaces( - const VertexVector& vertices, - const EpsilonVector& epsilons, - const DistanceType& multiplier, - Generator& rips, - short unsigned& skeleton, - unsigned& i, - SimplexSet& cofaces) -{ - cofaces.clear(); - rips.vertex_cofaces( - vertices[i], - skeleton, - multiplier * epsilons[i - 1], - [&cofaces](Simplex&& s) { cofaces.insert(s); }, - vertices.begin(), - vertices.begin() + i + 1); - // std::cout << "Total cofaces: " << cofaces.size() << std::endl; -} - -inline unsigned int build_rips_zigzag_filtration(std::vector > &simpls, - std::vector& dirs, - unsigned int numberOfPoints, - int seed = -1, - short unsigned skeleton = 2, - DistanceType multiplier = 6) -{ - // std::cout << "Building filtration" << std::endl; - unsigned int numberOfSimplices = 0; - - PointContainer points = compute_points(numberOfPoints, seed); - - // Construct distances and Rips generator - PairDistances distances(points); - Generator rips(distances); - - // Order vertices and epsilons (in maxmin fashion) - VertexVector vertices; - EpsilonVector epsilons; - EdgeVector edges; - - compute_vertices_and_epsilons(distances, vertices, epsilons); - - // Generate and sort all the edges - compute_edges(distances, vertices, epsilons, multiplier, edges); - - // Insert vertices - for (auto v : vertices) { - // Add a vertex - simpls.push_back({static_cast(v)}); - dirs.push_back(true); - ++numberOfSimplices; - } - - // Process vertices - // dlog::progress progress(vertices.size()); - unsigned ce = 0; // index of the current one past last edge in the complex - SimplexSet cofaces; // record the cofaces of all the simplices that need to be removed and reinserted - - for (unsigned stage = 0; stage != vertices.size() - 1; ++stage) { - unsigned i = vertices.size() - 1 - stage; - - /* Increase epsilon */ - compute_positive_cofaces(distances, vertices, edges, epsilons, multiplier, rips, skeleton, ce, i, cofaces); - - for (auto& s : cofaces) { - // std::cout << "Inserting: " << s << std::endl; - simpls.emplace_back(s.begin(), s.end()); - dirs.push_back(true); - ++numberOfSimplices; - } - - /* Remove the vertex */ - // std::cout << "Removing vertex: " << vertices[i] << std::endl; - compute_negative_cofaces(vertices, epsilons, multiplier, rips, skeleton, i, cofaces); - - for (auto& s : cofaces | ba::reversed) { - // std::cout << "Removing: " << s << std::endl; - simpls.emplace_back(s.begin(), s.end()); - dirs.push_back(false); - } - - // ++progress; - } - - return numberOfSimplices; - // std::cout << "Finished" << std::endl; -} diff --git a/src/Zigzag_persistence/example/CMakeLists.txt b/src/Zigzag_persistence/example/CMakeLists.txt index c9814e2768..dd1f88e1d2 100644 --- a/src/Zigzag_persistence/example/CMakeLists.txt +++ b/src/Zigzag_persistence/example/CMakeLists.txt @@ -13,24 +13,6 @@ endif() file(COPY "zigzag_filtration_example.txt" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) add_test(NAME Zigzag_persistence_example_zzfiltration_from_file COMMAND $ "${CMAKE_CURRENT_BINARY_DIR}/zigzag_filtration_example.txt") -# add_executable ( comp comparison_for_tests.cpp ./ext_zz/fzz/fzz.cpp ) -# target_include_directories(comp PUBLIC ./ext_zz) -# target_compile_options(comp PUBLIC "-fopenmp") -# target_link_options(comp PUBLIC "-fopenmp") -# add_executable ( Zigzag_persistence_example_rips_zigzag_filtration example_rips_zigzag_filtration.cpp ) -# target_include_directories(Zigzag_persistence_example_rips_zigzag_filtration PUBLIC ./ext_zz) -# target_compile_options(Zigzag_persistence_example_rips_zigzag_filtration PUBLIC "-fopenmp") -# target_link_options(Zigzag_persistence_example_rips_zigzag_filtration PUBLIC "-fopenmp") - -# add_executable ( rips example_rips_zigzag_filtration.cpp ) -# target_include_directories(rips PUBLIC ./ext_zz) -# target_compile_options(rips PUBLIC "-fopenmp") -# target_link_options(rips PUBLIC "-fopenmp") - -# add_executable ( rips_old example_rips_zigzag_filtration.cpp ) -# target_include_directories(rips_old PUBLIC ./ext_zz) -# target_compile_options(rips_old PUBLIC "-fopenmp") -# target_link_options(rips_old PUBLIC "-fopenmp") diff --git a/src/Zigzag_persistence/example/comparison_for_tests.cpp b/src/Zigzag_persistence/example/comparison_for_tests.cpp deleted file mode 100644 index a2f972966c..0000000000 --- a/src/Zigzag_persistence/example/comparison_for_tests.cpp +++ /dev/null @@ -1,440 +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): Hannah Schreiber - * - * Copyright (C) 2023 Inria - * - * Modification(s): - * - YYYY/MM Author: Description of the modification - */ - -#include -#include -#include -#include -#include // for pair -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "rips-zigzag-dionysus.h" - -using ST = Gudhi::Simplex_tree; -using Gudhi::persistence_matrix::Zigzag_options; -using CT = Gudhi::persistence_matrix::Column_types; -using ZP = Gudhi::zigzag_persistence::Zigzag_persistence >; -using Vertex_handle = ST::Vertex_handle; -using Filtration_value = ST::Filtration_value; -using interval_filtration = ZP::filtration_value_interval; - -using DField = dionysus::Z2Field; -using Simplex = dionysus::Simplex<>; -using DFiltration = dionysus::Filtration; -using DZZ = dionysus::ZigzagPersistence; -using DIndex = typename DZZ::Index; -using DChain = dionysus::ChainEntry; -using DIChain = dionysus::ChainEntry; - -void print_complex(ZP& zp){ - std::clog << std::endl << "Current complex:" << std::endl; - zp.print_current_complex(); -} - -// void print_barcode(ZP& zp){ -// std::clog << std::endl << "Current barcode:" << std::endl; -// for (auto& bar : zp.persistence_diagram()){ -// std::clog << std::floor(bar.birth()) << " - "; -// if (bar.death() == std::numeric_limits::infinity()){ -// std::clog << "inf"; -// } else { -// std::clog << std::floor(bar.death()); -// } -// std::clog << " (" << bar.dim() << ")\n"; -// } -// } - -std::vector< std::pair > print_indices(ZP& zp, unsigned int numberOfSimplices){ - std::set essentials; - std::vector< std::pair > res; - - for (unsigned int i = 0; i < numberOfSimplices; ++i){ - essentials.insert(essentials.end(), i); - } - - for (auto& bar : zp.get_index_persistence_diagram()){ - // std::clog << bar.birth() << " - "; - // std::clog << bar.death(); - // std::clog << " (" << bar.dim() << ")\n"; - res.emplace_back(bar.birth(), bar.death()); - essentials.erase(bar.birth()); - essentials.erase(bar.death()); - } - - for (unsigned int v : essentials){ - // std::clog << v << " - "; - // std::clog << "inf\n"; - res.emplace_back(v, numberOfSimplices); - } - - return res; -} - -// std::vector > get_simplices() -// { -// return { -// {0}, -// {1}, -// {2}, -// {0,1}, -// {0,2}, -// {3}, -// {1,2}, -// {4}, -// {3,4}, -// {5}, -// {0,1,2}, -// {4,5}, -// {3,5}, -// {3,4,5}, -// {0,1,2}, //r -// {3,4,5}, //r -// {1,4}, -// {0,1,2}, -// {2,4}, -// {3,4,5}, -// {0,4}, -// {0,2,4}, -// {1,2,4}, -// {0,1,4}, -// {3,4,5}, //r -// {3,4}, //r -// {3,5}, //r -// {0,1,2,4}}; -// } - -// std::vector get_filtration_values() -// { -// return { -// 0, 0, 0, -// 1, 1, 1, -// 2, 2, 2, -// 3, 3, 3, 3, -// 4, -// 5, -// 6, 6, 6, -// 7, 7, 7, 7, 7, 7, -// 8, -// 9, 9, 9 -// }; -// } - -// std::vector get_directions() -// { -// return { -// true, -// true, -// true, -// true, -// true, -// true, -// true, -// true, -// true, -// true, -// true, -// true, -// true, -// true, -// false, -// false, -// true, -// true, -// true, -// true, -// true, -// true, -// true, -// true, -// false, -// false, -// false, -// true}; -// } - -std::vector< std::pair > compute_with_gudhi( - const std::vector >& simplices, - const std::vector& dirs) -{ - ZP zp(simplices.size()); - - std::cout << "====================== Gudhi =====================\n"; - - // std::vector filValues = get_filtration_values(); - std::vector filValues(simplices.size(), 1.0); - - auto start = simplices.begin(); - auto filIt = filValues.begin(); - unsigned int i = 0; - - while (start != simplices.end()){ - unsigned int c = 1; - auto end = start + 1; - ++i; - while (end != simplices.end() && dirs[i - 1] == dirs[i]) { - ++end; - ++i; - ++c; - } - - if (dirs[i - 1]){ - zp.insert_simplices_contiguously( - start, end, filIt); - } else { - zp.remove_simplices_contiguously( - start, end, filIt); - } - - start = end; - filIt += c; - // print_complex(zp); - } - - std::cout << "==================================================\n"; - - // print_complex(zp); - // print_barcode(zp); - return print_indices(zp, i); -} - -std::vector< std::pair > compute_with_dionysus( - const std::vector >& simplices, - const std::vector& dirs) -{ - DField k; - std::unordered_map indices; - DZZ persistence(k); - std::vector< std::pair > res; - - std::cout << "==================== Dionysus ====================\n"; - - std::set essentials; - - unsigned int op = 0; - unsigned int idx = 0; - - for (const std::vector& simplex : simplices){ - Simplex c(simplex); - DIndex pair; - if (dirs[op]) { - indices.try_emplace(c, idx++); - // int dim = boost::distance(c.boundary(persistence.field())); - // dim = dim == 0 ? 0 : dim -1; - // fmt::print("[{}] Adding: {} : {}\n", op, c, dim); - pair = persistence.add(c.boundary(persistence.field()) | - boost::adaptors::transformed([&indices](const DChain& e) { - return DIChain(e.element(), indices.find(e.index())->second); - })); - } else { - // fmt::print("[{}] Removing: {} : {}\n", op, c, boost::distance(c.boundary(persistence.field())) - 1); - auto idxIt = indices.find(c); - pair = persistence.remove(idxIt->second); - indices.erase(idxIt); - } - - if (pair != DZZ::unpaired()) { - // fmt::print("{} - {}\n", pair, op); - res.emplace_back(pair, op); - essentials.erase(pair); - } else { - essentials.insert(essentials.end(), op); - } - op++; - } - - for (unsigned int v : essentials){ - // fmt::print("{} - inf\n", v); - res.emplace_back(v, op); - } - - std::cout << "==================================================\n"; - - return res; -} - -std::vector< std::tuple > compute_with_fzz( - const std::vector >& simplices, - const std::vector& dirs) -{ - std::vector< std::tuple > persistence; - // std::vector< std::pair > res; - FZZ::FastZigzag fzz; - - std::cout << "======================= FZZ ======================\n"; - - fzz.compute(simplices, dirs, &persistence); - - std::sort(persistence.begin(), persistence.end(), - [](const std::tuple& p1, const std::tuple& p2){ - if (std::get<1>(p1) == std::get<1>(p2)){ - return std::get<0>(p1) < std::get<0>(p2); - } - - return std::get<1>(p1) < std::get<1>(p2); - }); - - // for (auto& t : persistence) - // res.emplace_back(std::get<0>(t), std::get<1>(t)); - - // for (const auto& e : persistence) { - // std::cout << (std::get<0>(e) - 1) << " - "; - // if (static_cast(std::get<1>(e)) == simplices.size()) std::cout << "inf"; - // else std::cout << std::get<1>(e); - // std::cout << " (" << std::get<2>(e) << ")" << std::endl; - // } - - std::cout << "==================================================\n"; - - return persistence; -} - -bool are_equal(const std::vector >& gudhiRes, - const std::vector >& dioRes) -{ - if (gudhiRes.size() != dioRes.size()) return false; - - for (unsigned int i = 0; i < gudhiRes.size(); ++i){ - if (gudhiRes[i].first != dioRes[i].first || gudhiRes[i].second != dioRes[i].second) - return false; - } - - return true; -} - -bool are_equal(const std::vector >& gudhiRes, - const std::vector >& fzzRes) -{ - if (gudhiRes.size() != fzzRes.size()) return false; - - for (unsigned int i = 0; i < gudhiRes.size(); ++i){ - if (static_cast(gudhiRes[i].first) != std::get<0>(fzzRes[i]) - 1 || static_cast(gudhiRes[i].second) != std::get<1>(fzzRes[i])) - return false; - } - - return true; -} - -void print(const std::vector >& res, unsigned int infValue){ - for (const auto& p : res) { - std::cout << p.first << " - "; - if (p.second == infValue) std::cout << "inf"; - else std::cout << p.second; - std::cout << std::endl; - } -} - -void print(const std::vector >& res, int infValue){ - for (const auto& e : res) { - std::cout << (std::get<0>(e) - 1) << " - "; - if (std::get<1>(e) == infValue) std::cout << "inf"; - else std::cout << std::get<1>(e); - std::cout << std::endl; - } -} - -void print_differences(const std::vector >& gudhiRes, - const std::vector >& dioRes, - unsigned int infValue) -{ - for (unsigned int i = 0; i < gudhiRes.size(); ++i){ - if (gudhiRes[i].first != dioRes[i].first || gudhiRes[i].second != dioRes[i].second){ - std::string dg = gudhiRes[i].second == infValue ? "inf" : std::to_string(gudhiRes[i].second); - std::string dd = dioRes[i].second == infValue ? "inf" : std::to_string(dioRes[i].second); - std::cout << "[" << i << "] " - << gudhiRes[i].first << " - " << dg - << " / " - << dioRes[i].first << " - " << dd - << "\n"; - } - } -} - -void print_differences(const std::vector >& gudhiRes, - const std::vector >& fzzRes, - int infValue) -{ - for (unsigned int i = 0; i < gudhiRes.size(); ++i){ - if (static_cast(gudhiRes[i].first) != std::get<0>(fzzRes[i]) || static_cast(gudhiRes[i].second) != std::get<1>(fzzRes[i])){ - std::string dg = static_cast(gudhiRes[i].second) == infValue ? "inf" : std::to_string(gudhiRes[i].second); - std::string dd = std::get<1>(fzzRes[i]) == infValue ? "inf" : std::to_string(std::get<1>(fzzRes[i])); - std::cout << "[" << i << "] " - << gudhiRes[i].first << " - " << dg - << " / " - << std::get<0>(fzzRes[i]) << " - " << dd - << "\n"; - } - } -} - - - -int main(int argc, char* const argv[]) { - if (argc < 2 || argc > 3) { - std::cout << "Wrong arguments.\n"; - return 0; - } - - // std::vector > simplices = get_simplices(); - // std::vector dirs = get_directions(); - std::vector > simplices; - std::vector dirs; - - unsigned int numberOfPoints = std::stoi(argv[1]); - int seed = -1; - - if (argc == 3) - seed = std::stoi(argv[2]); - - unsigned int numberOfSimplices = build_rips_zigzag_filtration(simplices, dirs, numberOfPoints, seed); - std::cout << "\n" << "numberOfSimplices: " << numberOfSimplices << "\n"; - - auto gudhiRes = compute_with_gudhi(simplices, dirs); - auto dioRes = compute_with_dionysus(simplices, dirs); - auto fzzRes = compute_with_fzz(simplices, dirs); - - std::cout << "Res sizes: " << gudhiRes.size() << ", " << dioRes.size() << ", " << fzzRes.size() << "\n"; - - bool firstRes = are_equal(gudhiRes, dioRes); - if (!firstRes){ - std::cout << "------------------------ Gudhi and Dionysus results are not equal!\n"; - // print(gudhiRes, numberOfSimplices); - // std::cout << "------------------------\n"; - // print(dioRes, numberOfSimplices); - print_differences(gudhiRes, dioRes, numberOfSimplices); - } else { - std::cout << "+++++++++++++++++++++++++ Gudhi and Dionysus results are equal.\n"; - } - - if (!are_equal(gudhiRes, fzzRes)){ - std::cout << "------------------------ Gudhi and FZZ results are not equal!\n"; - // if (firstRes) { - // print(gudhiRes, numberOfSimplices); - // std::cout << "------------------------\n"; - // } - // print(fzzRes, numberOfSimplices); - } else { - std::cout << "+++++++++++++++++++++++++ Gudhi and FZZ results are equal.\n"; - } - - return 0; -} diff --git a/src/Zigzag_persistence/example/example_rips_zigzag_filtration.cpp b/src/Zigzag_persistence/example/example_rips_zigzag_filtration.cpp deleted file mode 100644 index 88298890c7..0000000000 --- a/src/Zigzag_persistence/example/example_rips_zigzag_filtration.cpp +++ /dev/null @@ -1,158 +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): Hannah Schreiber - * - * Copyright (C) 2023 Inria - * - * Modification(s): - * - YYYY/MM Author: Description of the modification - */ - -#include // atoi, getenv -#include // size_t -#include // printf -#include // strcmp -#include // read, write -#include -#include - -#include -#include -#include - -#include -#include // for pair -#include - -#include "rips-zigzag-dionysus.h" - -using ST = Gudhi::Simplex_tree; -using Gudhi::persistence_matrix::Zigzag_options; -using CT = Gudhi::persistence_matrix::Column_types; -using ZP = Gudhi::zigzag_persistence::Zigzag_persistence >; -// using coltype = Gudhi::zigzag_persistence::Zigzag_persistence_collist; -// using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; -using Vertex_handle = ST::Vertex_handle; -using Filtration_value = ST::Filtration_value; -using interval_filtration = ZP::filtration_value_interval; - -std::vector< std::pair > print_indices(ZP& zp, unsigned int numberOfSimplices){ - std::set essentials; - std::vector< std::pair > res; - - for (unsigned int i = 0; i < numberOfSimplices; ++i){ - essentials.insert(essentials.end(), i); - } - - for (auto& bar : zp.get_index_persistence_diagram()){ - res.emplace_back(bar.birth(), bar.death()); - essentials.erase(bar.birth()); - essentials.erase(bar.death()); - } - - for (unsigned int v : essentials){ - res.emplace_back(v, numberOfSimplices); - } - - return res; -} - -std::vector< std::pair > compute_with_gudhi( - const std::vector >& simplices, - const std::vector& dirs) -{ - ZP zp(simplices.size()); - - // std::cout << "====================== Gudhi =====================\n"; - - std::vector filValues(simplices.size(), 1.0); - - auto start = simplices.begin(); - auto filIt = filValues.begin(); - unsigned int i = 0; - - while (start != simplices.end()){ - unsigned int c = 1; - auto end = start + 1; - ++i; - while (end != simplices.end() && dirs[i - 1] == dirs[i]) { - ++end; - ++i; - ++c; - } - - if (dirs[i - 1]){ - zp.insert_simplices_contiguously( - start, end, filIt); - } else { - zp.remove_simplices_contiguously( - start, end, filIt); - } - - start = end; - filIt += c; - } - - // std::cout << "==================================================\n"; - - return print_indices(zp, i); -} - -int main(int argc, char* const argv[]) { - if (argc < 2 || argc > 3) { - std::cout << "Wrong arguments.\n"; - return 0; - } - - int perf_ctl_fd = open("/tmp/perf_ctl.fifo",O_WRONLY); - int perf_ctl_ack_fd = open("/tmp/perf_ctl_ack.fifo",O_RDONLY); - char ack[5]; - std::cout << "perf_ctl_fd: " << perf_ctl_fd << "\n"; - std::cout << "perf_ctl_ack_fd: " << perf_ctl_ack_fd << "\n"; - - unsigned int numberOfPoints = std::stoi(argv[1]); - int seed = -1; - if (argc == 3) - seed = std::stoi(argv[2]); - - std::vector > simplices; - std::vector dirs; - - unsigned int numberOfSimplices = build_rips_zigzag_filtration(simplices, dirs, numberOfPoints, seed); - std::cout << "Filtration size: " << simplices.size() << "\n"; - std::cout << "Number of simplices: " << numberOfSimplices << "\n"; - - // Start the performance counter and read the ack - if (perf_ctl_fd != -1){ - write(perf_ctl_fd, "enable\n", 8); - read(perf_ctl_ack_fd, ack, 5); - if(std::strcmp(ack, "ack\n") != 0){ - std::cout << "No acknowledgment\n"; - return 1; - } - } - - Gudhi::Clock time("Zigzag Rips"); - /* auto res = */compute_with_gudhi(simplices, dirs); - time.end(); - std::cout << time; - - // Stop the performance counter and read the ack - if (perf_ctl_fd != -1){ - write(perf_ctl_fd, "disable\n", 9); - read(perf_ctl_ack_fd, ack, 5); - if(std::strcmp(ack, "ack\n") != 0){ - std::cout << "No acknowledgment\n"; - return 1; - } - } - - // for (const auto& p : res) { - // std::cout << p.first << " - "; - // if (p.second == numberOfSimplices) std::cout << "inf"; - // else std::cout << p.second; - // std::cout << std::endl; - // } - - return 0; -} diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/chain.h b/src/Zigzag_persistence/example/ext_zz/dionysus/chain.h deleted file mode 100644 index 00c983623a..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/chain.h +++ /dev/null @@ -1,153 +0,0 @@ -#ifndef DIONYSUS_CHAIN_H -#define DIONYSUS_CHAIN_H - -#include -#include -#include - -#include "fields/z2.h" - -namespace dionysus -{ - -template -struct FieldElement -{ - typedef typename Field::Element Element; - FieldElement(Element e_): - e(e_) {} - Element element() const { return e; } - void set_element(Element e_) { e = e_; } - Element e; -}; - -template<> -struct FieldElement -{ - typedef Z2Field::Element Element; - FieldElement(Element) {} - Element element() const { return Z2Field::id(); } - void set_element(Element) {} -}; - -template -struct ChainEntry: public FieldElement, public Extra... -{ - typedef Field_ Field; - typedef Index_ Index; - - typedef FieldElement Parent; - typedef typename Parent::Element Element; - - ChainEntry(): Parent(Element()), i(Index()) {} // need for serialization - - ChainEntry(ChainEntry&& other) = default; - ChainEntry(const ChainEntry& other) = default; - ChainEntry& operator=(ChainEntry&& other) = default; - - ChainEntry(Element e_, const Index& i_): - Parent(e_), i(i_) {} - - ChainEntry(Element e_, Index&& i_): - Parent(e_), i(std::move(i_)) {} - - const Index& index() const { return i; } - Index& index() { return i; } - - // debug - bool operator==(const ChainEntry& other) const { return i == other.i; } - - Index i; -}; - -template -struct Chain -{ - struct Visitor - { - template - void first(Iter it) const {} - - template - void second(Iter it) const {} - - template - void equal_keep(Iter it) const {} - - template - void equal_drop(Iter it) const {} - }; - - // x += a*y - template - static void addto(C1& x, typename Field::Element a, const C2& y, const Field& field, const Cmp& cmp, const Visitor_& = Visitor_()); -}; - -template -struct Chain> -{ - struct Visitor - { - template - void first(Iter it) const {} - - template - void second(Iter it) const {} - - template - void equal_keep(Iter it) const {} - - template - void equal_drop(Iter it) const {} - }; - - // x += a*y - template - static void addto(std::list& x, typename Field::Element a, const C2& y, - const Field& field, const Cmp& cmp, const Visitor_& visitor = Visitor_()); -}; - - -template -struct Chain> -{ - struct Visitor - { - template - void first(Iter it) const {} - - template - void second(Iter it) const {} - - template - void equal_keep(Iter it) const {} - - template - void equal_drop(Iter it) const {} - }; - - // x += a*y - template - static void addto(std::set& x, typename Field::Element a, const C2& y, - const Field& field, const Cmp& cmp, const Visitor_& = Visitor_()); - - template - static void addto(std::set& x, typename Field::Element a, T&& y, - const Field& field, const Cmp& cmp, const Visitor_& = Visitor_()); -}; - -} - -//namespace std -//{ -// template -// void swap(::dionysus::ChainEntry& x, ::dionysus::ChainEntry& y) -// { -// std::swap(x.e, y.e); -// std::swap(x.i, y.i); -// } -//} - -#include "chain.hpp" - -#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/chain.hpp b/src/Zigzag_persistence/example/ext_zz/dionysus/chain.hpp deleted file mode 100644 index 4da9f44615..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/chain.hpp +++ /dev/null @@ -1,188 +0,0 @@ -template -template -void -dionysus::Chain>:: -addto(std::list& x, typename Field::Element a, const C2& y, const Field& field, const Cmp& cmp, const Visitor_& visitor) -{ - typedef typename Field::Element Element; - - auto cur_x = std::begin(x), - end_x = std::end(x); - auto cur_y = std::begin(y), - end_y = std::end(y); - - while (cur_x != end_x && cur_y != end_y) - { - if (cmp(cur_x->index(), cur_y->index())) - { - visitor.first(cur_x++); - } else if (cmp(cur_y->index(), cur_x->index())) - { - // multiply and add - Element ay = field.mul(a, cur_y->element()); - auto nw_x = x.insert(cur_x, *cur_y); - nw_x->set_element(ay); - ++cur_y; - visitor.second(nw_x); - } else - { - Element ay = field.mul(a, cur_y->element()); - Element r = field.add(cur_x->element(), ay); - if (field.is_zero(r)) - { - visitor.equal_drop(cur_x); - x.erase(cur_x++); - } - else - { - cur_x->set_element(r); - visitor.equal_keep(cur_x); - ++cur_x; - } - ++cur_y; - } - } - - for (auto it = cur_y; it != end_y; ++it) - { - Element ay = field.mul(a, it->element()); - x.push_back(*it); - x.back().set_element(ay); - visitor.second(--x.end()); - } -} - -template -template -void -dionysus::Chain>:: -addto(std::set& x, typename Field::Element a, const C2& y, const Field& field, const Cmp&, const Visitor_& visitor) -{ - typedef typename Field::Element Element; - - auto cur_y = std::begin(y), - end_y = std::end(y); - - while (cur_y != end_y) - { - auto cur_x = x.find(*cur_y); - if (cur_x == x.end()) - { - auto nw = x.insert(*cur_y).first; - Element ay = field.mul(a, nw->element()); - const_cast(*nw).set_element(ay); - visitor.second(nw); - } else - { - Element ay = field.mul(a, cur_y->element()); - Element r = field.add(cur_x->element(), ay); - if (field.is_zero(r)) - { - visitor.equal_drop(cur_x); - x.erase(cur_x); - } - else - { - const_cast(*cur_x).set_element(r); - visitor.equal_keep(cur_x); - } - } - ++cur_y; - } -} - -template -template -void -dionysus::Chain>:: -addto(std::set& x, typename Field::Element a, T&& y, const Field& field, const Cmp&, const Visitor_& visitor) -{ - typedef typename Field::Element Element; - - auto cur_x = x.find(y); - if (cur_x == x.end()) - { - auto nw = x.insert(std::move(y)).first; - Element ay = field.mul(a, nw->element()); - const_cast(*nw).set_element(ay); - visitor.second(nw); - } else - { - Element ay = field.mul(a, y.element()); - Element r = field.add(cur_x->element(), ay); - if (field.is_zero(r)) - { - visitor.equal_drop(cur_x); - x.erase(cur_x); - } - else - { - const_cast(*cur_x).set_element(r); - visitor.equal_keep(cur_x); - } - } -} - -template -template -void -dionysus::Chain:: -addto(C1& x, typename Field::Element a, const C2& y, const Field& field, const Cmp& cmp, const Visitor_& visitor) -{ - typedef typename Field::Element Element; - - C1 res; - - auto cur_x = std::begin(x), - end_x = std::end(x); - auto cur_y = std::begin(y), - end_y = std::end(y); - - while (cur_x != end_x && cur_y != end_y) - { - if (cmp(*cur_x, *cur_y)) - { - res.emplace_back(std::move(*cur_x)); - visitor.first(--res.end()); - ++cur_x; - } else if (cmp(*cur_y, *cur_x)) - { - // multiply and add - Element ay = field.mul(a, cur_y->element()); - res.emplace_back(ay, cur_y->index()); - visitor.second(--res.end()); - ++cur_y; - } else - { - Element ay = field.mul(a, cur_y->element()); - Element r = field.add(cur_x->element(), ay); - if (field.is_zero(r)) - visitor.equal_drop(cur_x); - else - { - res.emplace_back(std::move(*cur_x)); - res.back().set_element(r); - visitor.equal_keep(--res.end()); - } - ++cur_x; - ++cur_y; - } - } - - while (cur_y != end_y) - { - Element ay = field.mul(a, cur_y->element()); - res.emplace_back(ay, cur_y->index()); - visitor.second(--res.end()); - ++cur_y; - } - - while (cur_x != end_x) - { - res.emplace_back(std::move(*cur_x)); - visitor.first(--res.end()); - ++cur_x; - } - - x.swap(res); -} diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/clearing-reduction.h b/src/Zigzag_persistence/example/ext_zz/dionysus/clearing-reduction.h deleted file mode 100644 index 8651e9a69a..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/clearing-reduction.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef DIONYSUS_CLEARING_REDUCTION_H -#define DIONYSUS_CLEARING_REDUCTION_H - -namespace dionysus -{ - -// Mid-level interface -template -class ClearingReduction -{ - public: - using Persistence = Persistence_; - using Field = typename Persistence::Field; - using Index = typename Persistence::Index; - - public: - ClearingReduction(Persistence& persistence): - persistence_(persistence) {} - - template - void operator()(const Filtration& f, const Relative& relative, const ReportPair& report_pair, const Progress& progress); - - template - void operator()(const Filtration& f, const ReportPair& report_pair); - - template - void operator()(const Filtration& f) { return (*this)(f, &no_report_pair); } - - static void no_report_pair(int, Index, Index) {} - static void no_progress() {} - - const Persistence& - persistence() const { return persistence_; } - Persistence& persistence() { return persistence_; } - - private: - Persistence& persistence_; -}; - -} - -#include "clearing-reduction.hpp" - -#endif - diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/clearing-reduction.hpp b/src/Zigzag_persistence/example/ext_zz/dionysus/clearing-reduction.hpp deleted file mode 100644 index ceac11879d..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/clearing-reduction.hpp +++ /dev/null @@ -1,60 +0,0 @@ -#include -#include - -#include -namespace ba = boost::adaptors; - -template -template -void -dionysus::ClearingReduction

:: -operator()(const Filtration& filtration, const ReportPair& report_pair) -{ - using Cell = typename Filtration::Cell; - (*this)(filtration, [](const Cell&) { return false; }, report_pair, &no_progress); -} - -template -template -void -dionysus::ClearingReduction

:: -operator()(const Filtration& filtration, const Relative& relative, const ReportPair& report_pair, const Progress& progress) -{ - persistence_.resize(filtration.size()); - - // sort indices by decreasing dimension - std::vector indices(filtration.size()); - std::iota(indices.begin(), indices.end(), 0); - std::stable_sort(indices.begin(), indices.end(), - [&filtration](size_t x, size_t y) - { return filtration[x].dimension() > filtration[y].dimension(); }); - - typedef typename Filtration::Cell Cell; - typedef ChainEntry CellChainEntry; - typedef ChainEntry ChainEntry; - - for(size_t i : indices) - { - progress(); - const auto& c = filtration[i]; - - if (relative(c)) - { - persistence_.set_skip(i); - continue; - } - - if (persistence_.pair(i) != persistence_.unpaired()) - continue; - - persistence_.set(i, c.boundary(persistence_.field()) | - ba::filtered([relative](const CellChainEntry& e) { return !relative(e.index()); }) | - ba::transformed([this,&filtration](const CellChainEntry& e) - { return ChainEntry(e.element(), filtration.index(e.index())); })); - - Index pair = persistence_.reduce(i); - if (pair != persistence_.unpaired()) - report_pair(c.dimension(), pair, i); - } -} - diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/cnpy.h b/src/Zigzag_persistence/example/ext_zz/dionysus/cnpy.h deleted file mode 100644 index b11013b9d7..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/cnpy.h +++ /dev/null @@ -1,241 +0,0 @@ -//Copyright (C) 2011 Carl Rogers -//Released under MIT License -//license available in LICENSE file, or at http://www.opensource.org/licenses/mit-license.php - -#ifndef LIBCNPY_H_ -#define LIBCNPY_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cnpy { - - struct NpyArray { - char* data; - std::vector shape; - unsigned int word_size; - bool fortran_order; - void destruct() {delete[] data;} - }; - - struct npz_t : public std::map - { - void destruct() - { - npz_t::iterator it = this->begin(); - for(; it != this->end(); ++it) (*it).second.destruct(); - } - }; - - char BigEndianTest(); - char map_type(const std::type_info& t); - template std::vector create_npy_header(const T* data, const unsigned int* shape, const unsigned int ndims); - void parse_npy_header(FILE* fp,unsigned int& word_size, unsigned int*& shape, unsigned int& ndims, bool& fortran_order); - void parse_zip_footer(FILE* fp, unsigned short& nrecs, unsigned int& global_header_size, unsigned int& global_header_offset); - npz_t npz_load(std::string fname); - NpyArray npz_load(std::string fname, std::string varname); - NpyArray npy_load(std::string fname); - - template std::vector& operator+=(std::vector& lhs, const T rhs) { - //write in little endian - for(char byte = 0; byte < sizeof(T); byte++) { - char val = *((char*)&rhs+byte); - lhs.push_back(val); - } - return lhs; - } - - template<> std::vector& operator+=(std::vector& lhs, const std::string rhs); - template<> std::vector& operator+=(std::vector& lhs, const char* rhs); - - - template std::string tostring(T i, int pad = 0, char padval = ' ') { - std::stringstream s; - s << i; - return s.str(); - } - - template void npy_save(std::string fname, const T* data, const unsigned int* shape, const unsigned int ndims, std::string mode = "w") { - FILE* fp = NULL; - - if(mode == "a") fp = fopen(fname.c_str(),"r+b"); - - if(fp) { - //file exists. we need to append to it. read the header, modify the array size - unsigned int word_size, tmp_dims; - unsigned int* tmp_shape = 0; - bool fortran_order; - parse_npy_header(fp,word_size,tmp_shape,tmp_dims,fortran_order); - assert(!fortran_order); - - if(word_size != sizeof(T)) { - std::cout<<"libnpy error: "< header = create_npy_header(data,tmp_shape,ndims); - fwrite(&header[0],sizeof(char),header.size(),fp); - fseek(fp,0,SEEK_END); - - delete[] tmp_shape; - } - else { - fp = fopen(fname.c_str(),"wb"); - std::vector header = create_npy_header(data,shape,ndims); - fwrite(&header[0],sizeof(char),header.size(),fp); - } - - unsigned int nels = 1; - for(int i = 0;i < ndims;i++) nels *= shape[i]; - - fwrite(data,sizeof(T),nels,fp); - fclose(fp); - } - - template void npz_save(std::string zipname, std::string fname, const T* data, const unsigned int* shape, const unsigned int ndims, std::string mode = "w") - { - //first, append a .npy to the fname - fname += ".npy"; - - //now, on with the show - FILE* fp = NULL; - unsigned short nrecs = 0; - unsigned int global_header_offset = 0; - std::vector global_header; - - if(mode == "a") fp = fopen(zipname.c_str(),"r+b"); - - if(fp) { - //zip file exists. we need to add a new npy file to it. - //first read the footer. this gives us the offset and size of the global header - //then read and store the global header. - //below, we will write the the new data at the start of the global header then append the global header and footer below it - unsigned int global_header_size; - parse_zip_footer(fp,nrecs,global_header_size,global_header_offset); - fseek(fp,global_header_offset,SEEK_SET); - global_header.resize(global_header_size); - size_t res = fread(&global_header[0],sizeof(char),global_header_size,fp); - if(res != global_header_size){ - throw std::runtime_error("npz_save: header read error while adding to existing zip"); - } - fseek(fp,global_header_offset,SEEK_SET); - } - else { - fp = fopen(zipname.c_str(),"wb"); - } - - std::vector npy_header = create_npy_header(data,shape,ndims); - - unsigned long nels = 1; - for (int m=0; m local_header; - local_header += "PK"; //first part of sig - local_header += (unsigned short) 0x0403; //second part of sig - local_header += (unsigned short) 20; //min version to extract - local_header += (unsigned short) 0; //general purpose bit flag - local_header += (unsigned short) 0; //compression method - local_header += (unsigned short) 0; //file last mod time - local_header += (unsigned short) 0; //file last mod date - local_header += (unsigned int) crc; //crc - local_header += (unsigned int) nbytes; //compressed size - local_header += (unsigned int) nbytes; //uncompressed size - local_header += (unsigned short) fname.size(); //fname length - local_header += (unsigned short) 0; //extra field length - local_header += fname; - - //build global header - global_header += "PK"; //first part of sig - global_header += (unsigned short) 0x0201; //second part of sig - global_header += (unsigned short) 20; //version made by - global_header.insert(global_header.end(),local_header.begin()+4,local_header.begin()+30); - global_header += (unsigned short) 0; //file comment length - global_header += (unsigned short) 0; //disk number where file starts - global_header += (unsigned short) 0; //internal file attributes - global_header += (unsigned int) 0; //external file attributes - global_header += (unsigned int) global_header_offset; //relative offset of local file header, since it begins where the global header used to begin - global_header += fname; - - //build footer - std::vector footer; - footer += "PK"; //first part of sig - footer += (unsigned short) 0x0605; //second part of sig - footer += (unsigned short) 0; //number of this disk - footer += (unsigned short) 0; //disk where footer starts - footer += (unsigned short) (nrecs+1); //number of records on this disk - footer += (unsigned short) (nrecs+1); //total number of records - footer += (unsigned int) global_header.size(); //nbytes of global headers - footer += (unsigned int) (global_header_offset + nbytes + local_header.size()); //offset of start of global headers, since global header now starts after newly written array - footer += (unsigned short) 0; //zip file comment length - - //write everything - fwrite(&local_header[0],sizeof(char),local_header.size(),fp); - fwrite(&npy_header[0],sizeof(char),npy_header.size(),fp); - fwrite(data,sizeof(T),nels,fp); - fwrite(&global_header[0],sizeof(char),global_header.size(),fp); - fwrite(&footer[0],sizeof(char),footer.size(),fp); - fclose(fp); - } - - template std::vector create_npy_header(const T* data, const unsigned int* shape, const unsigned int ndims) { - - std::vector dict; - dict += "{'descr': '"; - dict += BigEndianTest(); - dict += map_type(typeid(T)); - dict += tostring(sizeof(T)); - dict += "', 'fortran_order': False, 'shape': ("; - dict += tostring(shape[0]); - for(int i = 1;i < ndims;i++) { - dict += ", "; - dict += tostring(shape[i]); - } - if(ndims == 1) dict += ","; - dict += "), }"; - //pad with spaces so that preamble+dict is modulo 16 bytes. preamble is 10 bytes. dict needs to end with \n - int remainder = 16 - (10 + dict.size()) % 16; - dict.insert(dict.end(),remainder,' '); - dict.back() = '\n'; - - std::vector header; - header += (char) 0x93; - header += "NUMPY"; - header += (char) 0x01; //major version of numpy format - header += (char) 0x00; //minor version of numpy format - header += (unsigned short) dict.size(); - header.insert(header.end(),dict.begin(),dict.end()); - - return header; - } - - -} - -#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/cohomology-persistence.h b/src/Zigzag_persistence/example/ext_zz/dionysus/cohomology-persistence.h deleted file mode 100644 index 8d2019e89d..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/cohomology-persistence.h +++ /dev/null @@ -1,116 +0,0 @@ -#ifndef DIONYSUS_COHOMOLOGY_PERSISTENCE_H -#define DIONYSUS_COHOMOLOGY_PERSISTENCE_H - -#include -#include - -#include -namespace bi = boost::intrusive; - -#include "reduction.h" -#include "chain.h" - -namespace dionysus -{ - -template> -class CohomologyPersistence -{ - public: - typedef Field_ Field; - typedef Index_ Index; - typedef Comparison_ Comparison; - - typedef typename Field::Element FieldElement; - - typedef bi::list_base_hook> auto_unlink_hook; - struct Entry; - struct ColumnHead; - - typedef std::vector Column; - typedef bi::list> Row; - typedef std::list Columns; - typedef typename Columns::iterator ColumnsIterator; - typedef Column Chain; - - using IndexColumn = std::tuple; - - CohomologyPersistence(const Field& field, - const Comparison& cmp = Comparison()): - field_(field), cmp_(cmp) {} - - CohomologyPersistence(Field&& field, - const Comparison& cmp = Comparison()): - field_(std::move(field)), - cmp_(cmp) {} - - CohomologyPersistence(CohomologyPersistence&& other): - field_(std::move(other.field_)), - cmp_(std::move(other.cmp_)), - columns_(std::move(other.columns_)), - rows_(std::move(other.rows_)) {} - - template - Index add(const ChainRange& chain); - - template - IndexColumn add(const ChainRange& chain, bool keep_cocycle); - - // TODO: no skip support for now - bool skip(Index) const { return false; } - void add_skip() {} - void set_skip(Index, bool flag = true) {} - - const Field& field() const { return field_; } - const Columns& columns() const { return columns_; } - void reserve(size_t s) { rows_.reserve(s); } - - struct AddtoVisitor; - - static const Index unpaired() { return Reduction::unpaired; } - - private: - Field field_; - Comparison cmp_; - Columns columns_; - std::vector rows_; -}; - - -template -struct CohomologyPersistence::ColumnHead -{ - ColumnHead(Index i): index_(i) {} - - Index index() const { return index_; } - - Index index_; - Column chain; -}; - -template -struct CohomologyPersistence::Entry: - public ChainEntry -{ - typedef ChainEntry Parent; - - Entry(FieldElement e, const Index& i): // slightly dangerous - Parent(e,i) {} - - Entry(FieldElement e, const Index& i, ColumnsIterator it): - Parent(e,i), column(it) {} - - Entry(const Entry& other) = default; - Entry(Entry&& other) = default; - - void unlink() { auto_unlink_hook::unlink(); } - bool is_linked() const { return auto_unlink_hook::is_linked(); } - - ColumnsIterator column; // TODO: I really don't like this overhead -}; - -} - -#include "cohomology-persistence.hpp" - -#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/cohomology-persistence.hpp b/src/Zigzag_persistence/example/ext_zz/dionysus/cohomology-persistence.hpp deleted file mode 100644 index b2334f99e1..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/cohomology-persistence.hpp +++ /dev/null @@ -1,61 +0,0 @@ -template -template -typename dionysus::CohomologyPersistence::Index -dionysus::CohomologyPersistence:: -add(const ChainRange& chain) -{ - return std::get<0>(add(chain, false)); // return just the index -} - - -template -template -typename dionysus::CohomologyPersistence::IndexColumn -dionysus::CohomologyPersistence:: -add(const ChainRange& chain, bool keep_cocycle) -{ - auto entry_cmp = [this](const Entry& e1, const Entry& e2) { return this->cmp_(e1.index(), e2.index()); }; - std::set row_sum(entry_cmp); - for (auto it = std::begin(chain); it != std::end(chain); ++it) - for (auto& re : rows_[it->index()]) - dionysus::Chain::addto(row_sum, it->element(), Entry(re.element(), re.column->index(), re.column), field_, cmp_); - - if (row_sum.empty()) // Birth - { - columns_.emplace_back(rows_.size()); - auto before_end = columns_.end(); - --before_end; - columns_.back().chain.push_back(Entry(field_.id(), rows_.size(), before_end)); - rows_.emplace_back(); - rows_.back().push_back(columns_.back().chain.front()); - return std::make_tuple(unpaired(), Column()); - } else // Death - { - // Select front element in terms of comparison (rows are unsorted) - auto it = std::max_element(std::begin(row_sum), std::end(row_sum), entry_cmp); - - Entry first = std::move(*it); - row_sum.erase(it); - - for (auto& ce : row_sum) - { - FieldElement ay = field_.neg(field_.div(ce.element(), first.element())); - dionysus::Chain::addto(ce.column->chain, ay, first.column->chain, field_, - [this](const Entry& e1, const Entry& e2) - { return this->cmp_(e1.index(), e2.index()); }); - - for (auto& x : ce.column->chain) - { - x.column = ce.column; - rows_[x.index()].push_back(x); - } - } - Index pair = first.column->index(); - Column cocycle; - if (keep_cocycle) - cocycle = std::move(first.column->chain); - columns_.erase(first.column); - rows_.emplace_back(); // useless row; only present to make indices match - return std::make_tuple(pair, cocycle); - } -} diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/common.h b/src/Zigzag_persistence/example/ext_zz/dionysus/common.h deleted file mode 100644 index e012b10539..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/common.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef DIONYSUS_EXAMPLES_COMMON_H -#define DIONYSUS_EXAMPLES_COMMON_H - -#include -#include - -template -void read_points(const std::string& infilename, PointContainer& points) -{ - typedef typename PointContainer::value_type Point; - - std::ifstream in(infilename.c_str()); - std::string line; - while(std::getline(in, line)) - { - if (line[0] == '#') continue; // comment line in the file - std::stringstream linestream(line); - double x; - points.push_back(Point()); - while (linestream >> x) - points.back().push_back(x); - } -} - -#endif // DIONYSUS_EXAMPLES_COMMON_H diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/diagram.h b/src/Zigzag_persistence/example/ext_zz/dionysus/diagram.h deleted file mode 100644 index 04eb29a927..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/diagram.h +++ /dev/null @@ -1,114 +0,0 @@ -#ifndef DIONYSUS_DIAGRAM_H -#define DIONYSUS_DIAGRAM_H - -#include -#include -#include - -namespace dionysus -{ - -template -class Diagram -{ - public: - using Value = Value_; - using Data = Data_; - struct Point: public std::pair - { - using Parent = std::pair; - - Point(Value b, Value d, Data dd): - Parent(b,d), data(dd) {} - - Value birth() const { return Parent::first; } - Value death() const { return Parent::second; } - - // FIXME: temporary hack - Value operator[](size_t i) const { if (i == 0) return birth(); return death(); } - - Data data; - }; - - using Points = std::vector; - using iterator = typename Points::iterator; - using const_iterator = typename Points::const_iterator; - using value_type = Point; - - public: - const_iterator begin() const { return points.begin(); } - const_iterator end() const { return points.end(); } - iterator begin() { return points.begin(); } - iterator end() { return points.end(); } - - const Point& operator[](size_t i) const { return points[i]; } - - size_t size() const { return points.size(); } - void push_back(const Point& p) { points.push_back(p); } - template - void emplace_back(Args&&... args) { points.emplace_back(std::forward(args)...); } - - private: - std::vector points; -}; - -namespace detail -{ - template - struct Diagrams - { - using Value = decltype(std::declval()(std::declval())); - using Data = decltype(std::declval()(std::declval())); - using type = std::vector>; - }; -} - -template -typename detail::Diagrams::type -init_diagrams(const ReducedMatrix& m, const Filtration& f, const GetValue& get_value, const GetData& get_data) -{ - using Result = typename detail::Diagrams::type; - - Result diagrams; - for (typename ReducedMatrix::Index i = 0; i < m.size(); ++i) - { - if (m.skip(i)) - continue; - - auto s = f[i]; - auto d = s.dimension(); - - while (d + 1 > diagrams.size()) - diagrams.emplace_back(); - - auto pair = m.pair(i); - if (pair == m.unpaired()) - { - auto birth = get_value(s); - using Value = decltype(birth); - Value death = std::numeric_limits::infinity(); - diagrams[d].emplace_back(birth, death, get_data(i)); - } else if (pair > i) // positive - { - auto birth = get_value(s); - auto death = get_value(f[pair]); - - // hack to work with coboundaries - auto pd = f[pair].dimension(); - if (pd < d) - { - d = pd; - std::swap(birth, death); - } - - if (birth != death) // skip diagonal - diagrams[d].emplace_back(birth, death, get_data(i)); - } // else negative: do nothing - } - - return diagrams; -} - -} - -#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/distances.h b/src/Zigzag_persistence/example/ext_zz/dionysus/distances.h deleted file mode 100644 index 29cac601af..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/distances.h +++ /dev/null @@ -1,93 +0,0 @@ -#ifndef DIONYSUS_DISTANCES_H -#define DIONYSUS_DISTANCES_H - -#include -#include - -namespace dionysus -{ - -/** - * Class: ExplicitDistances - * Stores the pairwise distances of Distances_ instance passed at construction. - * It's a protypical Distances template argument for the Rips complex. - */ -template -class ExplicitDistances -{ - public: - typedef Distances_ Distances; - typedef size_t IndexType; - typedef typename Distances::DistanceType DistanceType; - - ExplicitDistances(IndexType size): - size_(size), - distances_(size*(size + 1)/2 + size) {} - ExplicitDistances(const Distances& distances); - - DistanceType operator()(IndexType a, IndexType b) const; - DistanceType& operator()(IndexType a, IndexType b); - - size_t size() const { return size_; } - IndexType begin() const { return 0; } - IndexType end() const { return size(); } - - private: - std::vector distances_; - size_t size_; -}; - - -/** - * Class: PairwiseDistances - * Given a Container_ of points and a Distance_, it computes distances between elements - * in the container (given as instances of Index_ defaulted to unsigned) using the Distance_ functor. - * - * Container_ is assumed to be an std::vector. That simplifies a number of things. - */ -template -class PairwiseDistances -{ - public: - typedef Container_ Container; - typedef Distance_ Distance; - typedef Index_ IndexType; - typedef typename Distance::result_type DistanceType; - - - PairwiseDistances(const Container& container, - const Distance& distance = Distance()): - container_(container), distance_(distance) {} - - DistanceType operator()(IndexType a, IndexType b) const { return distance_(container_[a], container_[b]); } - - size_t size() const { return container_.size(); } - IndexType begin() const { return 0; } - IndexType end() const { return size(); } - - private: - const Container& container_; - Distance distance_; -}; - -template -struct L2Distance -{ - typedef Point_ Point; - typedef decltype(Point()[0] + 0) result_type; - - result_type operator()(const Point& p1, const Point& p2) const - { - result_type sum = 0; - for (size_t i = 0; i < p1.size(); ++i) - sum += (p1[i] - p2[i])*(p1[i] - p2[i]); - - return sqrt(sum); - } -}; - -} - -#include "distances.hpp" - -#endif // DIONYSUS_DISTANCES_H diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/distances.hpp b/src/Zigzag_persistence/example/ext_zz/dionysus/distances.hpp deleted file mode 100644 index 9b1f20aa2b..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/distances.hpp +++ /dev/null @@ -1,30 +0,0 @@ -template -dionysus::ExplicitDistances:: -ExplicitDistances(const Distances& distances): - size_(distances.size()), distances_((distances.size() * (distances.size() + 1))/2) -{ - IndexType i = 0; - for (typename Distances::IndexType a = distances.begin(); a != distances.end(); ++a) - for (typename Distances::IndexType b = a; b != distances.end(); ++b) - { - distances_[i++] = distances(a,b); - } -} - -template -typename dionysus::ExplicitDistances::DistanceType -dionysus::ExplicitDistances:: -operator()(IndexType a, IndexType b) const -{ - if (a > b) std::swap(a,b); - return distances_[a*size_ - ((a*(a-1))/2) + (b-a)]; -} - -template -typename dionysus::ExplicitDistances::DistanceType& -dionysus::ExplicitDistances:: -operator()(IndexType a, IndexType b) -{ - if (a > b) std::swap(a,b); - return distances_[a*size_ - ((a*(a-1))/2) + (b-a)]; -} diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/dlog/progress.h b/src/Zigzag_persistence/example/ext_zz/dionysus/dlog/progress.h deleted file mode 100644 index 12bf86a4a2..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/dlog/progress.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef DLOG_PROGRESS_H -#define DLOG_PROGRESS_H - -#include -#include -#include -#include - -namespace dlog -{ - -struct progress -{ - progress(size_t total): - current_(0), total_(total) { show_progress(); } - - progress& operator++() { current_++; if (current_ * 100 / total_ > (current_ - 1) * 100 / total_) show_progress(); check_done(); return *this; } - progress& operator=(size_t cur) { current_ = cur; show_progress(); check_done(); return *this; } - progress& operator()(const std::string& s) { message_ = s; show_progress(); check_done(); return *this; } - template - progress& operator()(const T& x) { std::ostringstream oss; oss << x; return (*this)(oss.str()); } - - inline void show_progress() const; - void check_done() const { if (current_ >= total_) std::cout << "\n" << std::flush; } - - private: - size_t current_, total_; - std::string message_; -}; - -} - -void -dlog::progress:: -show_progress() const -{ - int barWidth = 70; - - std::cout << "["; - int pos = barWidth * current_ / total_; - for (int i = 0; i < barWidth; ++i) - { - if (i < pos) - std::cout << "="; - else if (i == pos) - std::cout << ">"; - else - std::cout << " "; - } - std::cout << "] " << std::setw(3) << current_ * 100 / total_ << "%"; - if (!message_.empty()) - std::cout << " (" << message_ << ")"; - std::cout << "\r"; - std::cout.flush(); -} - -#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/fields/q.h b/src/Zigzag_persistence/example/ext_zz/dionysus/fields/q.h deleted file mode 100644 index 8972ae2b5a..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/fields/q.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef DIONYSUS_Q_H -#define DIONYSUS_Q_H - -#include - -// TODO: eventually need to be able to adaptively switch to arbitrary precision arithmetic - -namespace dionysus -{ - -template -class Q -{ - public: - using BaseElement = Element_; - struct Element - { - BaseElement numerator, denominator; - - bool operator==(Element o) const { return numerator == o.numerator && denominator == o.denominator; } - bool operator!=(Element o) const { return !((*this) == o); } - - friend - std::ostream& operator<<(std::ostream& out, Element e) { out << e.numerator << '/' << e.denominator; return out; } - }; - - Element id() const { return { 1,1 }; } - Element zero() const { return { 0,1 }; } - Element init(BaseElement a) const { return { a,1 }; } - - Element neg(Element a) const { return { -a.numerator, a.denominator }; } - Element add(Element a, Element b) const { Element x { a.numerator*b.denominator + b.numerator*a.denominator, a.denominator*b.denominator }; normalize(x); return x; } - - Element inv(Element a) const { return { a.denominator, a.numerator }; } - Element mul(Element a, Element b) const { Element x { a.numerator*b.numerator, a.denominator*b.denominator }; normalize(x); return x; } - Element div(Element a, Element b) const { return mul(a, inv(b)); } - - bool is_zero(Element a) const { return a.numerator == 0; } - - BaseElement numerator(const Element& x) const { return x.numerator; } - BaseElement denominator(const Element& x) const { return x.denominator; } - - static void normalize(Element& x) - { - BaseElement q = gcd(abs(x.numerator), abs(x.denominator)); - x.numerator /= q; - x.denominator /= q; - if (x.denominator < 0) - { - x.numerator = -x.numerator; - x.denominator = -x.denominator; - } - } - - static BaseElement abs(BaseElement x) { if (x < 0) return -x; return x; } - static BaseElement gcd(BaseElement a, BaseElement b) { if (b < a) return gcd(b,a); while (a != 0) { b %= a; std::swap(a,b); } return b; } - - static bool is_prime(BaseElement x) { return false; } // Ok, since is_prime is only used as a shortcut -}; - -} - -#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/fields/z2.h b/src/Zigzag_persistence/example/ext_zz/dionysus/fields/z2.h deleted file mode 100644 index 6317ace6df..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/fields/z2.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef DIONYSUS_Z2_H -#define DIONYSUS_Z2_H - -namespace dionysus -{ - -class Z2Field -{ - public: - typedef short Element; - - Z2Field() {} - - static Element id() { return 1; } - static Element zero() { return 0; } - static Element init(int a) { return (a % 2 + 2) % 2; } - - Element neg(Element a) const { return 2 - a; } - Element add(Element a, Element b) const { return (a+b) % 2; } - - Element inv(Element a) const { return a; } - Element mul(Element a, Element b) const { return a*b; } - Element div(Element a, Element b) const { return a; } - - bool is_zero(Element a) const { return a == 0; } -}; - -} - -#endif - diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/fields/zp.h b/src/Zigzag_persistence/example/ext_zz/dionysus/fields/zp.h deleted file mode 100644 index c70c61cc87..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/fields/zp.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef DIONYSUS_ZP_H -#define DIONYSUS_ZP_H - -#include - -namespace dionysus -{ - -template -class ZpField -{ - public: - typedef Element_ Element; - - ZpField(Element p); - ZpField(const ZpField& other) = default; - ZpField(ZpField&& other) = default; - - Element id() const { return 1; } - Element zero() const { return 0; } - Element init(int a) const { return (a % p_ + p_) % p_; } - - Element neg(Element a) const { return p_ - a; } - Element add(Element a, Element b) const { return (a+b) % p_; } - - Element inv(Element a) const { while (a < 0) a += p_; return inverses_[a]; } - Element mul(Element a, Element b) const { return (a*b) % p_; } - Element div(Element a, Element b) const { return mul(a, inv(b)); } - - bool is_zero(Element a) const { return (a % p_) == 0; } - - Element prime() const { return p_; } - - private: - Element p_; - std::vector inverses_; -}; - -template -ZpField:: -ZpField(Element p): - p_(p), inverses_(p_) -{ - for (Element i = 1; i < p_; ++i) - for (Element j = 1; j < p_; ++j) - if (mul(i,j) == 1) - { - inverses_[i] = j; - break; - } -} - -} - -#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/filtration.h b/src/Zigzag_persistence/example/ext_zz/dionysus/filtration.h deleted file mode 100644 index caf871cdfb..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/filtration.h +++ /dev/null @@ -1,124 +0,0 @@ -#ifndef DIONYSUS_FILTRATION_H -#define DIONYSUS_FILTRATION_H - -#include -#include - -#include -#include -#include -#include - -namespace b = boost; -namespace bmi = boost::multi_index; - -namespace dionysus -{ - -// Filtration stores a filtered cell complex as boost::multi_index_container<...>. -// It allows for bidirectional translation between a cell and its index. -template>, - bool checked_index = false> -class Filtration -{ - public: - struct order {}; - - typedef Cell_ Cell; - typedef CellLookupIndex_ CellLookupIndex; - - typedef b::multi_index_container> - >> Container; - typedef typename Container::value_type value_type; - - typedef typename Container::template nth_index<0>::type Complex; - typedef typename Container::template nth_index<1>::type Order; - typedef typename Order::const_iterator OrderConstIterator; - typedef typename Order::iterator OrderIterator; - - - public: - Filtration() = default; - Filtration(Filtration&& other) = default; - Filtration& operator=(Filtration&& other) = default; - - Filtration(const std::initializer_list& cells): - Filtration(std::begin(cells), std::end(cells)) {} - - template - Filtration(Iterator bg, Iterator end): - cells_(bg, end) {} - - template - Filtration(const CellRange& cells): - Filtration(std::begin(cells), std::end(cells)) {} - - // Lookup - const Cell& operator[](size_t i) const { return cells_.template get()[i]; } - OrderConstIterator iterator(const Cell& s) const { return bmi::project(cells_, cells_.find(s)); } - size_t index(const Cell& s) const; - bool contains(const Cell& s) const { return cells_.find(s) != cells_.end(); } - - void push_back(const Cell& s) { cells_.template get().push_back(s); } - void push_back(Cell&& s) { cells_.template get().push_back(s); } - - void replace(size_t i, const Cell& s) { cells_.template get().replace(begin() + i, s); } - - // return index of the cell, adding it, if necessary - size_t add(const Cell& s) { size_t i = (iterator(s) - begin()); if (i == size()) emplace_back(s); return i; } - size_t add(Cell&& s) { size_t i = (iterator(s) - begin()); if (i == size()) emplace_back(std::move(s)); return i; } - - template - void emplace_back(Args&&... args) { cells_.template get().emplace_back(std::forward(args)...); } - - template> - void sort(const Cmp& cmp = Cmp()) { cells_.template get().sort(cmp); } - - void rearrange(const std::vector& indices); - - OrderConstIterator begin() const { return cells_.template get().begin(); } - OrderConstIterator end() const { return cells_.template get().end(); } - OrderIterator begin() { return cells_.template get().begin(); } - OrderIterator end() { return cells_.template get().end(); } - size_t size() const { return cells_.size(); } - void clear() { return Container().swap(cells_); } - - Cell& back() { return const_cast(cells_.template get().back()); } - const Cell& back() const { return cells_.template get().back(); } - - private: - Container cells_; -}; - -} - -template -size_t -dionysus::Filtration:: -index(const Cell& s) const -{ - auto it = iterator(s); - if (checked_index && it == end()) - { - std::ostringstream oss; - oss << "Trying to access non-existent cell: " << s; - throw std::runtime_error(oss.str()); - } - return it - begin(); -} - -template -void -dionysus::Filtration:: -rearrange(const std::vector& indices) -{ - std::vector> references; references.reserve(indices.size()); - for (size_t i : indices) - references.push_back(std::cref((*this)[i])); - cells_.template get().rearrange(references.begin()); -} - - -#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/format.h b/src/Zigzag_persistence/example/ext_zz/dionysus/format.h deleted file mode 100644 index 7f7ba83318..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/format.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef DIONYSUS_FORMAT_H -#define DIONYSUS_FORMAT_H - -#define FMT_HEADER_ONLY - -#include "format/format.h" - -#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/format/format.cc b/src/Zigzag_persistence/example/ext_zz/dionysus/format/format.cc deleted file mode 100644 index a01e272fd7..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/format/format.cc +++ /dev/null @@ -1,1156 +0,0 @@ -/* - Formatting library for C++ - - Copyright (c) 2012 - 2014, Victor Zverovich - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "format.h" - -#include - -#include -#include -#include -#include -#include - -#ifdef _WIN32 -# ifdef __MINGW32__ -# include -# endif -# include -#endif - -using fmt::internal::Arg; - -// Check if exceptions are disabled. -#if __GNUC__ && !__EXCEPTIONS -# define FMT_EXCEPTIONS 0 -#endif -#if _MSC_VER && !_HAS_EXCEPTIONS -# define FMT_EXCEPTIONS 0 -#endif -#ifndef FMT_EXCEPTIONS -# define FMT_EXCEPTIONS 1 -#endif - -#if FMT_EXCEPTIONS -# define FMT_TRY try -# define FMT_CATCH(x) catch (x) -#else -# define FMT_TRY if (true) -# define FMT_CATCH(x) if (false) -#endif - -#ifndef FMT_THROW -# if FMT_EXCEPTIONS -# define FMT_THROW(x) throw x -# define FMT_RETURN_AFTER_THROW(x) -# else -# define FMT_THROW(x) assert(false) -# define FMT_RETURN_AFTER_THROW(x) return x -# endif -#endif - -#ifdef FMT_HEADER_ONLY -# define FMT_FUNC inline -#else -# define FMT_FUNC -#endif - -#if _MSC_VER -# pragma warning(push) -# pragma warning(disable: 4127) // conditional expression is constant -# pragma warning(disable: 4702) // unreachable code -#endif - -namespace { - -#ifndef _MSC_VER -# define FMT_SNPRINTF snprintf -#else // _MSC_VER -inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) { - va_list args; - va_start(args, format); - int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); - va_end(args); - return result; -} -# define FMT_SNPRINTF fmt_snprintf -#endif // _MSC_VER - -// Checks if a value fits in int - used to avoid warnings about comparing -// signed and unsigned integers. -template -struct IntChecker { - template - static bool fits_in_int(T value) { - unsigned max = INT_MAX; - return value <= max; - } -}; - -template <> -struct IntChecker { - template - static bool fits_in_int(T value) { - return value >= INT_MIN && value <= INT_MAX; - } -}; - -const char RESET_COLOR[] = "\x1b[0m"; - -typedef void (*FormatFunc)(fmt::Writer &, int, fmt::StringRef); - -// Portable thread-safe version of strerror. -// Sets buffer to point to a string describing the error code. -// This can be either a pointer to a string stored in buffer, -// or a pointer to some static immutable string. -// Returns one of the following values: -// 0 - success -// ERANGE - buffer is not large enough to store the error message -// other - failure -// Buffer should be at least of size 1. -int safe_strerror( - int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT { - assert(buffer != 0 && buffer_size != 0); - int result = 0; -#if ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !_GNU_SOURCE) || __ANDROID__ - // XSI-compliant version of strerror_r. - result = strerror_r(error_code, buffer, buffer_size); - if (result != 0) - result = errno; -#elif _GNU_SOURCE - // GNU-specific version of strerror_r. - char *message = strerror_r(error_code, buffer, buffer_size); - // If the buffer is full then the message is probably truncated. - if (message == buffer && strlen(buffer) == buffer_size - 1) - result = ERANGE; - buffer = message; -#elif __MINGW32__ - errno = 0; - (void)buffer_size; - buffer = strerror(error_code); - result = errno; -#elif _WIN32 - result = strerror_s(buffer, buffer_size, error_code); - // If the buffer is full then the message is probably truncated. - if (result == 0 && std::strlen(buffer) == buffer_size - 1) - result = ERANGE; -#else - result = strerror_r(error_code, buffer, buffer_size); - if (result == -1) - result = errno; // glibc versions before 2.13 return result in errno. -#endif - return result; -} - -void format_error_code(fmt::Writer &out, int error_code, - fmt::StringRef message) FMT_NOEXCEPT { - // Report error code making sure that the output fits into - // INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential - // bad_alloc. - out.clear(); - static const char SEP[] = ": "; - static const char ERR[] = "error "; - fmt::internal::IntTraits::MainType ec_value = error_code; - // Subtract 2 to account for terminating null characters in SEP and ERR. - std::size_t error_code_size = - sizeof(SEP) + sizeof(ERR) + fmt::internal::count_digits(ec_value) - 2; - if (message.size() <= fmt::internal::INLINE_BUFFER_SIZE - error_code_size) - out << message << SEP; - out << ERR << error_code; - assert(out.size() <= fmt::internal::INLINE_BUFFER_SIZE); -} - -void report_error(FormatFunc func, - int error_code, fmt::StringRef message) FMT_NOEXCEPT { - fmt::MemoryWriter full_message; - func(full_message, error_code, message); - // Use Writer::data instead of Writer::c_str to avoid potential memory - // allocation. - std::fwrite(full_message.data(), full_message.size(), 1, stderr); - std::fputc('\n', stderr); -} - -// IsZeroInt::visit(arg) returns true iff arg is a zero integer. -class IsZeroInt : public fmt::internal::ArgVisitor { - public: - template - bool visit_any_int(T value) { return value == 0; } -}; - -// Parses an unsigned integer advancing s to the end of the parsed input. -// This function assumes that the first character of s is a digit. -template -int parse_nonnegative_int(const Char *&s) { - assert('0' <= *s && *s <= '9'); - unsigned value = 0; - do { - unsigned new_value = value * 10 + (*s++ - '0'); - // Check if value wrapped around. - if (new_value < value) { - value = UINT_MAX; - break; - } - value = new_value; - } while ('0' <= *s && *s <= '9'); - if (value > INT_MAX) - FMT_THROW(fmt::FormatError("number is too big")); - return value; -} - -inline void require_numeric_argument(const Arg &arg, char spec) { - if (arg.type > Arg::LAST_NUMERIC_TYPE) { - std::string message = - fmt::format("format specifier '{}' requires numeric argument", spec); - FMT_THROW(fmt::FormatError(message)); - } -} - -template -void check_sign(const Char *&s, const Arg &arg) { - char sign = static_cast(*s); - require_numeric_argument(arg, sign); - if (arg.type == Arg::UINT || arg.type == Arg::ULONG_LONG) { - FMT_THROW(fmt::FormatError(fmt::format( - "format specifier '{}' requires signed argument", sign))); - } - ++s; -} - -// Checks if an argument is a valid printf width specifier and sets -// left alignment if it is negative. -class WidthHandler : public fmt::internal::ArgVisitor { - private: - fmt::FormatSpec &spec_; - - FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler); - - public: - explicit WidthHandler(fmt::FormatSpec &spec) : spec_(spec) {} - - unsigned visit_unhandled_arg() { - FMT_THROW(fmt::FormatError("width is not integer")); - FMT_RETURN_AFTER_THROW(0); - } - - template - unsigned visit_any_int(T value) { - typedef typename fmt::internal::IntTraits::MainType UnsignedType; - UnsignedType width = value; - if (fmt::internal::is_negative(value)) { - spec_.align_ = fmt::ALIGN_LEFT; - width = 0 - width; - } - if (width > INT_MAX) - FMT_THROW(fmt::FormatError("number is too big")); - return static_cast(width); - } -}; - -class PrecisionHandler : - public fmt::internal::ArgVisitor { - public: - unsigned visit_unhandled_arg() { - FMT_THROW(fmt::FormatError("precision is not integer")); - FMT_RETURN_AFTER_THROW(0); - } - - template - int visit_any_int(T value) { - if (!IntChecker::is_signed>::fits_in_int(value)) - FMT_THROW(fmt::FormatError("number is too big")); - return static_cast(value); - } -}; - -// Converts an integer argument to an integral type T for printf. -template -class ArgConverter : public fmt::internal::ArgVisitor, void> { - private: - fmt::internal::Arg &arg_; - wchar_t type_; - - FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter); - - public: - ArgConverter(fmt::internal::Arg &arg, wchar_t type) - : arg_(arg), type_(type) {} - - template - void visit_any_int(U value) { - bool is_signed = type_ == 'd' || type_ == 'i'; - using fmt::internal::Arg; - if (sizeof(T) <= sizeof(int)) { - // Extra casts are used to silence warnings. - if (is_signed) { - arg_.type = Arg::INT; - arg_.int_value = static_cast(static_cast(value)); - } else { - arg_.type = Arg::UINT; - arg_.uint_value = static_cast( - static_cast::Type>(value)); - } - } else { - if (is_signed) { - arg_.type = Arg::LONG_LONG; - arg_.long_long_value = - static_cast::Type>(value); - } else { - arg_.type = Arg::ULONG_LONG; - arg_.ulong_long_value = - static_cast::Type>(value); - } - } - } -}; - -// Converts an integer argument to char for printf. -class CharConverter : public fmt::internal::ArgVisitor { - private: - fmt::internal::Arg &arg_; - - FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter); - - public: - explicit CharConverter(fmt::internal::Arg &arg) : arg_(arg) {} - - template - void visit_any_int(T value) { - arg_.type = Arg::CHAR; - arg_.int_value = static_cast(value); - } -}; - -// This function template is used to prevent compile errors when handling -// incompatible string arguments, e.g. handling a wide string in a narrow -// string formatter. -template -Arg::StringValue ignore_incompatible_str(Arg::StringValue); - -template <> -inline Arg::StringValue ignore_incompatible_str( - Arg::StringValue) { return Arg::StringValue(); } - -template <> -inline Arg::StringValue ignore_incompatible_str( - Arg::StringValue s) { return s; } -} // namespace - -FMT_FUNC void fmt::SystemError::init( - int err_code, StringRef format_str, ArgList args) { - error_code_ = err_code; - MemoryWriter w; - internal::format_system_error(w, err_code, format(format_str, args)); - std::runtime_error &base = *this; - base = std::runtime_error(w.str()); -} - -template -int fmt::internal::CharTraits::format_float( - char *buffer, std::size_t size, const char *format, - unsigned width, int precision, T value) { - if (width == 0) { - return precision < 0 ? - FMT_SNPRINTF(buffer, size, format, value) : - FMT_SNPRINTF(buffer, size, format, precision, value); - } - return precision < 0 ? - FMT_SNPRINTF(buffer, size, format, width, value) : - FMT_SNPRINTF(buffer, size, format, width, precision, value); -} - -template -int fmt::internal::CharTraits::format_float( - wchar_t *buffer, std::size_t size, const wchar_t *format, - unsigned width, int precision, T value) { - if (width == 0) { - return precision < 0 ? - swprintf(buffer, size, format, value) : - swprintf(buffer, size, format, precision, value); - } - return precision < 0 ? - swprintf(buffer, size, format, width, value) : - swprintf(buffer, size, format, width, precision, value); -} - -template -const char fmt::internal::BasicData::DIGITS[] = - "0001020304050607080910111213141516171819" - "2021222324252627282930313233343536373839" - "4041424344454647484950515253545556575859" - "6061626364656667686970717273747576777879" - "8081828384858687888990919293949596979899"; - -#define FMT_POWERS_OF_10(factor) \ - factor * 10, \ - factor * 100, \ - factor * 1000, \ - factor * 10000, \ - factor * 100000, \ - factor * 1000000, \ - factor * 10000000, \ - factor * 100000000, \ - factor * 1000000000 - -template -const uint32_t fmt::internal::BasicData::POWERS_OF_10_32[] = { - 0, FMT_POWERS_OF_10(1) -}; - -template -const uint64_t fmt::internal::BasicData::POWERS_OF_10_64[] = { - 0, - FMT_POWERS_OF_10(1), - FMT_POWERS_OF_10(fmt::ULongLong(1000000000)), - // Multiply several constants instead of using a single long long constant - // to avoid warnings about C++98 not supporting long long. - fmt::ULongLong(1000000000) * fmt::ULongLong(1000000000) * 10 -}; - -FMT_FUNC void fmt::internal::report_unknown_type(char code, const char *type) { - if (std::isprint(static_cast(code))) { - FMT_THROW(fmt::FormatError( - fmt::format("unknown format code '{}' for {}", code, type))); - } - FMT_THROW(fmt::FormatError( - fmt::format("unknown format code '\\x{:02x}' for {}", - static_cast(code), type))); -} - -#ifdef _WIN32 - -FMT_FUNC fmt::internal::UTF8ToUTF16::UTF8ToUTF16(fmt::StringRef s) { - int length = MultiByteToWideChar( - CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, 0, 0); - static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16"; - if (length == 0) - FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); - buffer_.resize(length); - length = MultiByteToWideChar( - CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, &buffer_[0], length); - if (length == 0) - FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); -} - -FMT_FUNC fmt::internal::UTF16ToUTF8::UTF16ToUTF8(fmt::WStringRef s) { - if (int error_code = convert(s)) { - FMT_THROW(WindowsError(error_code, - "cannot convert string from UTF-16 to UTF-8")); - } -} - -FMT_FUNC int fmt::internal::UTF16ToUTF8::convert(fmt::WStringRef s) { - int length = WideCharToMultiByte(CP_UTF8, 0, s.c_str(), -1, 0, 0, 0, 0); - if (length == 0) - return GetLastError(); - buffer_.resize(length); - length = WideCharToMultiByte( - CP_UTF8, 0, s.c_str(), -1, &buffer_[0], length, 0, 0); - if (length == 0) - return GetLastError(); - return 0; -} - -FMT_FUNC void fmt::WindowsError::init( - int err_code, StringRef format_str, ArgList args) { - error_code_ = err_code; - MemoryWriter w; - internal::format_windows_error(w, err_code, format(format_str, args)); - std::runtime_error &base = *this; - base = std::runtime_error(w.str()); -} - -#endif - -FMT_FUNC void fmt::internal::format_system_error( - fmt::Writer &out, int error_code, - fmt::StringRef message) FMT_NOEXCEPT { - FMT_TRY { - MemoryBuffer buffer; - buffer.resize(INLINE_BUFFER_SIZE); - for (;;) { - char *system_message = &buffer[0]; - int result = safe_strerror(error_code, system_message, buffer.size()); - if (result == 0) { - out << message << ": " << system_message; - return; - } - if (result != ERANGE) - break; // Can't get error message, report error code instead. - buffer.resize(buffer.size() * 2); - } - } FMT_CATCH(...) {} - format_error_code(out, error_code, message); -} - -#ifdef _WIN32 -FMT_FUNC void fmt::internal::format_windows_error( - fmt::Writer &out, int error_code, - fmt::StringRef message) FMT_NOEXCEPT { - class String { - private: - LPWSTR str_; - - public: - String() : str_() {} - ~String() { LocalFree(str_); } - LPWSTR *ptr() { return &str_; } - LPCWSTR c_str() const { return str_; } - }; - FMT_TRY { - String system_message; - if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, - error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - reinterpret_cast(system_message.ptr()), 0, 0)) { - UTF16ToUTF8 utf8_message; - if (utf8_message.convert(system_message.c_str()) == ERROR_SUCCESS) { - out << message << ": " << utf8_message; - return; - } - } - } FMT_CATCH(...) {} - format_error_code(out, error_code, message); -} -#endif - -// An argument formatter. -template -class fmt::internal::ArgFormatter : - public fmt::internal::ArgVisitor, void> { - private: - fmt::BasicFormatter &formatter_; - fmt::BasicWriter &writer_; - fmt::FormatSpec &spec_; - const Char *format_; - - FMT_DISALLOW_COPY_AND_ASSIGN(ArgFormatter); - - public: - ArgFormatter( - fmt::BasicFormatter &f,fmt::FormatSpec &s, const Char *fmt) - : formatter_(f), writer_(f.writer()), spec_(s), format_(fmt) {} - - template - void visit_any_int(T value) { writer_.write_int(value, spec_); } - - template - void visit_any_double(T value) { writer_.write_double(value, spec_); } - - void visit_char(int value) { - if (spec_.type_ && spec_.type_ != 'c') { - spec_.flags_ |= CHAR_FLAG; - writer_.write_int(value, spec_); - return; - } - if (spec_.align_ == ALIGN_NUMERIC || spec_.flags_ != 0) - FMT_THROW(FormatError("invalid format specifier for char")); - typedef typename fmt::BasicWriter::CharPtr CharPtr; - Char fill = static_cast(spec_.fill()); - if (spec_.precision_ == 0) { - std::fill_n(writer_.grow_buffer(spec_.width_), spec_.width_, fill); - return; - } - CharPtr out = CharPtr(); - if (spec_.width_ > 1) { - out = writer_.grow_buffer(spec_.width_); - if (spec_.align_ == fmt::ALIGN_RIGHT) { - std::fill_n(out, spec_.width_ - 1, fill); - out += spec_.width_ - 1; - } else if (spec_.align_ == fmt::ALIGN_CENTER) { - out = writer_.fill_padding(out, spec_.width_, 1, fill); - } else { - std::fill_n(out + 1, spec_.width_ - 1, fill); - } - } else { - out = writer_.grow_buffer(1); - } - *out = static_cast(value); - } - - void visit_string(Arg::StringValue value) { - writer_.write_str(value, spec_); - } - void visit_wstring(Arg::StringValue value) { - writer_.write_str(ignore_incompatible_str(value), spec_); - } - - void visit_pointer(const void *value) { - if (spec_.type_ && spec_.type_ != 'p') - fmt::internal::report_unknown_type(spec_.type_, "pointer"); - spec_.flags_ = fmt::HASH_FLAG; - spec_.type_ = 'x'; - writer_.write_int(reinterpret_cast(value), spec_); - } - - void visit_custom(Arg::CustomValue c) { - c.format(&formatter_, c.value, &format_); - } -}; - -template -template -void fmt::BasicWriter::write_str( - const Arg::StringValue &s, const FormatSpec &spec) { - // Check if StrChar is convertible to Char. - internal::CharTraits::convert(StrChar()); - if (spec.type_ && spec.type_ != 's') - internal::report_unknown_type(spec.type_, "string"); - const StrChar *str_value = s.value; - std::size_t str_size = s.size; - if (str_size == 0) { - if (!str_value) - FMT_THROW(FormatError("string pointer is null")); - if (*str_value) - str_size = std::char_traits::length(str_value); - } - std::size_t precision = spec.precision_; - if (spec.precision_ >= 0 && precision < str_size) - str_size = spec.precision_; - write_str(str_value, str_size, spec); -} - -template -inline Arg fmt::BasicFormatter::parse_arg_index(const Char *&s) { - const char *error = 0; - Arg arg = *s < '0' || *s > '9' ? - next_arg(error) : get_arg(parse_nonnegative_int(s), error); - if (error) { - FMT_THROW(FormatError( - *s != '}' && *s != ':' ? "invalid format string" : error)); - } - return arg; -} - -FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg( - unsigned arg_index, const char *&error) { - Arg arg = args_[arg_index]; - if (arg.type == Arg::NONE) - error = "argument index out of range"; - return arg; -} - -inline Arg fmt::internal::FormatterBase::next_arg(const char *&error) { - if (next_arg_index_ >= 0) - return do_get_arg(next_arg_index_++, error); - error = "cannot switch from manual to automatic argument indexing"; - return Arg(); -} - -inline Arg fmt::internal::FormatterBase::get_arg( - unsigned arg_index, const char *&error) { - if (next_arg_index_ <= 0) { - next_arg_index_ = -1; - return do_get_arg(arg_index, error); - } - error = "cannot switch from automatic to manual argument indexing"; - return Arg(); -} - -template -void fmt::internal::PrintfFormatter::parse_flags( - FormatSpec &spec, const Char *&s) { - for (;;) { - switch (*s++) { - case '-': - spec.align_ = ALIGN_LEFT; - break; - case '+': - spec.flags_ |= SIGN_FLAG | PLUS_FLAG; - break; - case '0': - spec.fill_ = '0'; - break; - case ' ': - spec.flags_ |= SIGN_FLAG; - break; - case '#': - spec.flags_ |= HASH_FLAG; - break; - default: - --s; - return; - } - } -} - -template -Arg fmt::internal::PrintfFormatter::get_arg( - const Char *s, unsigned arg_index) { - const char *error = 0; - Arg arg = arg_index == UINT_MAX ? - next_arg(error) : FormatterBase::get_arg(arg_index - 1, error); - if (error) - FMT_THROW(FormatError(!*s ? "invalid format string" : error)); - return arg; -} - -template -unsigned fmt::internal::PrintfFormatter::parse_header( - const Char *&s, FormatSpec &spec) { - unsigned arg_index = UINT_MAX; - Char c = *s; - if (c >= '0' && c <= '9') { - // Parse an argument index (if followed by '$') or a width possibly - // preceded with '0' flag(s). - unsigned value = parse_nonnegative_int(s); - if (*s == '$') { // value is an argument index - ++s; - arg_index = value; - } else { - if (c == '0') - spec.fill_ = '0'; - if (value != 0) { - // Nonzero value means that we parsed width and don't need to - // parse it or flags again, so return now. - spec.width_ = value; - return arg_index; - } - } - } - parse_flags(spec, s); - // Parse width. - if (*s >= '0' && *s <= '9') { - spec.width_ = parse_nonnegative_int(s); - } else if (*s == '*') { - ++s; - spec.width_ = WidthHandler(spec).visit(get_arg(s)); - } - return arg_index; -} - -template -void fmt::internal::PrintfFormatter::format( - BasicWriter &writer, BasicStringRef format_str, - const ArgList &args) { - const Char *start = format_str.c_str(); - set_args(args); - const Char *s = start; - while (*s) { - Char c = *s++; - if (c != '%') continue; - if (*s == c) { - write(writer, start, s); - start = ++s; - continue; - } - write(writer, start, s - 1); - - FormatSpec spec; - spec.align_ = ALIGN_RIGHT; - - // Parse argument index, flags and width. - unsigned arg_index = parse_header(s, spec); - - // Parse precision. - if (*s == '.') { - ++s; - if ('0' <= *s && *s <= '9') { - spec.precision_ = parse_nonnegative_int(s); - } else if (*s == '*') { - ++s; - spec.precision_ = PrecisionHandler().visit(get_arg(s)); - } - } - - Arg arg = get_arg(s, arg_index); - if (spec.flag(HASH_FLAG) && IsZeroInt().visit(arg)) - spec.flags_ &= ~HASH_FLAG; - if (spec.fill_ == '0') { - if (arg.type <= Arg::LAST_NUMERIC_TYPE) - spec.align_ = ALIGN_NUMERIC; - else - spec.fill_ = ' '; // Ignore '0' flag for non-numeric types. - } - - // Parse length and convert the argument to the required type. - switch (*s++) { - case 'h': - if (*s == 'h') - ArgConverter(arg, *++s).visit(arg); - else - ArgConverter(arg, *s).visit(arg); - break; - case 'l': - if (*s == 'l') - ArgConverter(arg, *++s).visit(arg); - else - ArgConverter(arg, *s).visit(arg); - break; - case 'j': - ArgConverter(arg, *s).visit(arg); - break; - case 'z': - ArgConverter(arg, *s).visit(arg); - break; - case 't': - ArgConverter(arg, *s).visit(arg); - break; - case 'L': - // printf produces garbage when 'L' is omitted for long double, no - // need to do the same. - break; - default: - --s; - ArgConverter(arg, *s).visit(arg); - } - - // Parse type. - if (!*s) - FMT_THROW(FormatError("invalid format string")); - spec.type_ = static_cast(*s++); - if (arg.type <= Arg::LAST_INTEGER_TYPE) { - // Normalize type. - switch (spec.type_) { - case 'i': case 'u': - spec.type_ = 'd'; - break; - case 'c': - // TODO: handle wchar_t - CharConverter(arg).visit(arg); - break; - } - } - - start = s; - - // Format argument. - switch (arg.type) { - case Arg::INT: - writer.write_int(arg.int_value, spec); - break; - case Arg::UINT: - writer.write_int(arg.uint_value, spec); - break; - case Arg::LONG_LONG: - writer.write_int(arg.long_long_value, spec); - break; - case Arg::ULONG_LONG: - writer.write_int(arg.ulong_long_value, spec); - break; - case Arg::CHAR: { - if (spec.type_ && spec.type_ != 'c') - writer.write_int(arg.int_value, spec); - typedef typename BasicWriter::CharPtr CharPtr; - CharPtr out = CharPtr(); - if (spec.width_ > 1) { - Char fill = ' '; - out = writer.grow_buffer(spec.width_); - if (spec.align_ != ALIGN_LEFT) { - std::fill_n(out, spec.width_ - 1, fill); - out += spec.width_ - 1; - } else { - std::fill_n(out + 1, spec.width_ - 1, fill); - } - } else { - out = writer.grow_buffer(1); - } - *out = static_cast(arg.int_value); - break; - } - case Arg::DOUBLE: - writer.write_double(arg.double_value, spec); - break; - case Arg::LONG_DOUBLE: - writer.write_double(arg.long_double_value, spec); - break; - case Arg::CSTRING: - arg.string.size = 0; - writer.write_str(arg.string, spec); - break; - case Arg::STRING: - writer.write_str(arg.string, spec); - break; - case Arg::WSTRING: - writer.write_str(ignore_incompatible_str(arg.wstring), spec); - break; - case Arg::POINTER: - if (spec.type_ && spec.type_ != 'p') - internal::report_unknown_type(spec.type_, "pointer"); - spec.flags_= HASH_FLAG; - spec.type_ = 'x'; - writer.write_int(reinterpret_cast(arg.pointer), spec); - break; - case Arg::CUSTOM: { - if (spec.type_) - internal::report_unknown_type(spec.type_, "object"); - const void *str_format = "s"; - arg.custom.format(&writer, arg.custom.value, &str_format); - break; - } - default: - assert(false); - break; - } - } - write(writer, start, s); -} - -template -const Char *fmt::BasicFormatter::format( - const Char *&format_str, const Arg &arg) { - const Char *s = format_str; - FormatSpec spec; - if (*s == ':') { - if (arg.type == Arg::CUSTOM) { - arg.custom.format(this, arg.custom.value, &s); - return s; - } - ++s; - // Parse fill and alignment. - if (Char c = *s) { - const Char *p = s + 1; - spec.align_ = ALIGN_DEFAULT; - do { - switch (*p) { - case '<': - spec.align_ = ALIGN_LEFT; - break; - case '>': - spec.align_ = ALIGN_RIGHT; - break; - case '=': - spec.align_ = ALIGN_NUMERIC; - break; - case '^': - spec.align_ = ALIGN_CENTER; - break; - } - if (spec.align_ != ALIGN_DEFAULT) { - if (p != s) { - if (c == '}') break; - if (c == '{') - FMT_THROW(FormatError("invalid fill character '{'")); - s += 2; - spec.fill_ = c; - } else ++s; - if (spec.align_ == ALIGN_NUMERIC) - require_numeric_argument(arg, '='); - break; - } - } while (--p >= s); - } - - // Parse sign. - switch (*s) { - case '+': - check_sign(s, arg); - spec.flags_ |= SIGN_FLAG | PLUS_FLAG; - break; - case '-': - check_sign(s, arg); - spec.flags_ |= MINUS_FLAG; - break; - case ' ': - check_sign(s, arg); - spec.flags_ |= SIGN_FLAG; - break; - } - - if (*s == '#') { - require_numeric_argument(arg, '#'); - spec.flags_ |= HASH_FLAG; - ++s; - } - - // Parse width and zero flag. - if ('0' <= *s && *s <= '9') { - if (*s == '0') { - require_numeric_argument(arg, '0'); - spec.align_ = ALIGN_NUMERIC; - spec.fill_ = '0'; - } - // Zero may be parsed again as a part of the width, but it is simpler - // and more efficient than checking if the next char is a digit. - spec.width_ = parse_nonnegative_int(s); - } - - // Parse precision. - if (*s == '.') { - ++s; - spec.precision_ = 0; - if ('0' <= *s && *s <= '9') { - spec.precision_ = parse_nonnegative_int(s); - } else if (*s == '{') { - ++s; - const Arg &precision_arg = parse_arg_index(s); - if (*s++ != '}') - FMT_THROW(FormatError("invalid format string")); - ULongLong value = 0; - switch (precision_arg.type) { - case Arg::INT: - if (precision_arg.int_value < 0) - FMT_THROW(FormatError("negative precision")); - value = precision_arg.int_value; - break; - case Arg::UINT: - value = precision_arg.uint_value; - break; - case Arg::LONG_LONG: - if (precision_arg.long_long_value < 0) - FMT_THROW(FormatError("negative precision")); - value = precision_arg.long_long_value; - break; - case Arg::ULONG_LONG: - value = precision_arg.ulong_long_value; - break; - default: - FMT_THROW(FormatError("precision is not integer")); - } - if (value > INT_MAX) - FMT_THROW(FormatError("number is too big")); - spec.precision_ = static_cast(value); - } else { - FMT_THROW(FormatError("missing precision specifier")); - } - if (arg.type < Arg::LAST_INTEGER_TYPE || arg.type == Arg::POINTER) { - FMT_THROW(FormatError( - fmt::format("precision not allowed in {} format specifier", - arg.type == Arg::POINTER ? "pointer" : "integer"))); - } - } - - // Parse type. - if (*s != '}' && *s) - spec.type_ = static_cast(*s++); - } - - if (*s++ != '}') - FMT_THROW(FormatError("missing '}' in format string")); - start_ = s; - - // Format argument. - internal::ArgFormatter(*this, spec, s - 1).visit(arg); - return s; -} - -template -void fmt::BasicFormatter::format( - BasicStringRef format_str, const ArgList &args) { - const Char *s = start_ = format_str.c_str(); - set_args(args); - while (*s) { - Char c = *s++; - if (c != '{' && c != '}') continue; - if (*s == c) { - write(writer_, start_, s); - start_ = ++s; - continue; - } - if (c == '}') - FMT_THROW(FormatError("unmatched '}' in format string")); - write(writer_, start_, s - 1); - Arg arg = parse_arg_index(s); - s = format(s, arg); - } - write(writer_, start_, s); -} - -FMT_FUNC void fmt::report_system_error( - int error_code, fmt::StringRef message) FMT_NOEXCEPT { - report_error(internal::format_system_error, error_code, message); -} - -#ifdef _WIN32 -FMT_FUNC void fmt::report_windows_error( - int error_code, fmt::StringRef message) FMT_NOEXCEPT { - report_error(internal::format_windows_error, error_code, message); -} -#endif - -FMT_FUNC void fmt::print(std::FILE *f, StringRef format_str, ArgList args) { - MemoryWriter w; - w.write(format_str, args); - std::fwrite(w.data(), 1, w.size(), f); -} - -FMT_FUNC void fmt::print(StringRef format_str, ArgList args) { - print(stdout, format_str, args); -} - -FMT_FUNC void fmt::print(std::ostream &os, StringRef format_str, ArgList args) { - MemoryWriter w; - w.write(format_str, args); - os.write(w.data(), w.size()); -} - -FMT_FUNC void fmt::print_colored(Color c, StringRef format, ArgList args) { - char escape[] = "\x1b[30m"; - escape[3] = '0' + static_cast(c); - std::fputs(escape, stdout); - print(format, args); - std::fputs(RESET_COLOR, stdout); -} - -FMT_FUNC int fmt::fprintf(std::FILE *f, StringRef format, ArgList args) { - MemoryWriter w; - printf(w, format, args); - std::size_t size = w.size(); - return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast(size); -} - -// Explicit instantiations for char. - -template const char *fmt::BasicFormatter::format( - const char *&format_str, const fmt::internal::Arg &arg); - -template void fmt::BasicFormatter::format( - BasicStringRef format, const ArgList &args); - -template void fmt::internal::PrintfFormatter::format( - BasicWriter &writer, BasicStringRef format, const ArgList &args); - -template int fmt::internal::CharTraits::format_float( - char *buffer, std::size_t size, const char *format, - unsigned width, int precision, double value); - -template int fmt::internal::CharTraits::format_float( - char *buffer, std::size_t size, const char *format, - unsigned width, int precision, long double value); - -// Explicit instantiations for wchar_t. - -template const wchar_t *fmt::BasicFormatter::format( - const wchar_t *&format_str, const fmt::internal::Arg &arg); - -template void fmt::BasicFormatter::format( - BasicStringRef format, const ArgList &args); - -template void fmt::internal::PrintfFormatter::format( - BasicWriter &writer, BasicStringRef format, - const ArgList &args); - -template int fmt::internal::CharTraits::format_float( - wchar_t *buffer, std::size_t size, const wchar_t *format, - unsigned width, int precision, double value); - -template int fmt::internal::CharTraits::format_float( - wchar_t *buffer, std::size_t size, const wchar_t *format, - unsigned width, int precision, long double value); - -#if _MSC_VER -# pragma warning(pop) -#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/format/format.h b/src/Zigzag_persistence/example/ext_zz/dionysus/format/format.h deleted file mode 100644 index 03ed685383..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/format/format.h +++ /dev/null @@ -1,2546 +0,0 @@ -/* - Formatting library for C++ - - Copyright (c) 2012 - 2014, Victor Zverovich - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef FMT_FORMAT_H_ -#define FMT_FORMAT_H_ - -#include - -#include -#include -#include // for std::ptrdiff_t -#include -#include -#include -#include -#include -#include - -#if _SECURE_SCL -# include -#endif - -#ifdef _MSC_VER -# include // _BitScanReverse, _BitScanReverse64 - -namespace fmt { -namespace internal { -# pragma intrinsic(_BitScanReverse) -inline uint32_t clz(uint32_t x) { - unsigned long r = 0; - _BitScanReverse(&r, x); - return 31 - r; -} -# define FMT_BUILTIN_CLZ(n) fmt::internal::clz(n) -inline uint32_t clzll(uint64_t x) { - unsigned long r = 0; -# ifdef _WIN64 -# pragma intrinsic(_BitScanReverse64) - _BitScanReverse64(&r, x); -# else - // Scan the high 32 bits. - if (_BitScanReverse(&r, static_cast(x >> 32))) - return 63 - (r + 32); - - // Scan the low 32 bits. - _BitScanReverse(&r, static_cast(x)); -# endif - return 63 - r; -} -# define FMT_BUILTIN_CLZLL(n) fmt::internal::clzll(n) -} -} -#endif - -#ifdef __GNUC__ -# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -# define FMT_GCC_EXTENSION __extension__ -# if FMT_GCC_VERSION >= 406 -# pragma GCC diagnostic push -// Disable the warning about "long long" which is sometimes reported even -// when using __extension__. -# pragma GCC diagnostic ignored "-Wlong-long" -// Disable the warning about declaration shadowing because it affects too -// many valid cases. -# pragma GCC diagnostic ignored "-Wshadow" -# endif -# if __cplusplus >= 201103L || defined __GXX_EXPERIMENTAL_CXX0X__ -# define FMT_HAS_GXX_CXX11 1 -# endif -#else -# define FMT_GCC_EXTENSION -#endif - -#ifdef __clang__ -# pragma clang diagnostic ignored "-Wdocumentation-unknown-command" -#endif - -#ifdef __GNUC_LIBSTD__ -# define FMT_GNUC_LIBSTD_VERSION (__GNUC_LIBSTD__ * 100 + __GNUC_LIBSTD_MINOR__) -#endif - -#ifdef __has_feature -# define FMT_HAS_FEATURE(x) __has_feature(x) -#else -# define FMT_HAS_FEATURE(x) 0 -#endif - -#ifdef __has_builtin -# define FMT_HAS_BUILTIN(x) __has_builtin(x) -#else -# define FMT_HAS_BUILTIN(x) 0 -#endif - -#ifdef __has_cpp_attribute -# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) -#else -# define FMT_HAS_CPP_ATTRIBUTE(x) 0 -#endif - -#ifndef FMT_USE_VARIADIC_TEMPLATES -// Variadic templates are available in GCC since version 4.4 -// (http://gcc.gnu.org/projects/cxx0x.html) and in Visual C++ -// since version 2013. -# define FMT_USE_VARIADIC_TEMPLATES \ - (FMT_HAS_FEATURE(cxx_variadic_templates) || \ - (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1800) -#endif - -#ifndef FMT_USE_RVALUE_REFERENCES -// Don't use rvalue references when compiling with clang and an old libstdc++ -// as the latter doesn't provide std::move. -# if defined(FMT_GNUC_LIBSTD_VERSION) && FMT_GNUC_LIBSTD_VERSION <= 402 -# define FMT_USE_RVALUE_REFERENCES 0 -# else -# define FMT_USE_RVALUE_REFERENCES \ - (FMT_HAS_FEATURE(cxx_rvalue_references) || \ - (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1600) -# endif -#endif - -#if FMT_USE_RVALUE_REFERENCES -# include // for std::move -#endif - -// Define FMT_USE_NOEXCEPT to make C++ Format use noexcept (C++11 feature). -#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ - (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) -# define FMT_NOEXCEPT noexcept -#else -# define FMT_NOEXCEPT throw() -#endif - -// A macro to disallow the copy constructor and operator= functions -// This should be used in the private: declarations for a class -#if FMT_USE_DELETED_FUNCTIONS || FMT_HAS_FEATURE(cxx_deleted_functions) || \ - (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1800 -# define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&) = delete; \ - TypeName& operator=(const TypeName&) = delete -#else -# define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&); \ - TypeName& operator=(const TypeName&) -#endif - -namespace fmt { - -// Fix the warning about long long on older versions of GCC -// that don't support the diagnostic pragma. -FMT_GCC_EXTENSION typedef long long LongLong; -FMT_GCC_EXTENSION typedef unsigned long long ULongLong; - -#if FMT_USE_RVALUE_REFERENCES -using std::move; -#endif - -template -class BasicWriter; - -typedef BasicWriter Writer; -typedef BasicWriter WWriter; - -template -class BasicFormatter; - -template -void format(BasicFormatter &f, const Char *&format_str, const T &value); - -/** - \rst - A string reference. It can be constructed from a C string or - ``std::string``. - - You can use one of the following typedefs for common character types: - - +------------+-------------------------+ - | Type | Definition | - +============+=========================+ - | StringRef | BasicStringRef | - +------------+-------------------------+ - | WStringRef | BasicStringRef | - +------------+-------------------------+ - - This class is most useful as a parameter type to allow passing - different types of strings to a function, for example:: - - template - std::string format(StringRef format_str, const Args & ... args); - - format("{}", 42); - format(std::string("{}"), 42); - \endrst - */ -template -class BasicStringRef { - private: - const Char *data_; - std::size_t size_; - - public: - /** - Constructs a string reference object from a C string and a size. - */ - BasicStringRef(const Char *s, std::size_t size) : data_(s), size_(size) {} - - /** - Constructs a string reference object from a C string computing - the size with ``std::char_traits::length``. - */ - BasicStringRef(const Char *s) - : data_(s), size_(std::char_traits::length(s)) {} - - /** - Constructs a string reference from an `std::string` object. - */ - BasicStringRef(const std::basic_string &s) - : data_(s.c_str()), size_(s.size()) {} - - /** - Converts a string reference to an `std::string` object. - */ - operator std::basic_string() const { - return std::basic_string(data_, size()); - } - - /** - Returns the pointer to a C string. - */ - const Char *c_str() const { return data_; } - - /** - Returns the string size. - */ - std::size_t size() const { return size_; } - - friend bool operator==(BasicStringRef lhs, BasicStringRef rhs) { - return lhs.data_ == rhs.data_; - } - friend bool operator!=(BasicStringRef lhs, BasicStringRef rhs) { - return lhs.data_ != rhs.data_; - } -}; - -typedef BasicStringRef StringRef; -typedef BasicStringRef WStringRef; - -/** - A formatting error such as invalid format string. -*/ -class FormatError : public std::runtime_error { -public: - explicit FormatError(StringRef message) - : std::runtime_error(message.c_str()) {} -}; - -namespace internal { - -// The number of characters to store in the MemoryBuffer object itself -// to avoid dynamic memory allocation. -enum { INLINE_BUFFER_SIZE = 500 }; - -#if _SECURE_SCL -// Use checked iterator to avoid warnings on MSVC. -template -inline stdext::checked_array_iterator make_ptr(T *ptr, std::size_t size) { - return stdext::checked_array_iterator(ptr, size); -} -#else -template -inline T *make_ptr(T *ptr, std::size_t) { return ptr; } -#endif - -// A buffer for POD types. It supports a subset of std::vector's operations. -template -class Buffer { - private: - FMT_DISALLOW_COPY_AND_ASSIGN(Buffer); - - protected: - T *ptr_; - std::size_t size_; - std::size_t capacity_; - - Buffer(T *ptr = 0, std::size_t capacity = 0) - : ptr_(ptr), size_(0), capacity_(capacity) {} - - virtual void grow(std::size_t size) = 0; - - public: - virtual ~Buffer() {} - - // Returns the size of this buffer. - std::size_t size() const { return size_; } - - // Returns the capacity of this buffer. - std::size_t capacity() const { return capacity_; } - - // Resizes the buffer. If T is a POD type new elements are not initialized. - void resize(std::size_t new_size) { - if (new_size > capacity_) - grow(new_size); - size_ = new_size; - } - - // Reserves space to store at least capacity elements. - void reserve(std::size_t capacity) { - if (capacity > capacity_) - grow(capacity); - } - - void clear() FMT_NOEXCEPT { size_ = 0; } - - void push_back(const T &value) { - if (size_ == capacity_) - grow(size_ + 1); - ptr_[size_++] = value; - } - - // Appends data to the end of the buffer. - void append(const T *begin, const T *end); - - T &operator[](std::size_t index) { return ptr_[index]; } - const T &operator[](std::size_t index) const { return ptr_[index]; } -}; - -template -void Buffer::append(const T *begin, const T *end) { - std::ptrdiff_t num_elements = end - begin; - if (size_ + num_elements > capacity_) - grow(size_ + num_elements); - std::copy(begin, end, make_ptr(ptr_, capacity_) + size_); - size_ += num_elements; -} - -// A memory buffer for POD types with the first SIZE elements stored in -// the object itself. -template > -class MemoryBuffer : private Allocator, public Buffer { - private: - T data_[SIZE]; - - // Free memory allocated by the buffer. - void free() { - if (this->ptr_ != data_) this->deallocate(this->ptr_, this->capacity_); - } - - protected: - void grow(std::size_t size); - - public: - explicit MemoryBuffer(const Allocator &alloc = Allocator()) - : Allocator(alloc), Buffer(data_, SIZE) {} - ~MemoryBuffer() { free(); } - -#if FMT_USE_RVALUE_REFERENCES - private: - // Move data from other to this buffer. - void move(MemoryBuffer &other) { - Allocator &this_alloc = *this, &other_alloc = other; - this_alloc = std::move(other_alloc); - this->size_ = other.size_; - this->capacity_ = other.capacity_; - if (other.ptr_ == other.data_) { - this->ptr_ = data_; - std::copy(other.data_, - other.data_ + this->size_, make_ptr(data_, this->capacity_)); - } else { - this->ptr_ = other.ptr_; - // Set pointer to the inline array so that delete is not called - // when freeing. - other.ptr_ = other.data_; - } - } - - public: - MemoryBuffer(MemoryBuffer &&other) { - move(other); - } - - MemoryBuffer &operator=(MemoryBuffer &&other) { - assert(this != &other); - free(); - move(other); - return *this; - } -#endif - - // Returns a copy of the allocator associated with this buffer. - Allocator get_allocator() const { return *this; } -}; - -template -void MemoryBuffer::grow(std::size_t size) { - std::size_t new_capacity = - (std::max)(size, this->capacity_ + this->capacity_ / 2); - T *new_ptr = this->allocate(new_capacity); - // The following code doesn't throw, so the raw pointer above doesn't leak. - std::copy(this->ptr_, - this->ptr_ + this->size_, make_ptr(new_ptr, new_capacity)); - std::size_t old_capacity = this->capacity_; - T *old_ptr = this->ptr_; - this->capacity_ = new_capacity; - this->ptr_ = new_ptr; - // deallocate may throw (at least in principle), but it doesn't matter since - // the buffer already uses the new storage and will deallocate it in case - // of exception. - if (old_ptr != data_) - this->deallocate(old_ptr, old_capacity); -} - -#ifndef _MSC_VER -// Portable version of signbit. -inline int getsign(double x) { - // When compiled in C++11 mode signbit is no longer a macro but a function - // defined in namespace std and the macro is undefined. -# ifdef signbit - return signbit(x); -# else - return std::signbit(x); -# endif -} - -// Portable version of isinf. -# ifdef isinf -inline int isinfinity(double x) { return isinf(x); } -inline int isinfinity(long double x) { return isinf(x); } -# else -inline int isinfinity(double x) { return std::isinf(x); } -inline int isinfinity(long double x) { return std::isinf(x); } -# endif -#else -inline int getsign(double value) { - if (value < 0) return 1; - if (value == value) return 0; - int dec = 0, sign = 0; - char buffer[2]; // The buffer size must be >= 2 or _ecvt_s will fail. - _ecvt_s(buffer, sizeof(buffer), value, 0, &dec, &sign); - return sign; -} -inline int isinfinity(double x) { return !_finite(x); } -inline int isinfinity(long double x) { return !_finite(static_cast(x)); } -#endif - -template -class BasicCharTraits { - public: -#if _SECURE_SCL - typedef stdext::checked_array_iterator CharPtr; -#else - typedef Char *CharPtr; -#endif -}; - -template -class CharTraits; - -template <> -class CharTraits : public BasicCharTraits { - private: - // Conversion from wchar_t to char is not allowed. - static char convert(wchar_t); - -public: - typedef const wchar_t *UnsupportedStrType; - - static char convert(char value) { return value; } - - // Formats a floating-point number. - template - static int format_float(char *buffer, std::size_t size, - const char *format, unsigned width, int precision, T value); -}; - -template <> -class CharTraits : public BasicCharTraits { - public: - typedef const char *UnsupportedStrType; - - static wchar_t convert(char value) { return value; } - static wchar_t convert(wchar_t value) { return value; } - - template - static int format_float(wchar_t *buffer, std::size_t size, - const wchar_t *format, unsigned width, int precision, T value); -}; - -// Checks if a number is negative - used to avoid warnings. -template -struct SignChecker { - template - static bool is_negative(T value) { return value < 0; } -}; - -template <> -struct SignChecker { - template - static bool is_negative(T) { return false; } -}; - -// Returns true if value is negative, false otherwise. -// Same as (value < 0) but doesn't produce warnings if T is an unsigned type. -template -inline bool is_negative(T value) { - return SignChecker::is_signed>::is_negative(value); -} - -// Selects uint32_t if FitsIn32Bits is true, uint64_t otherwise. -template -struct TypeSelector { typedef uint32_t Type; }; - -template <> -struct TypeSelector { typedef uint64_t Type; }; - -template -struct IntTraits { - // Smallest of uint32_t and uint64_t that is large enough to represent - // all values of T. - typedef typename - TypeSelector::digits <= 32>::Type MainType; -}; - -// MakeUnsigned::Type gives an unsigned type corresponding to integer type T. -template -struct MakeUnsigned { typedef T Type; }; - -#define FMT_SPECIALIZE_MAKE_UNSIGNED(T, U) \ - template <> \ - struct MakeUnsigned { typedef U Type; } - -FMT_SPECIALIZE_MAKE_UNSIGNED(char, unsigned char); -FMT_SPECIALIZE_MAKE_UNSIGNED(signed char, unsigned char); -FMT_SPECIALIZE_MAKE_UNSIGNED(short, unsigned short); -FMT_SPECIALIZE_MAKE_UNSIGNED(int, unsigned); -FMT_SPECIALIZE_MAKE_UNSIGNED(long, unsigned long); -FMT_SPECIALIZE_MAKE_UNSIGNED(LongLong, ULongLong); - -void report_unknown_type(char code, const char *type); - -// Static data is placed in this class template to allow header-only -// configuration. -template -struct BasicData { - static const uint32_t POWERS_OF_10_32[]; - static const uint64_t POWERS_OF_10_64[]; - static const char DIGITS[]; -}; - -typedef BasicData<> Data; - -#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clz) -# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) -#endif - -#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clzll) -# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) -#endif - -#ifdef FMT_BUILTIN_CLZLL -// Returns the number of decimal digits in n. Leading zeros are not counted -// except for n == 0 in which case count_digits returns 1. -inline unsigned count_digits(uint64_t n) { - // Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 - // and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits. - unsigned t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12; - return t - (n < Data::POWERS_OF_10_64[t]) + 1; -} -#else -// Fallback version of count_digits used when __builtin_clz is not available. -inline unsigned count_digits(uint64_t n) { - unsigned count = 1; - for (;;) { - // Integer division is slow so do it for a group of four digits instead - // of for every digit. The idea comes from the talk by Alexandrescu - // "Three Optimization Tips for C++". See speed-test for a comparison. - if (n < 10) return count; - if (n < 100) return count + 1; - if (n < 1000) return count + 2; - if (n < 10000) return count + 3; - n /= 10000u; - count += 4; - } -} -#endif - -#ifdef FMT_BUILTIN_CLZ -// Optional version of count_digits for better performance on 32-bit platforms. -inline unsigned count_digits(uint32_t n) { - uint32_t t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12; - return t - (n < Data::POWERS_OF_10_32[t]) + 1; -} -#endif - -// Formats a decimal unsigned integer value writing into buffer. -template -inline void format_decimal(Char *buffer, UInt value, unsigned num_digits) { - --num_digits; - while (value >= 100) { - // Integer division is slow so do it for a group of two digits instead - // of for every digit. The idea comes from the talk by Alexandrescu - // "Three Optimization Tips for C++". See speed-test for a comparison. - unsigned index = (value % 100) * 2; - value /= 100; - buffer[num_digits] = Data::DIGITS[index + 1]; - buffer[num_digits - 1] = Data::DIGITS[index]; - num_digits -= 2; - } - if (value < 10) { - *buffer = static_cast('0' + value); - return; - } - unsigned index = static_cast(value * 2); - buffer[1] = Data::DIGITS[index + 1]; - buffer[0] = Data::DIGITS[index]; -} - -#ifdef _WIN32 -// A converter from UTF-8 to UTF-16. -// It is only provided for Windows since other systems support UTF-8 natively. -class UTF8ToUTF16 { - private: - MemoryBuffer buffer_; - - public: - explicit UTF8ToUTF16(StringRef s); - operator WStringRef() const { return WStringRef(&buffer_[0], size()); } - size_t size() const { return buffer_.size() - 1; } - const wchar_t *c_str() const { return &buffer_[0]; } - std::wstring str() const { return std::wstring(&buffer_[0], size()); } -}; - -// A converter from UTF-16 to UTF-8. -// It is only provided for Windows since other systems support UTF-8 natively. -class UTF16ToUTF8 { - private: - MemoryBuffer buffer_; - - public: - UTF16ToUTF8() {} - explicit UTF16ToUTF8(WStringRef s); - operator StringRef() const { return StringRef(&buffer_[0], size()); } - size_t size() const { return buffer_.size() - 1; } - const char *c_str() const { return &buffer_[0]; } - std::string str() const { return std::string(&buffer_[0], size()); } - - // Performs conversion returning a system error code instead of - // throwing exception on conversion error. This method may still throw - // in case of memory allocation error. - int convert(WStringRef s); -}; -#endif - -void format_system_error(fmt::Writer &out, int error_code, - fmt::StringRef message) FMT_NOEXCEPT; - -#ifdef _WIN32 -void format_windows_error(fmt::Writer &out, int error_code, - fmt::StringRef message) FMT_NOEXCEPT; -#endif - -// Computes max(Arg, 1) at compile time. It is used to avoid errors about -// allocating an array of 0 size. -template -struct NonZero { - enum { VALUE = Arg }; -}; - -template <> -struct NonZero<0> { - enum { VALUE = 1 }; -}; - -// The value of a formatting argument. It is a POD type to allow storage in -// internal::MemoryBuffer. -struct Value { - template - struct StringValue { - const Char *value; - std::size_t size; - }; - - typedef void (*FormatFunc)( - void *formatter, const void *arg, void *format_str_ptr); - - struct CustomValue { - const void *value; - FormatFunc format; - }; - - union { - int int_value; - unsigned uint_value; - LongLong long_long_value; - ULongLong ulong_long_value; - double double_value; - long double long_double_value; - const void *pointer; - StringValue string; - StringValue sstring; - StringValue ustring; - StringValue wstring; - CustomValue custom; - }; -}; - -struct Arg : Value { - enum Type { - NONE, - // Integer types should go first, - INT, UINT, LONG_LONG, ULONG_LONG, CHAR, LAST_INTEGER_TYPE = CHAR, - // followed by floating-point types. - DOUBLE, LONG_DOUBLE, LAST_NUMERIC_TYPE = LONG_DOUBLE, - CSTRING, STRING, WSTRING, POINTER, CUSTOM - }; - Type type; -}; - -// Makes a Value object from any type. -template -class MakeValue : public Value { - private: - // The following two methods are private to disallow formatting of - // arbitrary pointers. If you want to output a pointer cast it to - // "void *" or "const void *". In particular, this forbids formatting - // of "[const] volatile char *" which is printed as bool by iostreams. - // Do not implement! - template - MakeValue(const T *value); - template - MakeValue(T *value); - - void set_string(StringRef str) { - string.value = str.c_str(); - string.size = str.size(); - } - - void set_string(WStringRef str) { - CharTraits::convert(wchar_t()); - wstring.value = str.c_str(); - wstring.size = str.size(); - } - - // Formats an argument of a custom type, such as a user-defined class. - template - static void format_custom_arg( - void *formatter, const void *arg, void *format_str_ptr) { - format(*static_cast*>(formatter), - *static_cast(format_str_ptr), - *static_cast(arg)); - } - -public: - MakeValue() {} - -#define FMT_MAKE_VALUE(Type, field, TYPE) \ - MakeValue(Type value) { field = value; } \ - static uint64_t type(Type) { return Arg::TYPE; } - - FMT_MAKE_VALUE(bool, int_value, INT) - FMT_MAKE_VALUE(short, int_value, INT) - FMT_MAKE_VALUE(unsigned short, uint_value, UINT) - FMT_MAKE_VALUE(int, int_value, INT) - FMT_MAKE_VALUE(unsigned, uint_value, UINT) - - MakeValue(long value) { - // To minimize the number of types we need to deal with, long is - // translated either to int or to long long depending on its size. - if (sizeof(long) == sizeof(int)) - int_value = static_cast(value); - else - long_long_value = value; - } - static uint64_t type(long) { - return sizeof(long) == sizeof(int) ? Arg::INT : Arg::LONG_LONG; - } - - MakeValue(unsigned long value) { - if (sizeof(unsigned long) == sizeof(unsigned)) - uint_value = static_cast(value); - else - ulong_long_value = value; - } - static uint64_t type(unsigned long) { - return sizeof(unsigned long) == sizeof(unsigned) ? - Arg::UINT : Arg::ULONG_LONG; - } - - FMT_MAKE_VALUE(LongLong, long_long_value, LONG_LONG) - FMT_MAKE_VALUE(ULongLong, ulong_long_value, ULONG_LONG) - FMT_MAKE_VALUE(float, double_value, DOUBLE) - FMT_MAKE_VALUE(double, double_value, DOUBLE) - FMT_MAKE_VALUE(long double, long_double_value, LONG_DOUBLE) - FMT_MAKE_VALUE(signed char, int_value, CHAR) - FMT_MAKE_VALUE(unsigned char, int_value, CHAR) - FMT_MAKE_VALUE(char, int_value, CHAR) - - MakeValue(wchar_t value) { - int_value = internal::CharTraits::convert(value); - } - static uint64_t type(wchar_t) { return Arg::CHAR; } - -#define FMT_MAKE_STR_VALUE(Type, TYPE) \ - MakeValue(Type value) { set_string(value); } \ - static uint64_t type(Type) { return Arg::TYPE; } - - FMT_MAKE_VALUE(char *, string.value, CSTRING) - FMT_MAKE_VALUE(const char *, string.value, CSTRING) - FMT_MAKE_VALUE(const signed char *, sstring.value, CSTRING) - FMT_MAKE_VALUE(const unsigned char *, ustring.value, CSTRING) - FMT_MAKE_STR_VALUE(const std::string &, STRING) - FMT_MAKE_STR_VALUE(StringRef, STRING) - - FMT_MAKE_STR_VALUE(wchar_t *, WSTRING) - FMT_MAKE_STR_VALUE(const wchar_t *, WSTRING) - FMT_MAKE_STR_VALUE(const std::wstring &, WSTRING) - FMT_MAKE_STR_VALUE(WStringRef, WSTRING) - - FMT_MAKE_VALUE(void *, pointer, POINTER) - FMT_MAKE_VALUE(const void *, pointer, POINTER) - - template - MakeValue(const T &value) { - custom.value = &value; - custom.format = &format_custom_arg; - } - template - static uint64_t type(const T &) { return Arg::CUSTOM; } -}; - -#define FMT_DISPATCH(call) static_cast(this)->call - -// An argument visitor. -// To use ArgVisitor define a subclass that implements some or all of the -// visit methods with the same signatures as the methods in ArgVisitor, -// for example, visit_int(int). -// Specify the subclass name as the Impl template parameter. Then calling -// ArgVisitor::visit for some argument will dispatch to a visit method -// specific to the argument type. For example, if the argument type is -// double then visit_double(double) method of a subclass will be called. -// If the subclass doesn't contain a method with this signature, then -// a corresponding method of ArgVisitor will be called. -// -// Example: -// class MyArgVisitor : public ArgVisitor { -// public: -// void visit_int(int value) { print("{}", value); } -// void visit_double(double value) { print("{}", value ); } -// }; -// -// ArgVisitor uses the curiously recurring template pattern: -// http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern -template -class ArgVisitor { - public: - Result visit_unhandled_arg() { return Result(); } - - Result visit_int(int value) { - return FMT_DISPATCH(visit_any_int(value)); - } - Result visit_long_long(LongLong value) { - return FMT_DISPATCH(visit_any_int(value)); - } - Result visit_uint(unsigned value) { - return FMT_DISPATCH(visit_any_int(value)); - } - Result visit_ulong_long(ULongLong value) { - return FMT_DISPATCH(visit_any_int(value)); - } - Result visit_char(int value) { - return FMT_DISPATCH(visit_any_int(value)); - } - template - Result visit_any_int(T) { - return FMT_DISPATCH(visit_unhandled_arg()); - } - - Result visit_double(double value) { - return FMT_DISPATCH(visit_any_double(value)); - } - Result visit_long_double(long double value) { - return FMT_DISPATCH(visit_any_double(value)); - } - template - Result visit_any_double(T) { - return FMT_DISPATCH(visit_unhandled_arg()); - } - - Result visit_string(Arg::StringValue) { - return FMT_DISPATCH(visit_unhandled_arg()); - } - Result visit_wstring(Arg::StringValue) { - return FMT_DISPATCH(visit_unhandled_arg()); - } - Result visit_pointer(const void *) { - return FMT_DISPATCH(visit_unhandled_arg()); - } - Result visit_custom(Arg::CustomValue) { - return FMT_DISPATCH(visit_unhandled_arg()); - } - - Result visit(const Arg &arg) { - switch (arg.type) { - default: - assert(false); - return Result(); - case Arg::INT: - return FMT_DISPATCH(visit_int(arg.int_value)); - case Arg::UINT: - return FMT_DISPATCH(visit_uint(arg.uint_value)); - case Arg::LONG_LONG: - return FMT_DISPATCH(visit_long_long(arg.long_long_value)); - case Arg::ULONG_LONG: - return FMT_DISPATCH(visit_ulong_long(arg.ulong_long_value)); - case Arg::DOUBLE: - return FMT_DISPATCH(visit_double(arg.double_value)); - case Arg::LONG_DOUBLE: - return FMT_DISPATCH(visit_long_double(arg.long_double_value)); - case Arg::CHAR: - return FMT_DISPATCH(visit_char(arg.int_value)); - case Arg::CSTRING: { - Value::StringValue str = arg.string; - str.size = 0; - return FMT_DISPATCH(visit_string(str)); - } - case Arg::STRING: - return FMT_DISPATCH(visit_string(arg.string)); - case Arg::WSTRING: - return FMT_DISPATCH(visit_wstring(arg.wstring)); - case Arg::POINTER: - return FMT_DISPATCH(visit_pointer(arg.pointer)); - case Arg::CUSTOM: - return FMT_DISPATCH(visit_custom(arg.custom)); - } - } -}; - -class RuntimeError : public std::runtime_error { - protected: - RuntimeError() : std::runtime_error("") {} -}; - -template -class ArgFormatter; -} // namespace internal - -/** - An argument list. - */ -class ArgList { - private: - uint64_t types_; - const internal::Value *values_; - - public: - // Maximum number of arguments that can be passed in ArgList. - enum { MAX_ARGS = 16 }; - - ArgList() : types_(0) {} - ArgList(ULongLong types, const internal::Value *values) - : types_(types), values_(values) {} - - /** - Returns the argument at specified index. - */ - internal::Arg operator[](unsigned index) const { - using internal::Arg; - Arg arg; - if (index >= MAX_ARGS) { - arg.type = Arg::NONE; - return arg; - } - unsigned shift = index * 4; - uint64_t mask = 0xf; - Arg::Type type = - static_cast((types_ & (mask << shift)) >> shift); - arg.type = type; - if (type != Arg::NONE) { - internal::Value &value = arg; - value = values_[index]; - } - return arg; - } -}; - -struct FormatSpec; - -namespace internal { - -class FormatterBase { - private: - ArgList args_; - int next_arg_index_; - - // Returns the argument with specified index. - Arg do_get_arg(unsigned arg_index, const char *&error); - - protected: - void set_args(const ArgList &args) { - args_ = args; - next_arg_index_ = 0; - } - - // Returns the next argument. - Arg next_arg(const char *&error); - - // Checks if manual indexing is used and returns the argument with - // specified index. - Arg get_arg(unsigned arg_index, const char *&error); - - template - void write(BasicWriter &w, const Char *start, const Char *end) { - if (start != end) - w << BasicStringRef(start, end - start); - } -}; - -// A printf formatter. -template -class PrintfFormatter : private FormatterBase { - private: - void parse_flags(FormatSpec &spec, const Char *&s); - - // Returns the argument with specified index or, if arg_index is equal - // to the maximum unsigned value, the next argument. - Arg get_arg(const Char *s, - unsigned arg_index = (std::numeric_limits::max)()); - - // Parses argument index, flags and width and returns the argument index. - unsigned parse_header(const Char *&s, FormatSpec &spec); - - public: - void format(BasicWriter &writer, - BasicStringRef format_str, const ArgList &args); -}; -} // namespace internal - -// A formatter. -template -class BasicFormatter : private internal::FormatterBase { - private: - BasicWriter &writer_; - const Char *start_; - - FMT_DISALLOW_COPY_AND_ASSIGN(BasicFormatter); - - // Parses argument index and returns corresponding argument. - internal::Arg parse_arg_index(const Char *&s); - - public: - explicit BasicFormatter(BasicWriter &w) : writer_(w) {} - - BasicWriter &writer() { return writer_; } - - void format(BasicStringRef format_str, const ArgList &args); - - const Char *format(const Char *&format_str, const internal::Arg &arg); -}; - -enum Alignment { - ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC -}; - -// Flags. -enum { - SIGN_FLAG = 1, PLUS_FLAG = 2, MINUS_FLAG = 4, HASH_FLAG = 8, - CHAR_FLAG = 0x10 // Argument has char type - used in error reporting. -}; - -// An empty format specifier. -struct EmptySpec {}; - -// A type specifier. -template -struct TypeSpec : EmptySpec { - Alignment align() const { return ALIGN_DEFAULT; } - unsigned width() const { return 0; } - int precision() const { return -1; } - bool flag(unsigned) const { return false; } - char type() const { return TYPE; } - char fill() const { return ' '; } -}; - -// A width specifier. -struct WidthSpec { - unsigned width_; - // Fill is always wchar_t and cast to char if necessary to avoid having - // two specialization of WidthSpec and its subclasses. - wchar_t fill_; - - WidthSpec(unsigned width, wchar_t fill) : width_(width), fill_(fill) {} - - unsigned width() const { return width_; } - wchar_t fill() const { return fill_; } -}; - -// An alignment specifier. -struct AlignSpec : WidthSpec { - Alignment align_; - - AlignSpec(unsigned width, wchar_t fill, Alignment align = ALIGN_DEFAULT) - : WidthSpec(width, fill), align_(align) {} - - Alignment align() const { return align_; } - - int precision() const { return -1; } -}; - -// An alignment and type specifier. -template -struct AlignTypeSpec : AlignSpec { - AlignTypeSpec(unsigned width, wchar_t fill) : AlignSpec(width, fill) {} - - bool flag(unsigned) const { return false; } - char type() const { return TYPE; } -}; - -// A full format specifier. -struct FormatSpec : AlignSpec { - unsigned flags_; - int precision_; - char type_; - - FormatSpec( - unsigned width = 0, char type = 0, wchar_t fill = ' ') - : AlignSpec(width, fill), flags_(0), precision_(-1), type_(type) {} - - bool flag(unsigned f) const { return (flags_ & f) != 0; } - int precision() const { return precision_; } - char type() const { return type_; } -}; - -// An integer format specifier. -template , typename Char = char> -class IntFormatSpec : public SpecT { - private: - T value_; - - public: - IntFormatSpec(T val, const SpecT &spec = SpecT()) - : SpecT(spec), value_(val) {} - - T value() const { return value_; } -}; - -// A string format specifier. -template -class StrFormatSpec : public AlignSpec { - private: - const T *str_; - - public: - StrFormatSpec(const T *str, unsigned width, wchar_t fill) - : AlignSpec(width, fill), str_(str) {} - - const T *str() const { return str_; } -}; - -/** - Returns an integer format specifier to format the value in base 2. - */ -IntFormatSpec > bin(int value); - -/** - Returns an integer format specifier to format the value in base 8. - */ -IntFormatSpec > oct(int value); - -/** - Returns an integer format specifier to format the value in base 16 using - lower-case letters for the digits above 9. - */ -IntFormatSpec > hex(int value); - -/** - Returns an integer formatter format specifier to format in base 16 using - upper-case letters for the digits above 9. - */ -IntFormatSpec > hexu(int value); - -/** - \rst - Returns an integer format specifier to pad the formatted argument with the - fill character to the specified width using the default (right) numeric - alignment. - - **Example**:: - - MemoryWriter out; - out << pad(hex(0xcafe), 8, '0'); - // out.str() == "0000cafe" - - \endrst - */ -template -IntFormatSpec, Char> pad( - int value, unsigned width, Char fill = ' '); - -#define FMT_DEFINE_INT_FORMATTERS(TYPE) \ -inline IntFormatSpec > bin(TYPE value) { \ - return IntFormatSpec >(value, TypeSpec<'b'>()); \ -} \ - \ -inline IntFormatSpec > oct(TYPE value) { \ - return IntFormatSpec >(value, TypeSpec<'o'>()); \ -} \ - \ -inline IntFormatSpec > hex(TYPE value) { \ - return IntFormatSpec >(value, TypeSpec<'x'>()); \ -} \ - \ -inline IntFormatSpec > hexu(TYPE value) { \ - return IntFormatSpec >(value, TypeSpec<'X'>()); \ -} \ - \ -template \ -inline IntFormatSpec > pad( \ - IntFormatSpec > f, unsigned width) { \ - return IntFormatSpec >( \ - f.value(), AlignTypeSpec(width, ' ')); \ -} \ - \ -/* For compatibility with older compilers we provide two overloads for pad, */ \ -/* one that takes a fill character and one that doesn't. In the future this */ \ -/* can be replaced with one overload making the template argument Char */ \ -/* default to char (C++11). */ \ -template \ -inline IntFormatSpec, Char> pad( \ - IntFormatSpec, Char> f, \ - unsigned width, Char fill) { \ - return IntFormatSpec, Char>( \ - f.value(), AlignTypeSpec(width, fill)); \ -} \ - \ -inline IntFormatSpec > pad( \ - TYPE value, unsigned width) { \ - return IntFormatSpec >( \ - value, AlignTypeSpec<0>(width, ' ')); \ -} \ - \ -template \ -inline IntFormatSpec, Char> pad( \ - TYPE value, unsigned width, Char fill) { \ - return IntFormatSpec, Char>( \ - value, AlignTypeSpec<0>(width, fill)); \ -} - -FMT_DEFINE_INT_FORMATTERS(int) -FMT_DEFINE_INT_FORMATTERS(long) -FMT_DEFINE_INT_FORMATTERS(unsigned) -FMT_DEFINE_INT_FORMATTERS(unsigned long) -FMT_DEFINE_INT_FORMATTERS(LongLong) -FMT_DEFINE_INT_FORMATTERS(ULongLong) - -/** - \rst - Returns a string formatter that pads the formatted argument with the fill - character to the specified width using the default (left) string alignment. - - **Example**:: - - std::string s = str(MemoryWriter() << pad("abc", 8)); - // s == "abc " - - \endrst - */ -template -inline StrFormatSpec pad( - const Char *str, unsigned width, Char fill = ' ') { - return StrFormatSpec(str, width, fill); -} - -inline StrFormatSpec pad( - const wchar_t *str, unsigned width, char fill = ' ') { - return StrFormatSpec(str, width, fill); -} - -// Generates a comma-separated list with results of applying f to -// numbers 0..n-1. -# define FMT_GEN(n, f) FMT_GEN##n(f) -# define FMT_GEN1(f) f(0) -# define FMT_GEN2(f) FMT_GEN1(f), f(1) -# define FMT_GEN3(f) FMT_GEN2(f), f(2) -# define FMT_GEN4(f) FMT_GEN3(f), f(3) -# define FMT_GEN5(f) FMT_GEN4(f), f(4) -# define FMT_GEN6(f) FMT_GEN5(f), f(5) -# define FMT_GEN7(f) FMT_GEN6(f), f(6) -# define FMT_GEN8(f) FMT_GEN7(f), f(7) -# define FMT_GEN9(f) FMT_GEN8(f), f(8) -# define FMT_GEN10(f) FMT_GEN9(f), f(9) -# define FMT_GEN11(f) FMT_GEN10(f), f(10) -# define FMT_GEN12(f) FMT_GEN11(f), f(11) -# define FMT_GEN13(f) FMT_GEN12(f), f(12) -# define FMT_GEN14(f) FMT_GEN13(f), f(13) -# define FMT_GEN15(f) FMT_GEN14(f), f(14) - -namespace internal { -inline uint64_t make_type() { return 0; } - -template -inline uint64_t make_type(const T &arg) { return MakeValue::type(arg); } - -#if FMT_USE_VARIADIC_TEMPLATES -template -inline uint64_t make_type(const Arg &first, const Args & ... tail) { - return make_type(first) | (make_type(tail...) << 4); -} -#else - -struct ArgType { - uint64_t type; - - ArgType() : type(0) {} - - template - ArgType(const T &arg) : type(make_type(arg)) {} -}; - -# define FMT_ARG_TYPE_DEFAULT(n) ArgType t##n = ArgType() - -inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) { - return t0.type | (t1.type << 4) | (t2.type << 8) | (t3.type << 12) | - (t4.type << 16) | (t5.type << 20) | (t6.type << 24) | (t7.type << 28) | - (t8.type << 32) | (t9.type << 36) | (t10.type << 40) | (t11.type << 44) | - (t12.type << 48) | (t13.type << 52) | (t14.type << 56); -} -#endif -} // namespace internal - -# define FMT_MAKE_TEMPLATE_ARG(n) typename T##n -# define FMT_MAKE_ARG_TYPE(n) T##n -# define FMT_MAKE_ARG(n) const T##n &v##n -# define FMT_MAKE_REF_char(n) fmt::internal::MakeValue(v##n) -# define FMT_MAKE_REF_wchar_t(n) fmt::internal::MakeValue(v##n) - -#if FMT_USE_VARIADIC_TEMPLATES -// Defines a variadic function returning void. -# define FMT_VARIADIC_VOID(func, arg_type) \ - template \ - void func(arg_type arg1, const Args & ... args) { \ - const fmt::internal::Value values[ \ - fmt::internal::NonZero::VALUE] = { \ - fmt::internal::MakeValue(args)... \ - }; \ - func(arg1, ArgList(fmt::internal::make_type(args...), values)); \ - } - -// Defines a variadic constructor. -# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ - template \ - ctor(arg0_type arg0, arg1_type arg1, const Args & ... args) { \ - using fmt::internal::MakeValue; \ - const fmt::internal::Value values[ \ - fmt::internal::NonZero::VALUE] = { \ - MakeValue(args)... \ - }; \ - func(arg0, arg1, ArgList(fmt::internal::make_type(args...), values)); \ - } - -#else - -# define FMT_MAKE_REF(n) fmt::internal::MakeValue(v##n) -# define FMT_MAKE_REF2(n) v##n - -// Defines a wrapper for a function taking one argument of type arg_type -// and n additional arguments of arbitrary types. -# define FMT_WRAP1(func, arg_type, n) \ - template \ - inline void func(arg_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ - const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF)}; \ - func(arg1, fmt::ArgList( \ - fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), vals)); \ - } - -// Emulates a variadic function returning void on a pre-C++11 compiler. -# define FMT_VARIADIC_VOID(func, arg_type) \ - inline void func(arg_type arg) { func(arg, fmt::ArgList()); } \ - FMT_WRAP1(func, arg_type, 1) FMT_WRAP1(func, arg_type, 2) \ - FMT_WRAP1(func, arg_type, 3) FMT_WRAP1(func, arg_type, 4) \ - FMT_WRAP1(func, arg_type, 5) FMT_WRAP1(func, arg_type, 6) \ - FMT_WRAP1(func, arg_type, 7) FMT_WRAP1(func, arg_type, 8) \ - FMT_WRAP1(func, arg_type, 9) FMT_WRAP1(func, arg_type, 10) - -# define FMT_CTOR(ctor, func, arg0_type, arg1_type, n) \ - template \ - ctor(arg0_type arg0, arg1_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ - const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF)}; \ - func(arg0, arg1, fmt::ArgList( \ - fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), vals)); \ - } - -// Emulates a variadic constructor on a pre-C++11 compiler. -# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 1) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 2) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 3) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 4) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 5) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 6) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 7) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 8) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 9) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 10) -#endif - -// Generates a comma-separated list with results of applying f to pairs -// (argument, index). -#define FMT_FOR_EACH1(f, x0) f(x0, 0) -#define FMT_FOR_EACH2(f, x0, x1) \ - FMT_FOR_EACH1(f, x0), f(x1, 1) -#define FMT_FOR_EACH3(f, x0, x1, x2) \ - FMT_FOR_EACH2(f, x0 ,x1), f(x2, 2) -#define FMT_FOR_EACH4(f, x0, x1, x2, x3) \ - FMT_FOR_EACH3(f, x0, x1, x2), f(x3, 3) -#define FMT_FOR_EACH5(f, x0, x1, x2, x3, x4) \ - FMT_FOR_EACH4(f, x0, x1, x2, x3), f(x4, 4) -#define FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5) \ - FMT_FOR_EACH5(f, x0, x1, x2, x3, x4), f(x5, 5) -#define FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6) \ - FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5), f(x6, 6) -#define FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7) \ - FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6), f(x7, 7) -#define FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8) \ - FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7), f(x8, 8) -#define FMT_FOR_EACH10(f, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) \ - FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8), f(x9, 9) - -/** - An error returned by an operating system or a language runtime, - for example a file opening error. -*/ -class SystemError : public internal::RuntimeError { - private: - void init(int err_code, StringRef format_str, ArgList args); - - protected: - int error_code_; - - typedef char Char; // For FMT_VARIADIC_CTOR. - - SystemError() {} - - public: - /** - \rst - Constructs a :class:`fmt::SystemError` object with the description - of the form - - .. parsed-literal:: - **: ** - - where ** is the formatted message and ** is - the system message corresponding to the error code. - *error_code* is a system error code as given by ``errno``. - If *error_code* is not a valid error code such as -1, the system message - may look like "Unknown error -1" and is platform-dependent. - - **Example**:: - - // This throws a SystemError with the description - // cannot open file 'madeup': No such file or directory - // or similar (system message may vary). - const char *filename = "madeup"; - std::FILE *file = std::fopen(filename, "r"); - if (!file) - throw fmt::SystemError(errno, "cannot open file '{}'", filename); - \endrst - */ - SystemError(int error_code, StringRef message) { - init(error_code, message, ArgList()); - } - FMT_VARIADIC_CTOR(SystemError, init, int, StringRef) - - int error_code() const { return error_code_; } -}; - -/** - \rst - This template provides operations for formatting and writing data into - a character stream. The output is stored in a buffer provided by a subclass - such as :class:`fmt::BasicMemoryWriter`. - - You can use one of the following typedefs for common character types: - - +---------+----------------------+ - | Type | Definition | - +=========+======================+ - | Writer | BasicWriter | - +---------+----------------------+ - | WWriter | BasicWriter | - +---------+----------------------+ - - \endrst - */ -template -class BasicWriter { - private: - // Output buffer. - internal::Buffer &buffer_; - - FMT_DISALLOW_COPY_AND_ASSIGN(BasicWriter); - - typedef typename internal::CharTraits::CharPtr CharPtr; - -#if _SECURE_SCL - // Returns pointer value. - static Char *get(CharPtr p) { return p.base(); } -#else - static Char *get(Char *p) { return p; } -#endif - - // Fills the padding around the content and returns the pointer to the - // content area. - static CharPtr fill_padding(CharPtr buffer, - unsigned total_size, std::size_t content_size, wchar_t fill); - - // Grows the buffer by n characters and returns a pointer to the newly - // allocated area. - CharPtr grow_buffer(std::size_t n) { - std::size_t size = buffer_.size(); - buffer_.resize(size + n); - return internal::make_ptr(&buffer_[size], n); - } - - // Prepare a buffer for integer formatting. - CharPtr prepare_int_buffer(unsigned num_digits, - const EmptySpec &, const char *prefix, unsigned prefix_size) { - unsigned size = prefix_size + num_digits; - CharPtr p = grow_buffer(size); - std::copy(prefix, prefix + prefix_size, p); - return p + size - 1; - } - - template - CharPtr prepare_int_buffer(unsigned num_digits, - const Spec &spec, const char *prefix, unsigned prefix_size); - - // Formats an integer. - template - void write_int(T value, Spec spec); - - // Formats a floating-point number (double or long double). - template - void write_double(T value, const FormatSpec &spec); - - // Writes a formatted string. - template - CharPtr write_str( - const StrChar *s, std::size_t size, const AlignSpec &spec); - - template - void write_str( - const internal::Arg::StringValue &str, const FormatSpec &spec); - - // This method is private to disallow writing a wide string to a - // char stream and vice versa. If you want to print a wide string - // as a pointer as std::ostream does, cast it to const void*. - // Do not implement! - void operator<<(typename internal::CharTraits::UnsupportedStrType); - - // Appends floating-point length specifier to the format string. - // The second argument is only used for overload resolution. - void append_float_length(Char *&format_ptr, long double) { - *format_ptr++ = 'L'; - } - - template - void append_float_length(Char *&, T) {} - - friend class internal::ArgFormatter; - friend class internal::PrintfFormatter; - - protected: - /** - Constructs a ``BasicWriter`` object. - */ - explicit BasicWriter(internal::Buffer &b) : buffer_(b) {} - - public: - /** - Destroys a ``BasicWriter`` object. - */ - virtual ~BasicWriter() {} - - /** - Returns the total number of characters written. - */ - std::size_t size() const { return buffer_.size(); } - - /** - Returns a pointer to the output buffer content. No terminating null - character is appended. - */ - const Char *data() const FMT_NOEXCEPT { return &buffer_[0]; } - - /** - Returns a pointer to the output buffer content with terminating null - character appended. - */ - const Char *c_str() const { - std::size_t size = buffer_.size(); - buffer_.reserve(size + 1); - buffer_[size] = '\0'; - return &buffer_[0]; - } - - /** - Returns the content of the output buffer as an `std::string`. - */ - std::basic_string str() const { - return std::basic_string(&buffer_[0], buffer_.size()); - } - - /** - \rst - Writes formatted data. - - *args* is an argument list representing arbitrary arguments. - - **Example**:: - - MemoryWriter out; - out.write("Current point:\n"); - out.write("({:+f}, {:+f})", -3.14, 3.14); - - This will write the following output to the ``out`` object: - - .. code-block:: none - - Current point: - (-3.140000, +3.140000) - - The output can be accessed using :func:`data()`, :func:`c_str` or - :func:`str` methods. - - See also :ref:`syntax`. - \endrst - */ - void write(BasicStringRef format, ArgList args) { - BasicFormatter(*this).format(format, args); - } - FMT_VARIADIC_VOID(write, BasicStringRef) - - BasicWriter &operator<<(int value) { - return *this << IntFormatSpec(value); - } - BasicWriter &operator<<(unsigned value) { - return *this << IntFormatSpec(value); - } - BasicWriter &operator<<(long value) { - return *this << IntFormatSpec(value); - } - BasicWriter &operator<<(unsigned long value) { - return *this << IntFormatSpec(value); - } - BasicWriter &operator<<(LongLong value) { - return *this << IntFormatSpec(value); - } - - /** - Formats *value* and writes it to the stream. - */ - BasicWriter &operator<<(ULongLong value) { - return *this << IntFormatSpec(value); - } - - BasicWriter &operator<<(double value) { - write_double(value, FormatSpec()); - return *this; - } - - /** - Formats *value* using the general format for floating-point numbers - (``'g'``) and writes it to the stream. - */ - BasicWriter &operator<<(long double value) { - write_double(value, FormatSpec()); - return *this; - } - - /** - Writes a character to the stream. - */ - BasicWriter &operator<<(char value) { - buffer_.push_back(value); - return *this; - } - - BasicWriter &operator<<(wchar_t value) { - buffer_.push_back(internal::CharTraits::convert(value)); - return *this; - } - - /** - Writes *value* to the stream. - */ - BasicWriter &operator<<(fmt::BasicStringRef value) { - const Char *str = value.c_str(); - buffer_.append(str, str + value.size()); - return *this; - } - - template - BasicWriter &operator<<(IntFormatSpec spec) { - internal::CharTraits::convert(FillChar()); - write_int(spec.value(), spec); - return *this; - } - - template - BasicWriter &operator<<(const StrFormatSpec &spec) { - const StrChar *s = spec.str(); - // TODO: error if fill is not convertible to Char - write_str(s, std::char_traits::length(s), spec); - return *this; - } - - void clear() FMT_NOEXCEPT { buffer_.clear(); } -}; - -template -template -typename BasicWriter::CharPtr BasicWriter::write_str( - const StrChar *s, std::size_t size, const AlignSpec &spec) { - CharPtr out = CharPtr(); - if (spec.width() > size) { - out = grow_buffer(spec.width()); - Char fill = static_cast(spec.fill()); - if (spec.align() == ALIGN_RIGHT) { - std::fill_n(out, spec.width() - size, fill); - out += spec.width() - size; - } else if (spec.align() == ALIGN_CENTER) { - out = fill_padding(out, spec.width(), size, fill); - } else { - std::fill_n(out + size, spec.width() - size, fill); - } - } else { - out = grow_buffer(size); - } - std::copy(s, s + size, out); - return out; -} - -template -typename BasicWriter::CharPtr - BasicWriter::fill_padding( - CharPtr buffer, unsigned total_size, - std::size_t content_size, wchar_t fill) { - std::size_t padding = total_size - content_size; - std::size_t left_padding = padding / 2; - Char fill_char = static_cast(fill); - std::fill_n(buffer, left_padding, fill_char); - buffer += left_padding; - CharPtr content = buffer; - std::fill_n(buffer + content_size, padding - left_padding, fill_char); - return content; -} - -template -template -typename BasicWriter::CharPtr - BasicWriter::prepare_int_buffer( - unsigned num_digits, const Spec &spec, - const char *prefix, unsigned prefix_size) { - unsigned width = spec.width(); - Alignment align = spec.align(); - Char fill = static_cast(spec.fill()); - if (spec.precision() > static_cast(num_digits)) { - // Octal prefix '0' is counted as a digit, so ignore it if precision - // is specified. - if (prefix_size > 0 && prefix[prefix_size - 1] == '0') - --prefix_size; - unsigned number_size = prefix_size + spec.precision(); - AlignSpec subspec(number_size, '0', ALIGN_NUMERIC); - if (number_size >= width) - return prepare_int_buffer(num_digits, subspec, prefix, prefix_size); - buffer_.reserve(width); - unsigned fill_size = width - number_size; - if (align != ALIGN_LEFT) { - CharPtr p = grow_buffer(fill_size); - std::fill(p, p + fill_size, fill); - } - CharPtr result = prepare_int_buffer( - num_digits, subspec, prefix, prefix_size); - if (align == ALIGN_LEFT) { - CharPtr p = grow_buffer(fill_size); - std::fill(p, p + fill_size, fill); - } - return result; - } - unsigned size = prefix_size + num_digits; - if (width <= size) { - CharPtr p = grow_buffer(size); - std::copy(prefix, prefix + prefix_size, p); - return p + size - 1; - } - CharPtr p = grow_buffer(width); - CharPtr end = p + width; - if (align == ALIGN_LEFT) { - std::copy(prefix, prefix + prefix_size, p); - p += size; - std::fill(p, end, fill); - } else if (align == ALIGN_CENTER) { - p = fill_padding(p, width, size, fill); - std::copy(prefix, prefix + prefix_size, p); - p += size; - } else { - if (align == ALIGN_NUMERIC) { - if (prefix_size != 0) { - p = std::copy(prefix, prefix + prefix_size, p); - size -= prefix_size; - } - } else { - std::copy(prefix, prefix + prefix_size, end - size); - } - std::fill(p, end - size, fill); - p = end; - } - return p - 1; -} - -template -template -void BasicWriter::write_int(T value, Spec spec) { - unsigned prefix_size = 0; - typedef typename internal::IntTraits::MainType UnsignedType; - UnsignedType abs_value = value; - char prefix[4] = ""; - if (internal::is_negative(value)) { - prefix[0] = '-'; - ++prefix_size; - abs_value = 0 - abs_value; - } else if (spec.flag(SIGN_FLAG)) { - prefix[0] = spec.flag(PLUS_FLAG) ? '+' : ' '; - ++prefix_size; - } - switch (spec.type()) { - case 0: case 'd': { - unsigned num_digits = internal::count_digits(abs_value); - CharPtr p = prepare_int_buffer( - num_digits, spec, prefix, prefix_size) + 1 - num_digits; - internal::format_decimal(get(p), abs_value, num_digits); - break; - } - case 'x': case 'X': { - UnsignedType n = abs_value; - if (spec.flag(HASH_FLAG)) { - prefix[prefix_size++] = '0'; - prefix[prefix_size++] = spec.type(); - } - unsigned num_digits = 0; - do { - ++num_digits; - } while ((n >>= 4) != 0); - Char *p = get(prepare_int_buffer( - num_digits, spec, prefix, prefix_size)); - n = abs_value; - const char *digits = spec.type() == 'x' ? - "0123456789abcdef" : "0123456789ABCDEF"; - do { - *p-- = digits[n & 0xf]; - } while ((n >>= 4) != 0); - break; - } - case 'b': case 'B': { - UnsignedType n = abs_value; - if (spec.flag(HASH_FLAG)) { - prefix[prefix_size++] = '0'; - prefix[prefix_size++] = spec.type(); - } - unsigned num_digits = 0; - do { - ++num_digits; - } while ((n >>= 1) != 0); - Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size)); - n = abs_value; - do { - *p-- = '0' + (n & 1); - } while ((n >>= 1) != 0); - break; - } - case 'o': { - UnsignedType n = abs_value; - if (spec.flag(HASH_FLAG)) - prefix[prefix_size++] = '0'; - unsigned num_digits = 0; - do { - ++num_digits; - } while ((n >>= 3) != 0); - Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size)); - n = abs_value; - do { - *p-- = '0' + (n & 7); - } while ((n >>= 3) != 0); - break; - } - default: - internal::report_unknown_type( - spec.type(), spec.flag(CHAR_FLAG) ? "char" : "integer"); - break; - } -} - -template -template -void BasicWriter::write_double( - T value, const FormatSpec &spec) { - // Check type. - char type = spec.type(); - bool upper = false; - switch (type) { - case 0: - type = 'g'; - break; - case 'e': case 'f': case 'g': case 'a': - break; - case 'F': -#ifdef _MSC_VER - // MSVC's printf doesn't support 'F'. - type = 'f'; -#endif - // Fall through. - case 'E': case 'G': case 'A': - upper = true; - break; - default: - internal::report_unknown_type(type, "double"); - break; - } - - char sign = 0; - // Use getsign instead of value < 0 because the latter is always - // false for NaN. - if (internal::getsign(static_cast(value))) { - sign = '-'; - value = -value; - } else if (spec.flag(SIGN_FLAG)) { - sign = spec.flag(PLUS_FLAG) ? '+' : ' '; - } - - if (value != value) { - // Format NaN ourselves because sprintf's output is not consistent - // across platforms. - std::size_t nan_size = 4; - const char *nan = upper ? " NAN" : " nan"; - if (!sign) { - --nan_size; - ++nan; - } - CharPtr out = write_str(nan, nan_size, spec); - if (sign) - *out = sign; - return; - } - - if (internal::isinfinity(value)) { - // Format infinity ourselves because sprintf's output is not consistent - // across platforms. - std::size_t inf_size = 4; - const char *inf = upper ? " INF" : " inf"; - if (!sign) { - --inf_size; - ++inf; - } - CharPtr out = write_str(inf, inf_size, spec); - if (sign) - *out = sign; - return; - } - - std::size_t offset = buffer_.size(); - unsigned width = spec.width(); - if (sign) { - buffer_.reserve(buffer_.size() + (std::max)(width, 1u)); - if (width > 0) - --width; - ++offset; - } - - // Build format string. - enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg - Char format[MAX_FORMAT_SIZE]; - Char *format_ptr = format; - *format_ptr++ = '%'; - unsigned width_for_sprintf = width; - if (spec.flag(HASH_FLAG)) - *format_ptr++ = '#'; - if (spec.align() == ALIGN_CENTER) { - width_for_sprintf = 0; - } else { - if (spec.align() == ALIGN_LEFT) - *format_ptr++ = '-'; - if (width != 0) - *format_ptr++ = '*'; - } - if (spec.precision() >= 0) { - *format_ptr++ = '.'; - *format_ptr++ = '*'; - } - - append_float_length(format_ptr, value); - *format_ptr++ = type; - *format_ptr = '\0'; - - // Format using snprintf. - Char fill = static_cast(spec.fill()); - for (;;) { - std::size_t buffer_size = buffer_.capacity() - offset; -#if _MSC_VER - // MSVC's vsnprintf_s doesn't work with zero size, so reserve - // space for at least one extra character to make the size non-zero. - // Note that the buffer's capacity will increase by more than 1. - if (buffer_size == 0) { - buffer_.reserve(offset + 1); - buffer_size = buffer_.capacity() - offset; - } -#endif - Char *start = &buffer_[offset]; - int n = internal::CharTraits::format_float( - start, buffer_size, format, width_for_sprintf, spec.precision(), value); - if (n >= 0 && offset + n < buffer_.capacity()) { - if (sign) { - if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) || - *start != ' ') { - *(start - 1) = sign; - sign = 0; - } else { - *(start - 1) = fill; - } - ++n; - } - if (spec.align() == ALIGN_CENTER && - spec.width() > static_cast(n)) { - width = spec.width(); - CharPtr p = grow_buffer(width); - std::copy(p, p + n, p + (width - n) / 2); - fill_padding(p, spec.width(), n, fill); - return; - } - if (spec.fill() != ' ' || sign) { - while (*start == ' ') - *start++ = fill; - if (sign) - *(start - 1) = sign; - } - grow_buffer(n); - return; - } - // If n is negative we ask to increase the capacity by at least 1, - // but as std::vector, the buffer grows exponentially. - buffer_.reserve(n >= 0 ? offset + n + 1 : buffer_.capacity() + 1); - } -} - -/** - \rst - This template provides operations for formatting and writing data into - a character stream. The output is stored in a memory buffer that grows - dynamically. - - You can use one of the following typedefs for common character types - and the standard allocator: - - +---------------+-----------------------------------------------+ - | Type | Definition | - +===============+===============================================+ - | MemoryWriter | BasicWriter> | - +---------------+-----------------------------------------------+ - | WMemoryWriter | BasicWriter> | - +---------------+-----------------------------------------------+ - - **Example**:: - - MemoryWriter out; - out << "The answer is " << 42 << "\n"; - out.write("({:+f}, {:+f})", -3.14, 3.14); - - This will write the following output to the ``out`` object: - - .. code-block:: none - - The answer is 42 - (-3.140000, +3.140000) - - The output can be converted to an ``std::string`` with ``out.str()`` or - accessed as a C string with ``out.c_str()``. - \endrst - */ -template > -class BasicMemoryWriter : public BasicWriter { - private: - internal::MemoryBuffer buffer_; - - public: - explicit BasicMemoryWriter(const Allocator& alloc = Allocator()) - : BasicWriter(buffer_), buffer_(alloc) {} - -#if FMT_USE_RVALUE_REFERENCES - /** - Constructs a :class:`fmt::BasicMemoryWriter` object moving the content - of the other object to it. - */ - BasicMemoryWriter(BasicMemoryWriter &&other) - : BasicWriter(buffer_), buffer_(std::move(other.buffer_)) { - } - - /** - Moves the content of the other ``BasicMemoryWriter`` object to this one. - */ - BasicMemoryWriter &operator=(BasicMemoryWriter &&other) { - buffer_ = std::move(other.buffer_); - return *this; - } -#endif -}; - -typedef BasicMemoryWriter MemoryWriter; -typedef BasicMemoryWriter WMemoryWriter; - -// Formats a value. -template -void format(BasicFormatter &f, const Char *&format_str, const T &value) { - std::basic_ostringstream os; - os << value; - internal::Arg arg; - internal::Value &arg_value = arg; - std::basic_string str = os.str(); - arg_value = internal::MakeValue(str); - arg.type = static_cast(internal::MakeValue::type(str)); - format_str = f.format(format_str, arg); -} - -// Reports a system error without throwing an exception. -// Can be used to report errors from destructors. -void report_system_error(int error_code, StringRef message) FMT_NOEXCEPT; - -#ifdef _WIN32 - -/** A Windows error. */ -class WindowsError : public SystemError { - private: - void init(int error_code, StringRef format_str, ArgList args); - - public: - /** - \rst - Constructs a :class:`fmt::WindowsError` object with the description - of the form - - .. parsed-literal:: - **: ** - - where ** is the formatted message and ** is the system - message corresponding to the error code. - *error_code* is a Windows error code as given by ``GetLastError``. - If *error_code* is not a valid error code such as -1, the system message - will look like "error -1". - - **Example**:: - - // This throws a WindowsError with the description - // cannot open file 'madeup': The system cannot find the file specified. - // or similar (system message may vary). - const char *filename = "madeup"; - LPOFSTRUCT of = LPOFSTRUCT(); - HFILE file = OpenFile(filename, &of, OF_READ); - if (file == HFILE_ERROR) - throw fmt::WindowsError(GetLastError(), "cannot open file '{}'", filename); - \endrst - */ - WindowsError(int error_code, StringRef message) { - init(error_code, message, ArgList()); - } - FMT_VARIADIC_CTOR(WindowsError, init, int, StringRef) -}; - -// Reports a Windows error without throwing an exception. -// Can be used to report errors from destructors. -void report_windows_error(int error_code, StringRef message) FMT_NOEXCEPT; - -#endif - -enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE }; - -/** - Formats a string and prints it to stdout using ANSI escape sequences - to specify color (experimental). - Example: - PrintColored(fmt::RED, "Elapsed time: {0:.2f} seconds") << 1.23; - */ -void print_colored(Color c, StringRef format, ArgList args); - -/** - \rst - Formats arguments and returns the result as a string. - - **Example**:: - - std::string message = format("The answer is {}", 42); - \endrst -*/ -inline std::string format(StringRef format_str, ArgList args) { - MemoryWriter w; - w.write(format_str, args); - return w.str(); -} - -inline std::wstring format(WStringRef format_str, ArgList args) { - WMemoryWriter w; - w.write(format_str, args); - return w.str(); -} - -/** - \rst - Prints formatted data to the file *f*. - - **Example**:: - - print(stderr, "Don't {}!", "panic"); - \endrst - */ -void print(std::FILE *f, StringRef format_str, ArgList args); - -/** - \rst - Prints formatted data to ``stdout``. - - **Example**:: - - print("Elapsed time: {0:.2f} seconds", 1.23); - \endrst - */ -void print(StringRef format_str, ArgList args); - -/** - \rst - Prints formatted data to the stream *os*. - - **Example**:: - - print(cerr, "Don't {}!", "panic"); - \endrst - */ -void print(std::ostream &os, StringRef format_str, ArgList args); - -template -void printf(BasicWriter &w, BasicStringRef format, ArgList args) { - internal::PrintfFormatter().format(w, format, args); -} - -/** - \rst - Formats arguments and returns the result as a string. - - **Example**:: - - std::string message = fmt::sprintf("The answer is %d", 42); - \endrst -*/ -inline std::string sprintf(StringRef format, ArgList args) { - MemoryWriter w; - printf(w, format, args); - return w.str(); -} - -/** - \rst - Prints formatted data to the file *f*. - - **Example**:: - - fmt::fprintf(stderr, "Don't %s!", "panic"); - \endrst - */ -int fprintf(std::FILE *f, StringRef format, ArgList args); - -/** - \rst - Prints formatted data to ``stdout``. - - **Example**:: - - fmt::printf("Elapsed time: %.2f seconds", 1.23); - \endrst - */ -inline int printf(StringRef format, ArgList args) { - return fprintf(stdout, format, args); -} - -/** - Fast integer formatter. - */ -class FormatInt { - private: - // Buffer should be large enough to hold all digits (digits10 + 1), - // a sign and a null character. - enum {BUFFER_SIZE = std::numeric_limits::digits10 + 3}; - mutable char buffer_[BUFFER_SIZE]; - char *str_; - - // Formats value in reverse and returns the number of digits. - char *format_decimal(ULongLong value) { - char *buffer_end = buffer_ + BUFFER_SIZE - 1; - while (value >= 100) { - // Integer division is slow so do it for a group of two digits instead - // of for every digit. The idea comes from the talk by Alexandrescu - // "Three Optimization Tips for C++". See speed-test for a comparison. - unsigned index = (value % 100) * 2; - value /= 100; - *--buffer_end = internal::Data::DIGITS[index + 1]; - *--buffer_end = internal::Data::DIGITS[index]; - } - if (value < 10) { - *--buffer_end = static_cast('0' + value); - return buffer_end; - } - unsigned index = static_cast(value * 2); - *--buffer_end = internal::Data::DIGITS[index + 1]; - *--buffer_end = internal::Data::DIGITS[index]; - return buffer_end; - } - - void FormatSigned(LongLong value) { - ULongLong abs_value = static_cast(value); - bool negative = value < 0; - if (negative) - abs_value = 0 - abs_value; - str_ = format_decimal(abs_value); - if (negative) - *--str_ = '-'; - } - - public: - explicit FormatInt(int value) { FormatSigned(value); } - explicit FormatInt(long value) { FormatSigned(value); } - explicit FormatInt(LongLong value) { FormatSigned(value); } - explicit FormatInt(unsigned value) : str_(format_decimal(value)) {} - explicit FormatInt(unsigned long value) : str_(format_decimal(value)) {} - explicit FormatInt(ULongLong value) : str_(format_decimal(value)) {} - - /** - Returns the number of characters written to the output buffer. - */ - std::size_t size() const { return buffer_ - str_ + BUFFER_SIZE - 1; } - - /** - Returns a pointer to the output buffer content. No terminating null - character is appended. - */ - const char *data() const { return str_; } - - /** - Returns a pointer to the output buffer content with terminating null - character appended. - */ - const char *c_str() const { - buffer_[BUFFER_SIZE - 1] = '\0'; - return str_; - } - - /** - Returns the content of the output buffer as an `std::string`. - */ - std::string str() const { return std::string(str_, size()); } -}; - -// Formats a decimal integer value writing into buffer and returns -// a pointer to the end of the formatted string. This function doesn't -// write a terminating null character. -template -inline void format_decimal(char *&buffer, T value) { - typename internal::IntTraits::MainType abs_value = value; - if (internal::is_negative(value)) { - *buffer++ = '-'; - abs_value = 0 - abs_value; - } - if (abs_value < 100) { - if (abs_value < 10) { - *buffer++ = static_cast('0' + abs_value); - return; - } - unsigned index = static_cast(abs_value * 2); - *buffer++ = internal::Data::DIGITS[index]; - *buffer++ = internal::Data::DIGITS[index + 1]; - return; - } - unsigned num_digits = internal::count_digits(abs_value); - internal::format_decimal(buffer, abs_value, num_digits); - buffer += num_digits; -} -} - -#if FMT_GCC_VERSION -// Use the system_header pragma to suppress warnings about variadic macros -// because suppressing -Wvariadic-macros with the diagnostic pragma doesn't -// work. It is used at the end because we want to suppress as little warnings -// as possible. -# pragma GCC system_header -#endif - -// This is used to work around VC++ bugs in handling variadic macros. -#define FMT_EXPAND(args) args - -// Returns the number of arguments. -// Based on https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s. -#define FMT_NARG(...) FMT_NARG_(__VA_ARGS__, FMT_RSEQ_N()) -#define FMT_NARG_(...) FMT_EXPAND(FMT_ARG_N(__VA_ARGS__)) -#define FMT_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N -#define FMT_RSEQ_N() 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 - -#define FMT_CONCAT(a, b) a##b -#define FMT_FOR_EACH_(N, f, ...) \ - FMT_EXPAND(FMT_CONCAT(FMT_FOR_EACH, N)(f, __VA_ARGS__)) -#define FMT_FOR_EACH(f, ...) \ - FMT_EXPAND(FMT_FOR_EACH_(FMT_NARG(__VA_ARGS__), f, __VA_ARGS__)) - -#define FMT_ADD_ARG_NAME(type, index) type arg##index -#define FMT_GET_ARG_NAME(type, index) arg##index - -#if FMT_USE_VARIADIC_TEMPLATES -# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ - template \ - ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ - const Args & ... args) { \ - using fmt::internal::Value; \ - const Value values[fmt::internal::NonZero::VALUE] = { \ - fmt::internal::MakeValue(args)... \ - }; \ - call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \ - fmt::internal::make_type(args...), values)); \ - } -#else -// Defines a wrapper for a function taking __VA_ARGS__ arguments -// and n additional arguments of arbitrary types. -# define FMT_WRAP(Char, ReturnType, func, call, n, ...) \ - template \ - inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ - FMT_GEN(n, FMT_MAKE_ARG)) { \ - const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF_##Char)}; \ - call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \ - fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), vals)); \ - } - -# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ - inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__)) { \ - call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList()); \ - } \ - FMT_WRAP(Char, ReturnType, func, call, 1, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 2, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 3, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 4, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 5, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 6, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 7, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 8, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 9, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 10, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 11, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 12, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 13, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 14, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 15, __VA_ARGS__) -#endif // FMT_USE_VARIADIC_TEMPLATES - -/** - \rst - Defines a variadic function with the specified return type, function name - and argument types passed as variable arguments to this macro. - - **Example**:: - - void print_error(const char *file, int line, const char *format, - fmt::ArgList args) { - fmt::print("{}: {}: ", file, line); - fmt::print(format, args); - } - FMT_VARIADIC(void, print_error, const char *, int, const char *) - - ``FMT_VARIADIC`` is used for compatibility with legacy C++ compilers that - don't implement variadic templates. You don't have to use this macro if - you don't need legacy compiler support and can use variadic templates - directly:: - - template - void print_error(const char *file, int line, const char *format, - const Args & ... args) { - fmt::print("{}: {}: ", file, line); - fmt::print(format, args...); - } - \endrst - */ -#define FMT_VARIADIC(ReturnType, func, ...) \ - FMT_VARIADIC_(char, ReturnType, func, return func, __VA_ARGS__) - -#define FMT_VARIADIC_W(ReturnType, func, ...) \ - FMT_VARIADIC_(wchar_t, ReturnType, func, return func, __VA_ARGS__) - -namespace fmt { -FMT_VARIADIC(std::string, format, StringRef) -FMT_VARIADIC_W(std::wstring, format, WStringRef) -FMT_VARIADIC(void, print, StringRef) -FMT_VARIADIC(void, print, std::FILE *, StringRef) -FMT_VARIADIC(void, print, std::ostream &, StringRef) -FMT_VARIADIC(void, print_colored, Color, StringRef) -FMT_VARIADIC(std::string, sprintf, StringRef) -FMT_VARIADIC(int, printf, StringRef) -FMT_VARIADIC(int, fprintf, std::FILE *, StringRef) -} - -// Restore warnings. -#if FMT_GCC_VERSION >= 406 -# pragma GCC diagnostic pop -#endif - -#ifdef __clang__ -# pragma clang diagnostic pop -#endif - -#ifdef FMT_HEADER_ONLY -# include "format.cc" -#endif - -#endif // FMT_FORMAT_H_ diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/grid/box.h b/src/Zigzag_persistence/example/ext_zz/dionysus/grid/box.h deleted file mode 100644 index 5602141cb8..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/grid/box.h +++ /dev/null @@ -1,136 +0,0 @@ -#ifndef BOX_H -#define BOX_H - -#include -#include - -#include "grid.h" -#include "vertices.h" - -namespace grid -{ - -namespace ba = boost::adaptors; - -template -class Box -{ - public: - typedef GridRef GridProxy; - typedef typename GridProxy::Vertex Position; - - struct InternalTest; - struct BoundaryTest; - struct BoundsTest; - struct PositionToVertex; - - class FreudenthalLinkIterator; - typedef boost::iterator_range FreudenthalLinkRange; - - typedef VerticesIterator VI; - typedef boost::transformed_range - > VertexRange; - - // Topology interface - typedef typename GridProxy::Index Vertex; - typedef boost::transformed_range - > Link; - - - Box(): g_(0, Position()) {} - Box(const Position& shape): - g_(0, shape), to_(shape - Position::one()) {} - Box(const Position& shape, - const Position& from, - const Position& to): - g_(0, shape), from_(from), to_(to) {} - - - const Position& from() const { return from_; } - const Position& to() const { return to_; } - Position& from() { return from_; } - Position& to() { return to_; } - Position shape() const { return to_ - from_ + Position::one(); } - const Position& grid_shape() const { return g_.shape(); } - static unsigned dimension() { return D; } - - size_t size() const { size_t c = 1; for (unsigned i = 0; i < D; ++i) c *= (to_[i] - from_[i] + 1); return c; } - - VertexRange vertices() const { return boost::iterator_range(VI::begin(from_, to_), VI::end(from_, to_)) - | ba::transformed(position_to_vertex()); } - Link link(const Position& p) const { return FreudenthalLinkRange(FreudenthalLinkIterator::begin(p), FreudenthalLinkIterator::end(p)) - | ba::filtered(bounds_test()) - | ba::transformed(position_to_vertex()); } - Link link(const Vertex& v) const { return link(position(v)); } - - Box intersect(const Box& other) const; - bool intersects(const Box& other) const; - void merge(const Box& other); - - bool contains(const Position& p) const; - bool contains(const Vertex& v) const { return contains(position(v)); } - - bool boundary(const Position& p, bool degenerate = false) const; - bool boundary(const Vertex& v, bool deg = false) const { return boundary(position(v), deg); } - Box side(unsigned axis, bool upper) const; - - InternalTest internal_test() const { return InternalTest(*this); } - BoundaryTest boundary_test() const { return BoundaryTest(*this); } - BoundsTest bounds_test() const { return BoundsTest(*this); } - PositionToVertex position_to_vertex() const { return PositionToVertex(*this); } - - - void swap(Box& other) { g_.swap(other.g_); std::swap(from_, other.from_); std::swap(to_, other.to_); } - - bool operator==(const Box& other) const { return from_ == other.from_ && to_ == other.to_; } - - template - friend std::basic_ostream& - operator<<(std::basic_ostream& out, const Box& b) { out << "Box: " << b.from_ << " - " << b.to_ << " inside " << b.g_.shape(); return out; } - - struct InternalTest - { - InternalTest(const Box& box): box_(box) {} - bool operator()(const Vertex& v) const { return !box_.boundary(v); } - const Box& box_; - }; - - struct BoundaryTest - { - BoundaryTest(const Box& box): box_(box) {} - bool operator()(const Vertex& v) const { return box_.boundary(v); } - const Box& box_; - }; - - struct BoundsTest - { - BoundsTest(const Box& box): box_(box) {} - bool operator()(const Position& p) const { return box_.contains(p); } - bool operator()(const Vertex& v) const { return box_.contains(v); } - const Box& box_; - }; - - struct PositionToVertex - { - typedef Vertex result_type; - PositionToVertex(const Box& box): box_(box) {} - Vertex operator()(Position p) const { for (unsigned i = 0; i < D; ++i) p[i] %= box_.grid_shape()[i]; return box_.g_.index(p); } - const Box& box_; - }; - - // computes position inside the box (adjusted for the wrap-around, if need be) - Position position(const Vertex& v) const { Position p = g_.vertex(v); for (unsigned i = 0; i < D; ++i) if (p[i] < from()[i]) p[i] += grid_shape()[i]; return p; } - - private: - GridProxy g_; - Position from_, to_; -}; - -} - -#include "box.hpp" - -#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/grid/box.hpp b/src/Zigzag_persistence/example/ext_zz/dionysus/grid/box.hpp deleted file mode 100644 index f3af50c047..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/grid/box.hpp +++ /dev/null @@ -1,141 +0,0 @@ -template -grid::Box -grid::Box:: -intersect(const Box& other) const -{ - Position from, to; - for (unsigned i = 0; i < D; ++i) - { - from[i] = std::max(from_[i], other.from_[i]); - to[i] = std::min(to_[i], other.to_[i]); - } - - return Box(g_, from, to); -} - -template -bool -grid::Box:: -intersects(const Box& other) const -{ - for (unsigned i = 0; i < D; ++i) - if (std::max(from_[i], other.from_[i]) > std::min(to_[i], other.to_[i])) - return false; - - return true; -} - -template -bool -grid::Box:: -contains(const Position& p) const -{ - for (unsigned i = 0; i < D; ++i) - if (p[i] > to_[i] || p[i] < from_[i]) - return false; - return true; -} - -template -bool -grid::Box:: -boundary(const Position& p, bool degenerate) const -{ - for (unsigned i = 0; i < D; ++i) - { - if (degenerate && from_[i] == to_[i]) continue; - if (p[i] == from_[i] || p[i] == to_[i]) - return true; - } - - return false; -} - -template -grid::Box -grid::Box:: -side(unsigned axis, bool upper) const -{ - Box res(*this); - - if (upper) - res.from()[axis] = res.to()[axis]; - else - res.to()[axis] = res.from()[axis]; - - return res; -} - -template -void -grid::Box:: -merge(const Box& other) -{ - for (unsigned i = 0; i < D; ++i) - { - from_[i] = std::min(from_[i], other.from_[i]); - to_[i] = std::max(to_[i], other.to_[i]); - } -} - -/* Box::FreudenthalLinkIterator */ -template -class grid::Box::FreudenthalLinkIterator: - public boost::iterator_facade -{ - typedef boost::iterator_facade Parent; - - - public: - typedef typename Parent::value_type value_type; - typedef typename Parent::difference_type difference_type; - typedef typename Parent::reference reference; - - FreudenthalLinkIterator(): loc_(0), dir_(0) {} - FreudenthalLinkIterator(const Position& p, int loc = 0, int dir = 1): - p_(p), v_(p), loc_(loc), dir_(dir) {} - - static FreudenthalLinkIterator - begin(const Position& p) { FreudenthalLinkIterator it(p); ++it; return it; } - static FreudenthalLinkIterator - end(const Position& p) { return FreudenthalLinkIterator(p, 0, -1); } - - private: - void increment(); - bool equal(const FreudenthalLinkIterator& other) const { return v_ == other.v_; } - reference dereference() const { return v_; } - - friend class ::boost::iterator_core_access; - - private: - Position p_, v_; - int loc_; - int dir_; -}; - -template -void -grid::Box::FreudenthalLinkIterator:: -increment() -{ - loc_ += dir_; - if (loc_ == (1 << D)) - { - dir_ = -1; - loc_ += dir_; - } - - for (unsigned i = 0; i < D; ++i) - if (loc_ & (1 << i)) - v_[i] = p_[i] + dir_; - else - v_[i] = p_[i]; -} diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/grid/grid.h b/src/Zigzag_persistence/example/ext_zz/dionysus/grid/grid.h deleted file mode 100644 index c63fc7c598..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/grid/grid.h +++ /dev/null @@ -1,143 +0,0 @@ -#ifndef GRID_H -#define GRID_H - -#include "point.h" - -namespace grid -{ - -template -struct Grid; - -template -struct GridRef -{ - public: - typedef C Value; - - typedef Point Vertex; - typedef size_t Index; - - public: - template - GridRef(C* data, const Point& shape, bool c_order = true): - data_(data), shape_(shape), c_order_(c_order) { set_stride(); } - - GridRef(Grid& g): - data_(g.data()), shape_(g.shape()), - c_order_(g.c_order()) { set_stride(); } - - template - C operator()(const Point& v) const { return data_[v*stride_]; } - - template - C& operator()(const Point& v) { return data_[v*stride_]; } - - C operator()(Index i) const { return data_[i]; } - C& operator()(Index i) { return data_[i]; } - - const Vertex& - shape() const { return shape_; } - - const C* - data() const { return data_; } - C* data() { return data_; } - - // Set every element to the given value - GridRef& operator=(C value) { Index s = size(); for (Index i = 0; i < s; ++i) data_[i] = value; return *this; } - GridRef& operator/=(C value) { Index s = size(); for (Index i = 0; i < s; ++i) data_[i] /= value; return *this; } - - Vertex vertex(Index idx) const { Vertex v; for (unsigned i = 0; i < D; ++i) { v[i] = idx / stride_[i]; idx %= stride_[i]; } return v; } - Index index(const Vertex& v) const { return v*stride_; } - - Index size() const { return size(shape()); } - void swap(GridRef& other) { std::swap(data_, other.data_); std::swap(shape_, other.shape_); std::swap(stride_, other.stride_); } - - bool c_order() const { return c_order_; } - - protected: - static Index - size(const Vertex& v) { Index res = 1; for (unsigned i = 0; i < D; ++i) res *= v[i]; return res; } - - void set_stride() - { - Index cur = 1; - if (c_order_) - for (unsigned i = D; i > 0; --i) { stride_[i-1] = cur; cur *= shape_[i-1]; } - else - for (unsigned i = 0; i < D; ++i) { stride_[i] = cur; cur *= shape_[i]; } - - } - void set_shape(const Vertex& v) { shape_ = v; set_stride(); } - void set_data(C* data) { data_ = data; } - void set_c_order(bool order) { c_order_ = order; } - - private: - C* data_; - Vertex shape_; - Vertex stride_; - bool c_order_; -}; - - -template -struct Grid: public GridRef -{ - public: - typedef GridRef Parent; - typedef typename Parent::Value Value; - typedef typename Parent::Index Index; - typedef Parent Reference; - - template - struct rebind { typedef Grid type; }; - - public: - template - Grid(const Point& shape, bool c_order = true): - Parent(new C[size(shape)], shape, c_order) - {} - - Grid(const Parent& g): - Parent(new C[size(g.shape())], g.shape(), - g.c_order()) { copy_data(g.data()); } - - template - Grid(const OtherGrid& g): - Parent(new C[size(g.shape())], - g.shape(), - g.c_order()) { copy_data(g.data()); } - - ~Grid() { delete[] Parent::data(); } - - template - Grid& operator=(const GridRef& other) - { - delete[] Parent::data(); - Parent::set_c_order(other.c_order()); // NB: order needs to be set before the shape, to set the stride correctly - Parent::set_shape(other.shape()); - Index s = size(shape()); - Parent::set_data(new C[s]); - copy_data(other.data()); - return *this; - } - - using Parent::data; - using Parent::shape; - using Parent::operator(); - using Parent::operator=; - using Parent::size; - - private: - template - void copy_data(const OC* data) - { - Index s = size(shape()); - for (Index i = 0; i < s; ++i) - Parent::data()[i] = data[i]; - } -}; - -} - -#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/grid/point.h b/src/Zigzag_persistence/example/ext_zz/dionysus/grid/point.h deleted file mode 100644 index 0e867c34aa..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/grid/point.h +++ /dev/null @@ -1,132 +0,0 @@ -#ifndef POINT_H -#define POINT_H - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -namespace grid -{ - -template -class Point: public boost::array, - private boost::addable< Point // Point + Point - , boost::subtractable< Point // Point - Point - , boost::dividable2< Point, Coordinate_ // Point / Coordinate - , boost::multipliable2< Point, Coordinate_ // Point * Coordinate, Coordinate * Point - > > > > -{ - public: - typedef Coordinate_ Coordinate; - typedef boost::array ArrayParent; - - typedef Point LPoint; - typedef Point UPoint; - - template - struct rebind { typedef Point type; }; - - public: - Point() { for (unsigned i = 0; i < D; ++i) (*this)[i] = 0; } - Point(const ArrayParent& a): - ArrayParent(a) {} - template Point(const Point& p) { for (size_t i = 0; i < D; ++i) (*this)[i] = p[i]; } - template Point(const T* a) { for (unsigned i = 0; i < D; ++i) (*this)[i] = a[i]; } - template Point(const std::vector& a) { for (unsigned i = 0; i < D; ++i) (*this)[i] = a[i]; } - - static - unsigned dimension() { return D; } - - static Point zero() { return Point(); } - static Point one() { Point p; for (unsigned i = 0; i < D; ++i) p[i] = 1; return p; } - - LPoint drop(int dim) const { LPoint p; unsigned c = 0; for (unsigned i = 0; i < D; ++i) { if (i == dim) continue; p[c++] = (*this)[i]; } return p; } - UPoint lift(int dim, Coordinate x) const { UPoint p; for (unsigned i = 0; i < D+1; ++i) { if (i < dim) p[i] = (*this)[i]; else if (i == dim) p[i] = x; else if (i > dim) p[i] = (*this)[i-1]; } return p; } - - using ArrayParent::operator[]; - - Point& operator+=(const Point& y) { for (unsigned i = 0; i < D; ++i) (*this)[i] += y[i]; return *this; } - Point& operator-=(const Point& y) { for (unsigned i = 0; i < D; ++i) (*this)[i] -= y[i]; return *this; } - Point& operator*=(Coordinate a) { for (unsigned i = 0; i < D; ++i) (*this)[i] *= a; return *this; } - Point& operator/=(Coordinate a) { for (unsigned i = 0; i < D; ++i) (*this)[i] /= a; return *this; } - - Point operator-() const { Point res; for (unsigned i = 0; i < D; ++i) res[i] = -(*this)[i]; return res; } - - Coordinate norm() const { return (*this)*(*this); } - - std::ostream& operator<<(std::ostream& out) const { out << (*this)[0]; for (unsigned i = 1; i < D; ++i) out << " " << (*this)[i]; return out; } - std::istream& operator>>(std::istream& in); - - friend - Coordinate operator*(const Point& x, const Point& y) { Coordinate n = 0; for (size_t i = 0; i < D; ++i) n += x[i] * y[i]; return n; } - - template - friend - Coordinate operator*(const Point& x, const Point& y) { Coordinate n = 0; for (size_t i = 0; i < D; ++i) n += x[i] * y[i]; return n; } - - private: - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) { ar & boost::serialization::base_object(*this); } -}; - -template -std::istream& -Point:: -operator>>(std::istream& in) -{ - std::string point_str; - in >> point_str; // read until ' ' - std::stringstream ps(point_str); - - char x; - for (unsigned i = 0; i < dimension(); ++i) - { - ps >> (*this)[i]; - ps >> x; - } - - return in; -} - - -template -Coordinate norm2(const Point& p) -{ Coordinate res = 0; for (unsigned i = 0; i < D; ++i) res += p[i]*p[i]; return res; } - -template -std::ostream& -operator<<(std::ostream& out, const Point& p) -{ return p.operator<<(out); } - -template -std::istream& -operator>>(std::istream& in, Point& p) -{ return p.operator>>(in); } - -} - -namespace opts -{ - template - struct Traits; - - template - struct Traits< grid::Point > - { - static - std::string type_string() { return "POINT"; } - }; -} - - -#endif // POINT_H diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/grid/vertices.h b/src/Zigzag_persistence/example/ext_zz/dionysus/grid/vertices.h deleted file mode 100644 index 339782e8c4..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/grid/vertices.h +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef VERTICES_H -#define VERTICES_H - -#include - -namespace grid -{ - -template -class VerticesIterator: - public boost::iterator_facade, - Vertex_, - boost::forward_traversal_tag, - Vertex_, - std::ptrdiff_t> -{ - typedef boost::iterator_facade Parent; - - - public: - typedef typename Parent::value_type value_type; - typedef typename Parent::difference_type difference_type; - typedef typename Parent::reference reference; - - typedef value_type Vertex; - typedef typename Vertex::Coordinate Coordinate; - - // upper bounds are non-inclusive - VerticesIterator(const Vertex& bounds): - to_(bounds - Vertex::one()) {} - - VerticesIterator(const Vertex& pos, - const Vertex& bounds): - pos_(pos), to_(bounds - Vertex::one()) {} - - VerticesIterator(const Vertex& pos, - const Vertex& from, - const Vertex& to): - pos_(pos), from_(from), - to_(to) {} - - - static VerticesIterator - begin(const Vertex& bounds) { return VerticesIterator(bounds); } - static VerticesIterator - end(const Vertex& bounds) { Vertex e; e[0] = bounds[0]; return VerticesIterator(e, bounds); } - - static VerticesIterator - begin(const Vertex& from, const Vertex& to) { return VerticesIterator(from, from, to); } - static VerticesIterator - end(const Vertex& from, const Vertex& to) { Vertex e = from; e[0] = to[0] + 1; return VerticesIterator(e, from, to); } - - private: - void increment(); - bool equal(const VerticesIterator& other) const { return pos_ == other.pos_; } - reference dereference() const { return pos_; } - - friend class ::boost::iterator_core_access; - - private: - Vertex pos_; - Vertex from_; - Vertex to_; -}; - -} - -template -void -grid::VerticesIterator:: -increment() -{ - unsigned j = Vertex::dimension() - 1; - while (j > 0 && pos_[j] == to_[j]) - { - pos_[j] = from_[j]; - --j; - } - ++pos_[j]; -} - -#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/matrix-filtration.h b/src/Zigzag_persistence/example/ext_zz/dionysus/matrix-filtration.h deleted file mode 100644 index 516f27b2d1..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/matrix-filtration.h +++ /dev/null @@ -1,120 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace dionysus -{ - - -template -class MatrixFiltrationCell; - - -// adapt Matrix as a Filtration to make it possible to feed into reduction algorithms -template -class MatrixFiltration -{ - public: - using Matrix = Matrix_; - using CellValue = CellValue_; - using Dimensions = std::vector; - using Values = std::vector; - using Cell = MatrixFiltrationCell; - - - public: - MatrixFiltration(Matrix m, Dimensions dimensions, Values values): - m_(std::move(m)), - dimensions_(dimensions), - values_(values) { assert(m_->size() == dimensions_.size()); assert(m_->size() == values_.size()); } - - Cell operator[](size_t i) const { return Cell(this, i); } - size_t size() const { return m_.size(); } - - size_t index(const Cell& c) const; - - Cell begin() const { return Cell(this, 0); } - Cell end() const { return Cell(this, size()); } - - const Dimensions& dimensions() const { return dimensions_; } - const Values& values() const { return values_; } - - private: - Matrix m_; - Dimensions dimensions_; - Values values_; - - friend class MatrixFiltrationCell; -}; - - -template -class MatrixFiltrationCell -{ - public: - using MatrixFiltration = MatrixFiltration_; - using Matrix = typename MatrixFiltration::Matrix; - using Data = typename MatrixFiltration::CellValue; - using Field = typename Matrix::Field; - - template - using Entry = ChainEntry; - - template - using BoundaryChain = std::vector>; - - public: - MatrixFiltrationCell(const MatrixFiltration* mf, size_t i): - mf_(mf), i_(i) {} - - short unsigned dimension() const { return mf_->dimensions_[i_]; } - const Data& data() const { return mf_->values_[i_]; } - - bool operator==(const MatrixFiltrationCell& other) const { return i_ == other.i_; } - bool operator!=(const MatrixFiltrationCell& other) const { return i_ != other.i_; } - - BoundaryChain<> boundary() const - { - BoundaryChain<> bdry; - for (auto& entry : (mf_->m_)[i_]) - bdry.emplace_back(Entry<> { entry.e, MatrixFiltrationCell(mf_, entry.i) }); - return bdry; - } - - template - BoundaryChain boundary(const Field_& field) const - { - BoundaryChain bdry; - for (auto& entry : (mf_->m_)[i_]) - bdry.emplace_back(Entry { field.init(entry.e), MatrixFiltrationCell(mf_, entry.i) }); - return bdry; - } - - // iterator interface - MatrixFiltrationCell operator++(int) { MatrixFiltrationCell copy = *this; i_++; return copy; } - MatrixFiltrationCell& operator++() { ++i_; return *this; } - - const MatrixFiltrationCell& operator*() const { return *this; } - MatrixFiltrationCell& operator*() { return *this; } - - size_t i() const { return i_; } - - friend - std::ostream& operator<<(std::ostream& out, const MatrixFiltrationCell& c) - { out << c.i_; return out; } - - private: - const MatrixFiltration* mf_ = nullptr; - size_t i_; -}; - -template -size_t -MatrixFiltration::index(const Cell& c) const -{ - return c.i(); -} - -} diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/omni-field-persistence.h b/src/Zigzag_persistence/example/ext_zz/dionysus/omni-field-persistence.h deleted file mode 100644 index 3d83d4ae44..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/omni-field-persistence.h +++ /dev/null @@ -1,145 +0,0 @@ -#ifndef DIONYSUS_OMNI_FIELD_REDUCTION_H -#define DIONYSUS_OMNI_FIELD_REDUCTION_H - -#include -#include - -#include "reduction.h" // for unpaired -#include "fields/q.h" -#include "fields/zp.h" -#include "chain.h" - -namespace dionysus -{ - -template, class Q_ = ::dionysus::Q<>, class Zp_ = ::dionysus::ZpField> -class OmniFieldPersistence -{ - public: - using Index = Index_; - using Q = Q_; - using Field = Q; - using Comparison = Comparison_; - - using BaseElement = typename Q::BaseElement; - using Zp = Zp_; - using Zps = std::unordered_map; - - using QElement = typename Q::Element; - using QEntry = ChainEntry; - using QChain = std::vector; - - using ZpElement = typename Zp::Element; - using ZpEntry = ChainEntry; - using ZpChain = std::vector; - - using QChains = std::vector; - using ZpChains = std::unordered_map>; - - using QLows = std::unordered_map; - using ZpLows = std::unordered_map>; - - using QPairs = std::vector; - using ZpPairs = std::unordered_map>; - - using Factors = std::vector; - - using Specials = std::unordered_map>; - - const Field& field() const { return q_; } - - void sort(QChain& c) { std::sort(c.begin(), c.end(), - [this](const QEntry& e1, const QEntry& e2) - { return this->cmp_(e1.index(), e2.index()); }); } - - template - void add(const ChainRange& chain) { return add(QChain(std::begin(chain), std::end(chain))); } - void add(QChain&& chain); - - void reserve(size_t s) { q_chains_.reserve(s); q_pairs_.reserve(s); } - size_t size() const { return q_pairs_.size(); } - - void reduce(ZpChain& zp_chain, BaseElement p); - ZpChain convert(const QChain& c, const Zp& field) const; - bool special(Index i, BaseElement p) const { auto it = zp_chains_.find(i); if (it == zp_chains_.end()) return false; if (it->second.find(p) == it->second.end()) return false; return true; } - Specials specials() const - { - Specials specials; - for (auto& x : zp_chains_) - for (auto& y : x.second) - specials[x.first].push_back(y.first); - return specials; - } - - const Zp& zp(BaseElement p) const { auto it = zps_.find(p); if (it != zps_.end()) return it->second; return zps_.emplace(p, Zp(p)).first->second; } - - static Factors factor(BaseElement x); - - const QChains& q_chains() const { return q_chains_; } - const ZpChains& zp_chains() const { return zp_chains_; } - - // This is a bit of a hack; it takes advantage of the fact that zp(p) - // generates field on-demand and memoizes them. So there is an entry in - // zps_ only if something special happened over the prime. - Factors primes() const { Factors result; result.reserve(zps_.size()); for (auto& x : zps_) result.push_back(x.first); return result; } - - // TODO: no skip support for now - bool skip(Index) const { return false; } - void add_skip() {} - void set_skip(Index, bool flag = true) {} - - Index pair(Index i, BaseElement p) const; - void set_pair(Index i, Index j); - void set_pair(Index i, Index j, BaseElement p); - static const Index unpaired() { return Reduction::unpaired; } - - private: - QChains q_chains_; - ZpChains zp_chains_; - - QLows q_lows_; - ZpLows zp_lows_; - - QPairs q_pairs_; - ZpPairs zp_pairs_; - - Q q_; - mutable Zps zps_; - - Comparison cmp_; -}; - -// Make OmniFieldPersistence act like a ReducedMatrix (e.g., for the purpose of constructing a persistence diagram) -template -struct PrimeAdapter -{ - using Persistence = OmniFieldPersistence; - using Prime = typename Persistence::BaseElement; - using Index = typename Persistence::Index; - - PrimeAdapter(const Persistence& persistence, Prime p): - persistence_(persistence), p_(p) {} - - bool skip(Index i) const { return persistence_.skip(i); } - - size_t size() const { return persistence_.size(); } - Index pair(Index i) const { return persistence_.pair(i, p_); } - static const Index unpaired() { return Persistence::unpaired(); } - - const Persistence& persistence_; - Prime p_; -}; - -template -PrimeAdapter -prime_adapter(const OmniFieldPersistence& persistence, - typename PrimeAdapter::Prime p) -{ - return PrimeAdapter(persistence, p); -} - -} // dionysus - -#include "omni-field-persistence.hpp" - -#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/omni-field-persistence.hpp b/src/Zigzag_persistence/example/ext_zz/dionysus/omni-field-persistence.hpp deleted file mode 100644 index 68d5fbede7..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/omni-field-persistence.hpp +++ /dev/null @@ -1,250 +0,0 @@ -template -void -dionysus::OmniFieldPersistence:: -add(QChain&& chain) -{ - sort(chain); - - q_chains_.emplace_back(std::move(chain)); - q_pairs_.emplace_back(unpaired()); - Index i = q_chains_.size() - 1; - - QChain& c = q_chains_.back(); - - auto reduce = [this,&c,i](BaseElement p) - { - auto zp_chain = convert(c, zp(p)); - - this->reduce(zp_chain, p); - - if (!zp_chain.empty()) - { - auto l = zp_chain.back().index(); - zp_lows_[l].emplace(p,i); - set_pair(l,i,p); - } - - zp_chains_[i].emplace(p, std::move(zp_chain)); // empty chain is still a valid indicator that we don't need to bother with this field - }; - - // reduce - auto entry_cmp = [this](const QEntry& e1, const QEntry& e2) { return this->cmp_(e1.index(), e2.index()); }; - while (!c.empty()) - { - auto& low = c.back(); - - auto e = low.element(); - auto l = low.index(); - assert(!q_.is_zero(e)); - if (e != q_.id()) - { - auto factors = factor(q_.numerator(e)); - for (auto p : factors) - { - if (!special(i, p)) // there is already a dedicated column over p - reduce(p); - } - } - - auto it_zp = zp_lows_.find(l); - if (it_zp != zp_lows_.end()) - for (auto& x : it_zp->second) - { - auto p = x.first; - if (!special(i,p)) - reduce(p); - } - - auto it_q = q_lows_.find(l); - if (it_q != q_lows_.end()) - { - Index j = it_q->second; - - // add the primes from j to i - auto it_zp = zp_chains_.find(j); - if (it_zp != zp_chains_.end()) - for (auto& x : it_zp->second) - { - auto p = x.first; - if (!special(i,p)) - reduce(p); - } - - // reduce over Q - auto j_chain = q_chains_[j]; - auto j_e = j_chain.back().element(); - - auto m = q_.neg(q_.div(e,j_e)); - Chain::addto(c, m, j_chain, q_, entry_cmp); - assert(c.empty() || !q_.is_zero(c.back().element())); - } else - { - q_lows_.emplace(l,i); - set_pair(l,i); - break; - } - } -} - -template -void -dionysus::OmniFieldPersistence:: -reduce(ZpChain& zp_chain, BaseElement p) -{ - auto& field = zp(p); - - auto entry_cmp = [this](const ZpEntry& e1, const ZpEntry& e2) { return this->cmp_(e1.index(), e2.index()); }; - - while (!zp_chain.empty()) - { - auto& low = zp_chain.back(); - auto j = low.index(); - - auto it = zp_lows_.find(j); - if (it != zp_lows_.end()) - { - auto it2 = it->second.find(p); - if (it2 != it->second.end()) - { - const ZpChain& co = zp_chains_[it2->second][p]; - - auto m = field.neg(field.div(low.element(), co.back().element())); - assert(m < p); - Chain::addto(zp_chain, m, co, field, entry_cmp); - continue; - } - } - - auto qit = q_lows_.find(j); - if (qit == q_lows_.end() || special(qit->second, p)) // no valid pivot over Q - return; - - // TODO: this could be optimized (add and convert on the fly) - auto& q_chain = q_chains_[qit->second]; - assert(q_chain.empty() || !q_.is_zero(q_chain.back().element())); - - auto co = convert(q_chain, field); - auto m = field.neg(field.div(low.element(), co.back().element())); - Chain::addto(zp_chain, m, co, field, entry_cmp); - - assert(!zp_chain.empty() || zp_chain.back().index() != j); - } -} - -template -typename dionysus::OmniFieldPersistence::ZpChain -dionysus::OmniFieldPersistence:: -convert(const QChain& c, const Zp& field) const -{ - ZpChain result; - result.reserve(c.size()); - auto p = field.prime(); - for (auto& x : c) - { - auto num = q_.numerator(x.element()) % p; - if (num != 0) - { - while (num < 0) num += p; - auto denom = q_.denominator(x.element()) % p; - while (denom < 0) denom += p; - assert(denom % p != 0); - result.emplace_back(field.div(num, denom), x.index()); - } - } - return result; -} - - -template -typename dionysus::OmniFieldPersistence::Factors -dionysus::OmniFieldPersistence:: -factor(BaseElement x) -{ - if (x < 0) - x = -x; - Factors result; - - if (Q::is_prime(x)) - { - result.push_back(x); - return result; - } - - BaseElement p { 2 }; - while (p*p <= x) - { - if (x % p == 0) - { - result.push_back(p); - do { x /= p; } while (x % p == 0); - if (Q::is_prime(x)) - { - result.push_back(x); - break; - } - } - ++p; - } - if (x > 1) - result.push_back(x); - - return result; -} - -template -typename dionysus::OmniFieldPersistence::Index -dionysus::OmniFieldPersistence:: -pair(Index i, BaseElement p) const -{ - if (p == 1) - return q_pairs_[i]; - else - { - auto it = zp_pairs_.find(p); - if (it == zp_pairs_.end()) - return q_pairs_[i]; - else - { - auto pit = it->second.find(i); - if (pit == it->second.end()) - return q_pairs_[i]; - else - return pit->second; - } - } -} - -template -void -dionysus::OmniFieldPersistence:: -set_pair(Index i, Index j, BaseElement p) -{ - auto& pairs = zp_pairs_[p]; - pairs[i] = j; - pairs[j] = i; -} - -template -void -dionysus::OmniFieldPersistence:: -set_pair(Index i, Index j) -{ - q_pairs_[i] = j; - q_pairs_[j] = i; - - auto it = zp_chains_.find(j); - if (it == zp_chains_.end()) - return; - - auto& chains = it->second; - for (auto& x : chains) - { - auto p = x.first; - auto& chain = x.second; - if (chain.empty()) - { - zp_pairs_[p][j] = unpaired(); - zp_pairs_[p][i] = unpaired(); - } - } -} diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/opts/opts.h b/src/Zigzag_persistence/example/ext_zz/dionysus/opts/opts.h deleted file mode 100644 index 1a9bbf71bf..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/opts/opts.h +++ /dev/null @@ -1,499 +0,0 @@ -/** - * Author: Dmitriy Morozov - * The interface is heavily influenced by GetOptPP (https://code.google.com/p/getoptpp/). - * The parsing logic is from ProgramOptions.hxx (https://github.com/Fytch/ProgramOptions.hxx). - * - * History: - * - 2015-06-01: added Traits<...>::type_string() for long, unsigned long - * - ... - * - 2018-04-27: replace parsing logic with the one from ProgramOptions.hxx to - * make the parser compliant with [GNU Program Argument Syntax - * Conventions](https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html) - * - 2018-05-11: add dashed_non_option(), to accept arguments that are negative numbers - */ - -#ifndef OPTS_OPTS_H -#define OPTS_OPTS_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace opts { - -// Converters -template -struct Converter -{ - Converter() {} - static - bool convert(const std::string& val, T& res) - { - std::istringstream iss(val); - iss >> res; - return !iss.fail() && iss.eof(); - } -}; - -// Type -template -struct Traits -{ - static std::string type_string() { return "UNKNOWN TYPE"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "INT"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "SHORT INT"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "LONG"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "UNSIGNED INT"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "SHORT UNSIGNED INT"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "UNSIGNED LONG"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "FLOAT"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "DOUBLE"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "STRING"; } -}; - - -struct BasicOption -{ - using IsShort = std::function; - - BasicOption(char s_, - std::string l_, - std::string default_, - std::string type_, - std::string help_): - s(s_), l(l_), d(default_), t(type_), help(help_) {} - virtual ~BasicOption() {} - - int long_size() const { return l.size() + 1 + t.size(); } - - void output(std::ostream& out, int max_long) const - { - out << " "; - if (s) - out << '-' << s << ", "; - else - out << " "; - - out << "--" << l << ' '; - - if (!t.empty()) - out << t; - - for (int i = long_size(); i < max_long; ++i) - out << ' '; - - out << " " << help; - - if (!d.empty()) - { - out << " [default: " << d << "]"; - } - out << '\n'; - } - - virtual bool flag() const { return false; } - virtual bool parse(int argc, char** argv, int& i, int j, IsShort is_short); - virtual bool set(std::string arg) =0; - - char s; - std::string l; - std::string d; - std::string t; - std::string help; -}; - -// Option -template -struct OptionContainer: public BasicOption -{ - OptionContainer(char s_, - const std::string& l_, - T& var_, - const std::string& help_, - const std::string& type_ = Traits::type_string()): - BasicOption(s_, l_, default_value(var_), type_, help_), - var(&var_) {} - - static - std::string default_value(const T& def) - { - std::ostringstream oss; - oss << def; - return oss.str(); - } - - bool set(std::string s) override { return Converter::convert(s, *var); } - - T* var; -}; - -template<> -struct OptionContainer: public BasicOption -{ - OptionContainer(char s_, - const std::string& l_, - bool& var_, - const std::string& help_): - BasicOption(s_, l_, "", "", help_), - var(&var_) { *var = false; } - - bool parse(int, char**, int&, int, IsShort) override { *var = true; return true; } - bool set(std::string) override { return true; } - bool flag() const override { return true; } - - bool* var; -}; - -template -struct OptionContainer< std::vector >: public BasicOption -{ - OptionContainer(char s_, - const std::string& l_, - std::vector& var_, - const std::string& help_, - const std::string& type_ = "SEQUENCE"): - BasicOption(s_, l_, default_value(var_), type_, help_), - var(&var_), first(true) { } - - static - std::string default_value(const std::vector& def) - { - std::ostringstream oss; - oss << "("; - if (def.size()) - oss << def[0]; - for (size_t i = 1; i < def.size(); ++i) - oss << ", " << def[i]; - oss << ")"; - return oss.str(); - } - - bool set(std::string s) override - { - if (first) - { - var->clear(); - first = false; - } - - T x; - bool result = Converter::convert(s,x); - var->emplace_back(std::move(x)); - return result; - } - - std::vector* var; - mutable bool first; -}; - - -template -std::unique_ptr -Option(char s, const std::string& l, T& var, const std::string& help) { return std::unique_ptr{new OptionContainer(s, l, var, help)}; } - -template -std::unique_ptr -Option(char s, const std::string& l, T& var, - const std::string& type, const std::string& help) { return std::unique_ptr{new OptionContainer(s, l, var, help, type)}; } - -template -std::unique_ptr -Option(const std::string& l, T& var, const std::string& help) { return std::unique_ptr{new OptionContainer(0, l, var, help)}; } - -template -std::unique_ptr -Option(const std::string& l, T& var, - const std::string& type, const std::string& help) { return std::unique_ptr{new OptionContainer(0, l, var, help, type)}; } - -// PosOption -template -struct PosOptionContainer -{ - PosOptionContainer(T& var_): - var(&var_) {} - - bool parse(std::list& args) const - { - if (args.empty()) - return false; - - bool result = Converter::convert(args.front(), *var); - if (!result) - std::cerr << "error: failed to parse " << args.front() << '\n'; - args.pop_front(); - return result; - } - - T* var; -}; - -template -PosOptionContainer -PosOption(T& var) { return PosOptionContainer(var); } - - -// Options -struct Options -{ - Options(): - failed(false) {} - - inline - Options& operator>>(std::unique_ptr opt); - template - Options& operator>>(const PosOptionContainer& poc); - - operator bool() { return !failed; } - - - friend - std::ostream& - operator<<(std::ostream& out, const Options& ops) - { - int max_long = 0; - for (auto& cur : ops.options) - { - int cur_long = cur->long_size(); - if (cur_long > max_long) - max_long = cur_long; - } - - out << "Options:\n"; - for (auto& cur : ops.options) - cur->output(out, max_long); - - return out; - } - - bool parse(int argc, char** argv); - - void unrecognized_option(std::string arg) const - { - std::cerr << "error: unrecognized option " << arg << '\n'; - } - - static bool dashed_non_option(char* arg, BasicOption::IsShort is_short) - { - return arg[ 0 ] == '-' - && (std::isdigit(arg[ 1 ]) || arg[ 1 ] == '.') - && !is_short(arg[ 1 ]); - } - - private: - std::list args; - std::list> options; - bool failed; -}; - -bool -BasicOption::parse(int argc, char** argv, int& i, int j, IsShort is_short) -{ - char* argument; - char* cur_arg = argv[i]; - // -v... - if (argv[i][j] == '\0') - { - // -v data - if (i + 1 < argc && (argv[i+1][0] != '-' || Options::dashed_non_option(argv[i+1], is_short))) - { - ++i; - argument = argv[i]; - } else - { - std::cerr << "error: cannot find the argument; ignoring " << argv[i] << '\n'; - return false; - } - } else if (argv[i][j] == '=') - { - // -v=data - argument = &argv[i][j+1]; - } else if( j == 2 ) { // only for short options - // -vdata - argument = &argv[i][j]; - } else - { - std::cerr << "error: unexpected character \'" << argv[i][j] << "\' ignoring " << argv[i] << '\n'; - return false; - } - bool result = set(argument); - if (!result) - std::cerr << "error: failed to parse " << argument << " in " << cur_arg << '\n'; - return result; -} - -bool -Options::parse(int argc, char** argv) -{ - std::map short_opts; - std::map long_opts; - - for (auto& opt : options) - { - if (opt->s) - short_opts[opt->s] = opt.get(); - - long_opts[opt->l] = opt.get(); - } - - auto is_short = [&short_opts](char c) -> bool { return short_opts.find(c) != short_opts.end(); }; - - for (int i = 1; i < argc; ++i) - { - if( argv[ i ][ 0 ] == '\0' ) - continue; - if( argv[ i ][ 0 ] != '-' || dashed_non_option(argv[i], is_short)) - args.push_back(argv[i]); - else - { - // -... - if( argv[ i ][ 1 ] == '\0' ) - { - // - - args.push_back(argv[i]); - } else if( argv[ i ][ 1 ] == '-' ) - { - if( argv[ i ][ 2 ] == '\0' ) - { - // -- - while( ++i < argc ) - args.push_back(argv[i]); - } else { - // --... - char* first = &argv[ i ][ 2 ]; - char* last = first; - for(; *last != '=' && *last != '\0'; ++last); - if (first == last) - { - failed = true; - unrecognized_option(argv[i]); - } else - { - auto opt_it = long_opts.find(std::string{first,last}); - if (opt_it == long_opts.end()) - { - failed = true; - unrecognized_option(argv[i]); - } else - { - failed |= !opt_it->second->parse(argc, argv, i, last - argv[i], is_short); - } - } - } - } else - { - // -f... - auto opt_it = short_opts.find(argv[i][1]); - if (opt_it == short_opts.end()) - { - failed = true; - unrecognized_option(argv[i]); - } else if (opt_it->second->flag()) - { - opt_it->second->parse(argc, argv, i, 0, is_short); // arguments are meaningless; just sets the flag - - // -fgh - char c; - for(int j = 1; (c = argv[i][j]) != '\0'; ++j) - { - if (!std::isprint(c) || c == '-') - { - failed = true; - std::cerr << "error: invalid character\'" << c << " ignoring " << &argv[i][j] << '\n'; - break; - } - opt_it = short_opts.find(c); - if (opt_it == short_opts.end()) - { - failed = true; - unrecognized_option("-" + std::string(1, c)); - continue; - } - if (!opt_it->second->flag()) - { - failed = true; - std::cerr << "error: non-void options not allowed in option packs; ignoring " << c << '\n'; - continue; - } - opt_it->second->parse(argc, argv, i, 0, is_short); // arguments are meaningless; just sets the flag - } - } else - { - failed |= !opt_it->second->parse(argc, argv, i, 2, is_short); - } - } - } - } - - return !failed; -} - -Options& -Options::operator>>(std::unique_ptr opt) -{ - options.emplace_back(std::move(opt)); - return *this; -} - -template -Options& -Options::operator>>(const PosOptionContainer& poc) -{ - if (!failed) - failed = !poc.parse(args); - return *this; -} - -} - -#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/ordinary-persistence.h b/src/Zigzag_persistence/example/ext_zz/dionysus/ordinary-persistence.h deleted file mode 100644 index 5f26bd2a1b..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/ordinary-persistence.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef DIONYSUS_ORDINARY_PERSISTENCE_H -#define DIONYSUS_ORDINARY_PERSISTENCE_H - -#include "reduced-matrix.h" - -namespace dionysus -{ - -/* Move this into a ReducedMatrix class */ - -// Ordinary D -> R reduction -template, - template class... Visitors> -using OrdinaryPersistence = ReducedMatrix; - -// No negative optimization -template> -struct NoNegative -{ - template - struct Visitor: public EmptyVisitor - { - template - void chain_initialized(Self* matrix, Chain& c) - { - for (auto cur = std::begin(c); cur != std::end(c); ++cur) - { - Index i = cur->index(); - Index p = matrix->pair(i); - if (!(p == Self::unpaired() || (*matrix)[i].empty())) - c.erase(cur--); - } - } - }; - - template - using V2 = EmptyVisitor; -}; - -template, - template class... Visitors> -using OrdinaryPersistenceNoNegative = ReducedMatrix::template Visitor, - Visitors...>; - -// TODO: add clearing optimization (possibly bake it into the code itself) - -template, - template class... Visitors> -using FastPersistence = ReducedMatrix::template Visitor, - //Clearing::template Visitor, // FIXME - Visitors...>; - - -} - -#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/pair-recorder.h b/src/Zigzag_persistence/example/ext_zz/dionysus/pair-recorder.h deleted file mode 100644 index 81c066bda6..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/pair-recorder.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef DIONYSUS_PAIR_RECORDER_H -#define DIONYSUS_PAIR_RECORDER_H - -namespace dionysus -{ - -template -struct PairRecorder: public Persistence_ -{ - typedef Persistence_ Persistence; - typedef typename Persistence::Index Index; - - - using Persistence::Persistence; - - template - Index add(const ChainRange& chain) - { - Index p = Persistence::add(chain); - pairs_.push_back(p); - if (p != unpaired()) - pairs_[p] = pairs_.size() - 1; - - return p; - } - - Index pair(Index i) const { return pairs_[i]; } - - void resize(size_t s) { Persistence::resize(s); pairs_.resize(s, unpaired()); } - size_t size() const { return pairs_.size(); } - static const Index unpaired() { return Reduction::unpaired; } - - std::vector pairs_; -}; - -template -struct PairChainRecorder: public PairRecorder -{ - using Persistence = Persistence_; - using Parent = PairRecorder; - using Index = typename Persistence_::Index; - using Chain = typename Persistence_::Chain; - - using Parent::Parent; - - template - Index add(const ChainRange& chain) - { - auto p_chain = Persistence::add(chain, keep_cocycles); - Index p = std::get<0>(p_chain); - - pairs_.push_back(p); - chains_.emplace_back(); - - if (p != unpaired()) - { - pairs_[p] = pairs_.size() - 1; - chains_[p] = std::move(std::get<1>(p_chain)); - } - - return p; - } - - using Parent::unpaired; - - Index pair(Index i) const { return pairs_[i]; } - const Chain& chain(Index i) const { return chains_[i]; } // chain that dies at i - void resize(size_t s) { Parent::resize(s); chains_.resize(s); } - - std::vector chains_; - using Parent::pairs_; - - bool keep_cocycles = true; -}; - -} - -#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/reduced-matrix.h b/src/Zigzag_persistence/example/ext_zz/dionysus/reduced-matrix.h deleted file mode 100644 index f7a04b130d..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/reduced-matrix.h +++ /dev/null @@ -1,170 +0,0 @@ -#ifndef DIONYSUS_REDUCED_MATRIX_H -#define DIONYSUS_REDUCED_MATRIX_H - -#include -#include - -#include "chain.h" -#include "reduction.h" - -namespace dionysus -{ - -template, template class... Visitors> -class ReducedMatrix -{ - public: - typedef ReducedMatrix Self; - - typedef Field_ Field; - typedef Index_ Index; - typedef Comparison_ Comparison; - - typedef std::tuple...> VisitorsTuple; - template - using Visitor = std::tuple_element; - - typedef typename Field::Element FieldElement; - typedef ChainEntry Entry; - typedef std::vector Chain; - - typedef std::vector Chains; - typedef std::vector Indices; - typedef std::vector SkipFlags; - - public: - ReducedMatrix(const Field& field): - field_(field) {} - - ReducedMatrix(const Field& field, - const Comparison& cmp, - const Visitors&... visitors): - field_(field), - cmp_(cmp), - visitors_(visitors...) {} - - ReducedMatrix(Field&& field, - Comparison&& cmp, - Visitors&&... visitors): - field_(std::move(field)), - cmp_(std::move(cmp)), - visitors_(visitors...) {} - - ReducedMatrix(Self&& m) = default; - ReducedMatrix(const Self& m) = default; - - template class... OtherVisitors> - ReducedMatrix(ReducedMatrix&& other): - field_(other.field_), - cmp_(other.cmp_), - reduced_(std::move(other.reduced_)), - pairs_(std::move(other.pairs_)), - skip_(std::move(other.skip_)) {} - - template - Index add(const ChainRange& chain) { return add(Chain(std::begin(chain), std::end(chain))); } - Index add(Chain&& chain); - - template - void set(Index i, const ChainRange& chain) { return set(i, Chain(std::begin(chain), std::end(chain))); } - void set(Index i, Chain&& chain); - - Index reduce(Index i); - Index reduce(Chain& c) { return reduce(c, reduced_, pairs_); } - template - Index reduce(Chain& c, const ChainsLookup& chains, const LowLookup& low); - - Index reduce_upto(Index i); // TODO - - size_t size() const { return pairs_.size(); } - void clear() { Chains().swap(reduced_); Indices().swap(pairs_); } - - void sort(Chain& c) { std::sort(c.begin(), c.end(), [this](const Entry& e1, const Entry& e2) { return this->cmp_(e1.index(), e2.index()); }); } - - const Chain& operator[](Index i) const { return reduced_[i]; } - Index pair(Index i) const { return pairs_[i]; } - void set_pair(Index i, Index j) { pairs_[i] = j; pairs_[j] = i; } - - Chain& column(Index i) { return reduced_[i]; } - - bool skip(Index i) const { return skip_[i]; } - void add_skip(); - void set_skip(Index i, bool flag = true) { skip_[i] = flag; } - - const Field& field() const { return field_; } - const Comparison& cmp() const { return cmp_; } - void reserve(size_t s) { reduced_.reserve(s); pairs_.reserve(s); } - void resize(size_t s); - - const Chains& columns() const { return reduced_; } - - template - Visitor& visitor() { return std::get(visitors_); } - - static const Index unpaired() { return Reduction::unpaired; } - - private: - template class... Vs> - friend class ReducedMatrix; // let's all be friends - - public: - // Visitors::chain_initialized(c) - template - typename std::enable_if::type - visitors_chain_initialized(Chain& c) {} - - template - typename std::enable_if::type - visitors_chain_initialized(Chain& c) { std::get(visitors_).chain_initialized(this, c); visitors_chain_initialized(c); } - - // Visitors::addto(m, cl) - template - typename std::enable_if::type - visitors_addto(FieldElement m, Index cl) {} - - template - typename std::enable_if::type - visitors_addto(FieldElement m, Index cl) { std::get(visitors_).addto(this, m, cl); visitors_addto(m, cl); } - - // Visitors::reduction_finished(m, cl) - template - typename std::enable_if::type - visitors_reduction_finished() {} - - template - typename std::enable_if::type - visitors_reduction_finished() { std::get(visitors_).reduction_finished(this); visitors_reduction_finished(); } - - private: - Field field_; - Comparison cmp_; - Chains reduced_; // matrix R - Indices pairs_; - SkipFlags skip_; // indicates whether the column should be skipped (e.g., for relative homology) - VisitorsTuple visitors_; -}; - -/* Visitors */ - -// The prototypical visitor. Others may (and probably should) inherit from it. -template -struct EmptyVisitor -{ - EmptyVisitor() = default; - - template - EmptyVisitor(const EmptyVisitor&) {} - - - template - void chain_initialized(Self*, Chain& c) {} - - void addto(Self*, typename Field::Element m, Index cl) {} - void reduction_finished(Self*) {} -}; - -} - -#include "reduced-matrix.hpp" - -#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/reduced-matrix.hpp b/src/Zigzag_persistence/example/ext_zz/dionysus/reduced-matrix.hpp deleted file mode 100644 index 3e4aca8f29..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/reduced-matrix.hpp +++ /dev/null @@ -1,78 +0,0 @@ -template class... V> -void -dionysus::ReducedMatrix:: -resize(size_t s) -{ - reduced_.resize(s); - pairs_.resize(s, unpaired()); - skip_.resize(s, false); -} - -template class... V> -typename dionysus::ReducedMatrix::Index -dionysus::ReducedMatrix:: -add(Chain&& chain) -{ - // TODO: skip the computation entirely if we already know this is positive (in case of the clearing optimization) - Index i = pairs_.size(); - pairs_.emplace_back(unpaired()); - reduced_.emplace_back(); - skip_.push_back(false); - - set(i, std::move(chain)); - - return reduce(i); -} - -template class... V> -void -dionysus::ReducedMatrix:: -add_skip() -{ - pairs_.emplace_back(unpaired()); - reduced_.emplace_back(); - skip_.push_back(true); -} - -template class... V> -void -dionysus::ReducedMatrix:: -set(Index i, Chain&& c) -{ - sort(c); - visitors_chain_initialized(c); - reduced_[i] = std::move(c); -} - -template class... V> -typename dionysus::ReducedMatrix::Index -dionysus::ReducedMatrix:: -reduce(Index i) -{ - Chain& c = column(i); - Index pair = reduce(c); - - if (pair != unpaired()) - pairs_[pair] = i; - - pairs_[i] = pair; - visitors_reduction_finished<>(); - - return pair; -} - -template class... V> -template -typename dionysus::ReducedMatrix::Index -dionysus::ReducedMatrix:: -reduce( Chain& c, - const ChainsLookup& chains, - const LowLookup& lows) -{ - auto entry_cmp = [this](const Entry& e1, const Entry& e2) { return this->cmp_(e1.index(), e2.index()); }; - return Reduction::reduce(c, chains, lows, field_, - [this](FieldElement m, Index cl) - { this->visitors_addto<>(m, cl); }, - entry_cmp); -} diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/reduction.h b/src/Zigzag_persistence/example/ext_zz/dionysus/reduction.h deleted file mode 100644 index 2afd333d41..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/reduction.h +++ /dev/null @@ -1,109 +0,0 @@ -#ifndef DIONYSUS_REDUCTION_H -#define DIONYSUS_REDUCTION_H - -#include -#include -#include -#include -#include "chain.h" - -namespace dionysus -{ - -namespace detail -{ - -template -struct Unpaired -{ static constexpr Index value() { return std::numeric_limits::max(); } }; - -} - -template -struct Reduction -{ - typedef Index_ Index; - - template - using AddtoVisitor = std::function; - - template - struct CallToSub; - - static const Index unpaired; - - template> - static - Index reduce(Chain1& c, - const ChainsLookup& chains, - const LowLookup& lows, - const Field& field, - const AddtoVisitor& visitor = [](typename Field::Element, Index) {}, - const Comparison& cmp = Comparison()) - { - typedef typename Field::Element FieldElement; - - while (!c.empty()) - { - //auto& low = c.back(); - auto& low = *(std::prev(c.end())); - Index l = low.index(); - Index cl = lows(l); - // std::cout << "idx: " << std::get<0>(cl) << ", " << std::get<1>(cl) << "\n"; - if (cl == unpaired) - return l; - else - { - // Reduce further - auto& co = chains(cl); - auto& co_low = co.back(); - FieldElement m = field.neg(field.div(low.element(), co_low.element())); - // c += m*co - Chain::addto(c, m, co, field, cmp); - visitor(m, cl); - } - } - return unpaired; - } - - template> - static - Index reduce(Chain1& c, - const std::vector& chains, - const std::vector& lows, - const Field& field, - const AddtoVisitor& visitor = [](typename Field::Element, Index) {}, - const Comparison& cmp = Comparison()) - { - return reduce(c, - CallToSub(chains), - CallToSub(lows), - field, visitor, cmp); - } - - // This is a work-around a bug in GCC (should really be a lambda function) - template - struct CallToSub - { - CallToSub(const std::vector& items_): - items(items_) {} - const Item& operator()(Index i) const { return items[i]; } - const std::vector& items; - }; -}; - - -template -const Index -Reduction::unpaired = detail::Unpaired::value(); - -} - -#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/relative-homology-zigzag.h b/src/Zigzag_persistence/example/ext_zz/dionysus/relative-homology-zigzag.h deleted file mode 100644 index 167a32779d..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/relative-homology-zigzag.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef RELATIVE_HOMOLOGY_ZIGZAG_H -#define RELATIVE_HOMOLOGY_ZIGZAG_H - -#include -#include - -#include "zigzag-persistence.h" - -namespace dionysus -{ - -namespace ba = boost::adaptors; - -template> -class RelativeHomologyZigzag -{ - public: - typedef Field_ Field; - typedef Index_ Index; - typedef Comparison_ Comparison; - - typedef ZigzagPersistence ZZP; - typedef typename ZZP::IndexChain IndexChain; - typedef typename ZZP::FieldElement FieldElement; - typedef typename IndexChain::value_type ChainEntry; - - - typedef Comparison Cmp; - - RelativeHomologyZigzag(const Field& field, - const Comparison& cmp = Comparison()): - zzp_(field, cmp) - { - zzp_.add( IndexChain() ); // vertex w - ++zzp_op_; - ++zzp_cell_; - } - - template - void add_both(const ChainRange& chain); - - void remove_both(Index cell); - - // index of the absolute cell; chain = its boundary - template - Index add(Index cell, const ChainRange& chain); // add to the relative part - - Index remove(Index cell); // remove from the relative part - - const Field& field() const { return zzp_.field(); } - const Cmp& cmp() const { return zzp_.cmp(); } - - size_t alive_size() const { return zzp_.alive_size() - 1; } // -1 for the cone vertex - - static - const Index unpaired() { return ZZP::unpaired(); } - - private: - template - IndexChain relative_chain(Index cell, const ChainRange& chain) const; - - template - IndexChain absolute_chain(const ChainRange& chain) const; - - Index abs_index(Index idx) const { return absolute_.left.find(idx)->second; } - Index rel_index(Index idx) const { return relative_.left.find(idx)->second; } - Index decode_pair(Index pair); - - private: - ZZP zzp_; // underlying (cone) implementation - boost::bimap absolute_; // bimap between our cells and zzp absolute cells - boost::bimap relative_; // bimap between our cells and zzp relative cells - std::unordered_map op_map_; // map from zzp_op to our op - Index op_ = 0, - zzp_op_ = 0, - cell_ = 0, - zzp_cell_ = 0; -}; - -} - -#include "relative-homology-zigzag.hpp" - -#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/relative-homology-zigzag.hpp b/src/Zigzag_persistence/example/ext_zz/dionysus/relative-homology-zigzag.hpp deleted file mode 100644 index 499807106c..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/relative-homology-zigzag.hpp +++ /dev/null @@ -1,122 +0,0 @@ -template -template -void -dionysus::RelativeHomologyZigzag:: -add_both(const ChainRange& chain) -{ - zzp_.add(absolute_chain(chain)); - op_map_.insert( { zzp_op_++, op_ } ); - absolute_.left.insert( { cell_, zzp_cell_++ } ); - - zzp_.add(relative_chain(cell_, chain)); - op_map_.insert( { zzp_op_++, op_ } ); - relative_.left.insert( { cell_, zzp_cell_++ } ); - - cell_++; - op_++; -} - -template -void -dionysus::RelativeHomologyZigzag:: -remove_both(Index cell) -{ - Index abs_cell = absolute_.left.find(cell)->second; - Index rel_cell = relative_.left.find(cell)->second; - - zzp_.remove(rel_cell); - zzp_.remove(abs_cell); - - absolute_.left.erase(cell); - relative_.left.erase(cell); - - op_map_.insert( { zzp_op_++, op_ } ); - op_map_.insert( { zzp_op_++, op_ } ); - - op_++; -} - -template -template -typename dionysus::RelativeHomologyZigzag::Index -dionysus::RelativeHomologyZigzag:: -add(Index cell, const ChainRange& chain) -{ - Index pair = zzp_.add(relative_chain(cell, chain)); - op_map_.insert( { zzp_op_++, op_++ } ); - relative_.left.insert( { cell, zzp_cell_++ } ); - - return decode_pair(pair); -} - - -template -typename dionysus::RelativeHomologyZigzag::Index -dionysus::RelativeHomologyZigzag:: -decode_pair(Index pair) -{ - if (pair == unpaired()) - return pair; - - Index decoded = op_map_.find(pair)->second; - op_map_.erase(pair); - return decoded; -} - -template -template -typename dionysus::RelativeHomologyZigzag::IndexChain -dionysus::RelativeHomologyZigzag:: -absolute_chain(const ChainRange& chain) const -{ - IndexChain res; - for (const auto& e : chain) - res.push_back(ChainEntry(e.element(), abs_index(e.index()))); - return res; -} - -template -template -typename dionysus::RelativeHomologyZigzag::IndexChain -dionysus::RelativeHomologyZigzag:: -relative_chain(Index cell, const ChainRange& chain) const -{ - // NB: to compute the signs correctly, - // this assumes that the cone vertex w is the last vertex in some total order - - typedef typename IndexChain::value_type ChainEntry; - - IndexChain res; - if (!chain.empty()) - { - for (const auto& e : chain) - res.push_back(ChainEntry(e.element(), rel_index(e.index()))); - - FieldElement a = field().id(); - if (chain.size() % 2 == 0) // TODO: double-check - a = field().neg(a); - res.push_back(ChainEntry(a, abs_index(cell))); // add the base space cell - } else - { - res.reserve(2); - res.push_back(ChainEntry(field().id(), abs_index(cell))); - res.push_back(ChainEntry(field().neg(field().id()), 0)); - } - return res; -} - - -template -typename dionysus::RelativeHomologyZigzag::Index -dionysus::RelativeHomologyZigzag:: -remove(Index cell) -{ - Index rel_cell = rel_index(cell); - Index pair = zzp_.remove(rel_cell); - pair = decode_pair(pair); - - op_map_.insert( { zzp_op_++, op_++ } ); - relative_.left.erase(cell); - - return pair; -} diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/rips.h b/src/Zigzag_persistence/example/ext_zz/dionysus/rips.h deleted file mode 100644 index c7ccb1189e..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/rips.h +++ /dev/null @@ -1,147 +0,0 @@ -#ifndef DIONYSUS_RIPS_H -#define DIONYSUS_RIPS_H - -#include -#include - -#include - -#include "simplex.h" - -namespace dionysus -{ - -/** - * Rips class - * - * Class providing basic operations to work with Rips complexes. It implements Bron-Kerbosch algorithm, - * and provides simple wrappers for various functions. - * - * Distances_ is expected to define types IndexType and DistanceType as well as - * provide operator()(...) which given two IndexTypes should return - * the distance between them. There should be methods begin() and end() - * for iterating over IndexTypes as well as a method size(). - */ -template > -class Rips -{ - public: - typedef Distances_ Distances; - typedef typename Distances::IndexType IndexType; - typedef typename Distances::DistanceType DistanceType; - - typedef Simplex_ Simplex; - typedef typename Simplex::Vertex Vertex; // should be the same as IndexType - typedef std::vector VertexContainer; - - typedef short unsigned Dimension; - - class Evaluator; - class Comparison; - - public: - Rips(const Distances& distances): - distances_(distances) {} - - // Calls functor f on each simplex in the k-skeleton of the Rips complex - template - void generate(Dimension k, DistanceType max, const Functor& f, - Iterator candidates_begin, Iterator candidates_end) const; - - // Calls functor f on all the simplices of the Rips complex that contain the given vertex v - template - void vertex_cofaces(IndexType v, Dimension k, DistanceType max, const Functor& f, - Iterator candidates_begin, Iterator candidates_end) const; - - // Calls functor f on all the simplices of the Rips complex that contain the given edge [u,v] - template - void edge_cofaces(IndexType u, IndexType v, Dimension k, DistanceType max, const Functor& f, - Iterator candidates_begin, Iterator candidates_end) const; - - // Calls functor f on all the simplices of the Rips complex that contain the given Simplex s - // (unlike the previous methods it does not call the functor on the Simplex s itself) - template - void cofaces(const Simplex& s, Dimension k, DistanceType max, const Functor& f, - Iterator candidates_begin, Iterator candidates_end) const; - - - /* No Iterator argument means Iterator = IndexType and the range is [distances().begin(), distances().end()) */ - template - void generate(Dimension k, DistanceType max, const Functor& f) const - { generate(k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); } - - template - void vertex_cofaces(IndexType v, Dimension k, DistanceType max, const Functor& f) const - { vertex_cofaces(v, k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); } - - template - void edge_cofaces(IndexType u, IndexType v, Dimension k, DistanceType max, const Functor& f) const - { edge_cofaces(u, v, k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); } - - template - void cofaces(const Simplex& s, Dimension k, DistanceType max, const Functor& f) const - { cofaces(s, k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); } - - - const Distances& distances() const { return distances_; } - DistanceType max_distance() const; - - DistanceType distance(const Simplex& s1, const Simplex& s2) const; - - - template - static void bron_kerbosch(VertexContainer& current, - const VertexContainer& candidates, - typename VertexContainer::const_iterator excluded, - Dimension max_dim, - const NeighborTest& neighbor, - const Functor& functor, - bool check_initial = true); - - protected: - const Distances& distances_; -}; - -template -class Rips::Evaluator -{ - public: - typedef Simplex_ Simplex; - - Evaluator(const Distances& distances): - distances_(distances) {} - - DistanceType operator()(const Simplex& s) const; - - protected: - const Distances& distances_; -}; - -template -class Rips::Comparison -{ - public: - typedef Simplex_ Simplex; - - Comparison(const Distances& distances): - eval_(distances) {} - - bool operator()(const Simplex& s1, const Simplex& s2) const - { - DistanceType e1 = eval_(s1), - e2 = eval_(s2); - if (e1 == e2) - return s1.dimension() < s2.dimension(); - - return e1 < e2; - } - - protected: - Evaluator eval_; -}; - -} - -#include "rips.hpp" - -#endif // DIONYSUS_RIPS_H diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/rips.hpp b/src/Zigzag_persistence/example/ext_zz/dionysus/rips.hpp deleted file mode 100644 index 2fdda34a7a..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/rips.hpp +++ /dev/null @@ -1,162 +0,0 @@ -#include -#include -#include -#include - -#include -#include - -template -template -void -dionysus::Rips:: -generate(Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const -{ - auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; }; - - // current = empty - // candidates = everything - VertexContainer current; - VertexContainer candidates(bg, end); - bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f); -} - -template -template -void -dionysus::Rips:: -vertex_cofaces(IndexType v, Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const -{ - auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; }; - - // current = [v] - // candidates = everything - [v] - VertexContainer current; current.push_back(v); - VertexContainer candidates; - for (Iterator cur = bg; cur != end; ++cur) - if (*cur != v && neighbor(v, *cur)) - candidates.push_back(*cur); - - bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f); -} - -template -template -void -dionysus::Rips:: -edge_cofaces(IndexType u, IndexType v, Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const -{ - auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; }; - - // current = [u,v] - // candidates = everything - [u,v] - VertexContainer current; current.push_back(u); current.push_back(v); - - VertexContainer candidates; - for (Iterator cur = bg; cur != end; ++cur) - if (*cur != u && *cur != v && neighbor(v,*cur) && neighbor(u,*cur)) - candidates.push_back(*cur); - - bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f); -} - -template -template -void -dionysus::Rips:: -cofaces(const Simplex& s, Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const -{ - namespace ba = boost::adaptors; - - auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; }; - - // current = s - VertexContainer current(s.begin(), s.end()); - - // candidates = everything - s that is a neighbor of every vertex in the simplex - VertexContainer candidates; - boost::set_difference(std::make_pair(bg, end) | - ba::filtered([this,&s,&neighbor](Vertex cur) - { for (auto& v : s) - if (!neighbor(v, cur)) - return false; - }), - s, - std::back_inserter(candidates)); - - bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f, false); -} - - -template -template -void -dionysus::Rips:: -bron_kerbosch(VertexContainer& current, - const VertexContainer& candidates, - typename VertexContainer::const_iterator excluded, - Dimension max_dim, - const NeighborTest& neighbor, - const Functor& functor, - bool check_initial) -{ - if (check_initial && !current.empty()) - functor(Simplex(current)); - - if (current.size() == static_cast(max_dim) + 1) - return; - - for (auto cur = std::next(excluded); cur != candidates.end(); ++cur) - { - current.push_back(*cur); - - VertexContainer new_candidates; - for (auto ccur = candidates.begin(); ccur != cur; ++ccur) - if (neighbor(*ccur, *cur)) - new_candidates.push_back(*ccur); - size_t ex = new_candidates.size(); - for (auto ccur = std::next(cur); ccur != candidates.end(); ++ccur) - if (neighbor(*ccur, *cur)) - new_candidates.push_back(*ccur); - excluded = new_candidates.begin() + (ex - 1); - - bron_kerbosch(current, new_candidates, excluded, max_dim, neighbor, functor); - current.pop_back(); - } -} - -template -typename dionysus::Rips::DistanceType -dionysus::Rips:: -distance(const Simplex& s1, const Simplex& s2) const -{ - DistanceType mx = 0; - for (auto a : s1) - for (auto b : s2) - mx = std::max(mx, distances_(a,b)); - return mx; -} - -template -typename dionysus::Rips::DistanceType -dionysus::Rips:: -max_distance() const -{ - DistanceType mx = 0; - for (IndexType a = distances_.begin(); a != distances_.end(); ++a) - for (IndexType b = std::next(a); b != distances_.end(); ++b) - mx = std::max(mx, distances_(a,b)); - return mx; -} - -template -typename dionysus::Rips::DistanceType -dionysus::Rips::Evaluator:: -operator()(const Simplex& s) const -{ - DistanceType mx = 0; - for (auto a = s.begin(); a != s.end(); ++a) - for (auto b = std::next(a); b != s.end(); ++b) - mx = std::max(mx, distances_(*a,*b)); - return mx; -} diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/row-reduction.h b/src/Zigzag_persistence/example/ext_zz/dionysus/row-reduction.h deleted file mode 100644 index e2481ce080..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/row-reduction.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef DIONYSUS_ROW_REDUCTION_H -#define DIONYSUS_ROW_REDUCTION_H - -#include "reduced-matrix.h" - -namespace dionysus -{ - -// Mid-level interface -template, template class... Visitors> -class RowReduction -{ - public: - typedef Field_ Field; - typedef Index_ Index; - typedef Comparison_ Comparison; - - typedef ReducedMatrix Persistence; - - public: - RowReduction(const Field& field): - persistence_(field) {} - - RowReduction(const Field& field, - const Comparison& cmp, - const Visitors&... visitors): - persistence_(field, cmp, visitors...) {} - - template - void operator()(const Filtration& f, const Relative& relative, const ReportPair& report_pair, const Progress& progress); - - template - void operator()(const Filtration& f, const ReportPair& report_pair); - - template - void operator()(const Filtration& f) { return (*this)(f, &no_report_pair); } - - static void no_report_pair(int, Index, Index) {} - static void no_progress() {} - - const Persistence& - persistence() const { return persistence_; } - Persistence& persistence() { return persistence_; } - - private: - Persistence persistence_; -}; - -} - -#include "row-reduction.hpp" - -#endif - diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/row-reduction.hpp b/src/Zigzag_persistence/example/ext_zz/dionysus/row-reduction.hpp deleted file mode 100644 index edb1652872..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/row-reduction.hpp +++ /dev/null @@ -1,103 +0,0 @@ -#include -namespace ba = boost::adaptors; - -template class... V> -template -void -dionysus::RowReduction:: -operator()(const Filtration& filtration, const ReportPair& report_pair) -{ - using Cell = typename Filtration::Cell; - (*this)(filtration, [](const Cell&) { return false; }, report_pair, &no_progress); -} - -template class... V> -template -void -dionysus::RowReduction:: -operator()(const Filtration& filtration, const Relative& relative, const ReportPair& report_pair, const Progress& progress) -{ - persistence_.resize(filtration.size()); - - typedef typename Persistence::Index Index; - typedef typename Persistence::FieldElement Element; - typedef typename Persistence::Chain Chain; - typedef typename Filtration::Cell Cell; - typedef ChainEntry CellChainEntry; - typedef ChainEntry ChainEntry; - - std::vector rows(persistence_.size()); - - auto& field = persistence_.field(); - - // fill the matrix - Index i = 0; - for(auto& c : filtration) - { - progress(); - - if (relative(c)) - { - persistence_.set_skip(i); - ++i; - continue; - } - - persistence_.set(i, c.boundary(field) | - ba::filtered([relative](const CellChainEntry& e) { return !relative(e.index()); }) | - ba::transformed([this,&filtration](const CellChainEntry& e) - { return ChainEntry(e.element(), filtration.index(e.index())); })); - if (!persistence_[i].empty()) - { - auto& x = persistence_[i].back(); - rows[x.index()].emplace_back(x.element(),i); - } - ++i; - } - - auto entry_cmp = [this](const ChainEntry& e1, const ChainEntry& e2) { return this->persistence_.cmp()(e1.index(), e2.index()); }; - - // reduce the matrix from the bottom up - for (auto it = rows.rbegin(); it != rows.rend(); ++it) - { - auto& row = *it; - Index r = rows.rend() - it - 1; - - if (row.empty()) - continue; - - // add the first column to every other column - Index c = row.front().index(); - Element e = row.front().element(); - Chain& first = persistence_.column(c); - for (size_t i = 1; i < row.size(); ++i) - { - Index cur_idx = row[i].index(); - Element cur_elem = row[i].element(); - Chain& cur = persistence_.column(cur_idx); - if (cur.empty()) // zeroed out by the clearing optimization - continue; - - Element m = field.neg(field.div(cur_elem, e)); - // cur += m*first - ::dionysus::Chain::addto(cur, m, first, field, entry_cmp); - - // update row - if (!cur.empty()) - { - ChainEntry ce = cur.back(); - auto& new_row = rows[ce.index()]; - new_row.emplace_back(ce.element(), cur_idx); - if (entry_cmp(new_row.back(), new_row.front())) - std::swap(new_row.back(), new_row.front()); - } - } - - persistence_.set_pair(r,c); - report_pair(filtration[r].dimension(), r, c); - - // zero out the corresponding column (the clearing optimization) - persistence_.column(r).clear(); - } -} - diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/simplex.h b/src/Zigzag_persistence/example/ext_zz/dionysus/simplex.h deleted file mode 100644 index 4ac5cb6945..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/simplex.h +++ /dev/null @@ -1,280 +0,0 @@ -#ifndef DIONYSUS_SIMPLEX_H -#define DIONYSUS_SIMPLEX_H - -#include -#include - -//#include -#include -#include -#include - -#include "chain.h" - -namespace dionysus -{ - -struct Empty {}; - -template -class Simplex -{ - public: - typedef Vertex_ Vertex; - typedef T Data; - typedef std::unique_ptr Vertices; - - template - struct BoundaryChainIterator; - struct BoundaryIterator; - - template - using BoundaryChainRange = boost::iterator_range>; - using BoundaryRange = boost::iterator_range; - - template - using Entry = ChainEntry; - - public: - Simplex(const Data& d = Data()): - dim_(-1), data_(d) {} - - Simplex(const std::initializer_list& vertices, - Data&& d = Data()): - Simplex(vertices.size() - 1, vertices.begin(), vertices.end(), std::move(d)) - {} - - Simplex(const std::initializer_list& vertices, - const Data& d): - Simplex(vertices.size() - 1, vertices.begin(), vertices.end(), d) {} - - Simplex(short unsigned dim, Vertices&& vertices, Data&& data = Data()): - dim_(dim), vertices_(std::move(vertices)), data_(std::move(data)) { std::sort(begin(), end()); } - - template - Simplex(const VertexRange& vertices, - Data&& d = Data()): - Simplex(vertices.size() - 1, vertices.begin(), vertices.end(), std::move(d)) - {} - - template - Simplex(const VertexRange& vertices, - const Data& d): - Simplex(vertices.size() - 1, vertices.begin(), vertices.end(), d) {} - - Simplex(const Simplex& other): - Simplex(other.dim_, other.begin(), other.end(), other.data_) {} - Simplex& operator=(const Simplex& other) { dim_ = other.dim_; vertices_ = Vertices(new Vertex[dim_+1]); std::copy(other.begin(), other.end(), begin()); data_ = other.data_; return *this; } - - Simplex(Simplex&& other) noexcept: - dim_(other.dim_), - vertices_(std::move(other.vertices_)), - data_(std::move(other.data_)) {} - Simplex& operator=(Simplex&& other) = default; - - template - Simplex(short unsigned dim, - Iterator b, Iterator e, - Data&& d = Data()): - dim_(dim), - vertices_(new Vertex[dim_+1]), - data_(std::move(d)) { std::copy(b, e, begin()); std::sort(begin(), end()); } - - template - Simplex(short unsigned dim, - Iterator b, Iterator e, - const Data& d): - dim_(dim), - vertices_(new Vertex[dim_+1]), - data_(d) { std::copy(b, e, begin()); std::sort(begin(), end()); } - - short unsigned dimension() const { return dim_; } - - BoundaryRange boundary() const { return BoundaryRange(boundary_begin(), boundary_end()); } - BoundaryIterator boundary_begin() const; - BoundaryIterator boundary_end() const; - - template - BoundaryChainRange - boundary(const Field& field) const { return BoundaryChainRange(boundary_begin(field), boundary_end(field)); } - - template - BoundaryChainIterator - boundary_begin(const Field& field) const; - template - BoundaryChainIterator - boundary_end(const Field& field) const; - - const Vertex* begin() const { return vertices_.get(); } - const Vertex* end() const { return begin() + dim_ + 1; } - size_t size() const { return dim_ + 1; } - - std::pair - range() const { return std::make_pair(begin(), end()); } - - Simplex join(const Vertex& v) const { Vertices vertices(new Vertex[dim_+2]); std::copy(begin(), end(), vertices.get()); vertices[dim_+1] = v; return Simplex(dim_ + 1, std::move(vertices), Data(data_)); } - - bool operator==(const Simplex& other) const { return dim_ == other.dim_ && std::equal(begin(), end(), other.begin()); } - bool operator!=(const Simplex& other) const { return !operator==(other); } - bool operator<(const Simplex& other) const { return dim_ < other.dim_ || (dim_ == other.dim_ && std::lexicographical_compare(begin(), end(), other.begin(), other.end())); } - bool operator>(const Simplex& other) const { return other < (*this); } - - Vertex operator[](short unsigned i) const { return vertices_[i]; } - const Data& data() const { return data_; } - Data& data() { return data_; } - - friend - std::ostream& operator<<(std::ostream& out, const Simplex& s) - { out << '<' << *s.begin(); for (auto it = s.begin() + 1; it != s.end(); ++it) out << ',' << *it; out << '>'; return out; } - - private: - Vertex* begin() { return vertices_.get(); } - Vertex* end() { return begin() + dim_ + 1; } - - private: - short unsigned dim_; - //boost::compressed_pair vertices_data_; - Vertices vertices_; - Data data_; // TODO: optimize -}; - -template -size_t hash_value(const Simplex& s) { return boost::hash_range(s.begin(), s.end()); } - - -template -struct Simplex::BoundaryIterator: - public boost::iterator_adaptor, // Value - boost::use_default, - Simplex> // Reference -{ - public: - typedef const V* Iterator; - typedef Simplex Value; - - typedef boost::iterator_adaptor Parent; - - BoundaryIterator() {} - explicit BoundaryIterator(short unsigned dim, Iterator iter, Iterator bg, Iterator end): - Parent(iter), dim_(dim), bg_(bg), end_(end) {} - - Iterator begin() const { return bg_; } - - private: - friend class boost::iterator_core_access; - Value dereference() const - { - typedef std::not_equal_to NotEqualVertex; - - using std::placeholders::_1; - return Simplex(dim_ - 1, - boost::make_filter_iterator(std::bind(NotEqualVertex(), _1, *(this->base())), bg_, end_), - boost::make_filter_iterator(std::bind(NotEqualVertex(), _1, *(this->base())), end_, end_)); - } - - short unsigned dim_; - Iterator bg_; - Iterator end_; -}; - -template -template -struct Simplex::BoundaryChainIterator: - public boost::iterator_adaptor, // Derived - BoundaryIterator, - ChainEntry>, // Value - boost::use_default, - ChainEntry>> // Reference -{ - public: - typedef F Field; - typedef BoundaryIterator Iterator; - typedef ChainEntry> Value; - - typedef boost::iterator_adaptor Parent; - - BoundaryChainIterator() {} - explicit BoundaryChainIterator(const Field& field, Iterator iter): - Parent(iter), field_(&field) {} - - private: - friend class boost::iterator_core_access; - Value dereference() const - { - return Value(((this->base().base() - this->base().begin()) % 2 == 0)? field_->id() : field_->neg(field_->id()), - *(this->base())); - } - - const Field* field_ = nullptr; -}; - - -/* Simplex */ -template -typename Simplex::BoundaryIterator -Simplex:: -boundary_begin() const -{ - if (dimension() == 0) return boundary_end(); - return BoundaryIterator(dimension(), begin(), begin(), end()); -} - -template -typename Simplex::BoundaryIterator -Simplex:: -boundary_end() const -{ - return BoundaryIterator(dimension(), end(), begin(), end()); -} - -template -template -#if defined(_MSC_VER) -typename Simplex::BoundaryChainIterator -#else -typename Simplex::template BoundaryChainIterator -#endif -Simplex:: -boundary_begin(const F& field) const -{ - if (dimension() == 0) return boundary_end(field); - return BoundaryChainIterator(field, boundary_begin()); -} - -template -template -#if defined(_MSC_VER) -typename Simplex::BoundaryChainIterator -#else -typename Simplex::template BoundaryChainIterator -#endif -Simplex:: -boundary_end(const F& field) const -{ - return BoundaryChainIterator(field, boundary_end()); -} - -} // dionysus - -namespace std -{ - -template -struct hash> -{ - size_t operator()(const dionysus::Simplex& s) const { return hash_value(s); } -}; - -} // std - -#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/sparse-row-matrix.h b/src/Zigzag_persistence/example/ext_zz/dionysus/sparse-row-matrix.h deleted file mode 100644 index fb1e929e02..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/sparse-row-matrix.h +++ /dev/null @@ -1,184 +0,0 @@ -#ifndef DIONYSUS_SPARSE_ROW_MATRIX_H -#define DIONYSUS_SPARSE_ROW_MATRIX_H - -#include -#include -#include -#include // for debugging output - -#include - -#include "chain.h" -#include "reduction.h" - -namespace dionysus -{ - -namespace bi = boost::intrusive; - -namespace detail -{ - typedef bi::list_base_hook> auto_unlink_hook; - - template - struct SparseRowMatrixEntry: - public ChainEntry, auto_unlink_hook> - { - typedef I Index; - typedef typename F::Element FieldElement; - typedef std::tuple IndexPair; // (id, pair) - typedef ChainEntry Parent; - typedef SparseRowMatrixEntry Entry; - - SparseRowMatrixEntry(FieldElement e, const IndexPair& ip): - Parent(e,ip) {} - - SparseRowMatrixEntry(FieldElement e, const Index& r, const Index& c): - Parent(e,IndexPair(r,c)) {} - - SparseRowMatrixEntry(const Entry& other) = default; - SparseRowMatrixEntry(Entry&& other) = default; - Entry& operator=(Entry&& other) = default; - - void unlink() { auto_unlink_hook::unlink(); } - bool is_linked() const { return auto_unlink_hook::is_linked(); } - }; -} - -template, - template class Column_ = std::vector> -class SparseRowMatrix -{ - public: - typedef Field_ Field; - typedef Index_ Index; - typedef Comparison_ Comparison; - - typedef typename Field::Element FieldElement; - - typedef detail::SparseRowMatrixEntry Entry; - typedef Column_ Column; - typedef typename Entry::IndexPair IndexPair; - typedef bi::list> Row; - - typedef std::vector> IndexChain; - - typedef std::unordered_map Columns; - typedef std::unordered_map Rows; - typedef std::unordered_map LowMap; - - public: - SparseRowMatrix(const Field& field, - const Comparison& cmp = Comparison()): - field_(field), cmp_(cmp) {} - - SparseRowMatrix(SparseRowMatrix&& other) = default; - - - template - Column reduce(const ChainRange& chain, IndexChain& trail); - - Index set(Index i, Column&& chain); // returns previous column with this low - void fix(Index c, Column& column); - void fix(Index c) { fix(c, col(c)); } - - const Row& prepend_row(Index r, FieldElement m, const Row& chain); // could be horribly inefficient if Column is chosen poorly - - void drop_row(Index r) { rows_.erase(r); if (is_low(r)) lows_.erase(r); } - void drop_col(Index c) - { - auto cit = columns_.find(c); - Column& column = cit->second; - if (!column.empty()) - { - Index rlow = std::get<0>(column.back().index()); - auto it = lows_.find(rlow); - if (it != lows_.end() && it->second == c) - lows_.erase(it); - } - columns_.erase(cit); - } - void drop_low(Index r) { lows_.erase(r); } - - // accessors - Row& row(Index r) { return rows_[r]; } - Column& col(Index c) { assert(col_exists(c)); return columns_.find(c)->second; } - const Column& col(Index c) const { assert(col_exists(c)); return columns_.find(c)->second; } - Index low(Index r) const { return lows_.find(r)->second; } - bool is_low(Index r) const { return lows_.find(r) != lows_.end(); } - void update_low(Index c) { lows_[std::get<0>(col(c).back().index())] = c; } - - const Field& field() const { return field_; } - void reserve(size_t) {} // here for compatibility only - const Comparison& cmp() const { return cmp_; } - - // debug - bool col_exists(Index c) const { return columns_.find(c) != columns_.end(); } - const Columns& columns() const { return columns_; } - void check_columns() const - { - for (auto& x : columns_) - { - Index c = x.first; - if (x.second.empty()) - std::cout << "Warning: empty column " << c << std::endl; - Index rl = std::get<0>(x.second.back().index()); - if (!is_low(rl) || low(rl) != c) - { - std::cout << "Columns don't check out: lows don't match" << std::endl; - std::cout << " " << c << ' ' << rl << ' ' << ' ' << low(rl) << std::endl; - std::cout << "---\n"; - for (auto& x : col(c)) - std::cout << " " << x.element() << ' ' << std::get<0>(x.index()) << ' ' << std::get<1>(x.index()) << '\n'; - std::cout << "---\n"; - for (auto& x : col(low(rl))) - std::cout << " " << x.element() << ' ' << std::get<0>(x.index()) << ' ' << std::get<1>(x.index()) << '\n'; - assert(0); - } - - for (auto& x : lows_) - { - if (!col_exists(x.second)) - { - std::cout << "Still keeping low of a removed column" << std::endl; - assert(0); - } - else if (std::get<0>(col(x.second).back().index()) != x.first) - { - std::cout << "Low mismatch: " << x.second << ' ' << std::get<0>(col(x.second).back().index()) << ' ' << x.first << '\n'; - assert(0); - } - } - } - } - - private: - Field field_; - Comparison cmp_; - - Columns columns_; - Rows rows_; - LowMap lows_; // column that has this low -}; - - -namespace detail -{ - -template -struct Unpaired> -{ - static - constexpr std::tuple - value() - { return std::make_tuple(std::numeric_limits::max(), - std::numeric_limits::max()); } -}; - -} - -} - -#include "sparse-row-matrix.hpp" - -#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/sparse-row-matrix.hpp b/src/Zigzag_persistence/example/ext_zz/dionysus/sparse-row-matrix.hpp deleted file mode 100644 index 10f4808c17..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/sparse-row-matrix.hpp +++ /dev/null @@ -1,103 +0,0 @@ -template class Col> -template -typename dionysus::SparseRowMatrix::Column -dionysus::SparseRowMatrix:: -reduce(const ChainRange& chain_, IndexChain& trail) -{ - auto row_cmp = [this](const Entry& e1, const Entry& e2) - { return this->cmp_(std::get<0>(e1.index()), std::get<0>(e2.index())); }; - -#define __DIONYSUS_USE_VECTOR_CHAINS 1 - -#if !(__DIONYSUS_USE_VECTOR_CHAINS) - std::set chain(row_cmp); - for (auto x : chain_) - chain.insert(Entry(x.element(), IndexPair(x.index(), 0))); -#else - Column chain; - for (auto x : chain_) - chain.emplace_back(x.element(), IndexPair(x.index(), 0)); - std::sort(chain.begin(), chain.end(), row_cmp); -#endif - - typedef Reduction ReductionIP; - - auto chains = [this](const IndexPair& rc) -> const Column& { return this->col(std::get<1>(rc)); }; - auto lows = [this](const IndexPair& rc) -> IndexPair - { - Index r = std::get<0>(rc); - auto it = this->lows_.find(r); - if (it == this->lows_.end()) - return ReductionIP::unpaired; - else - { - Index rr = std::get<0>(col(it->second).back().index()); - if (rr != r) - std::cout << "Mismatch: " << rr << ' ' << r << std::endl; - return IndexPair(r, it->second); - } - }; - - auto addto = [&trail](FieldElement m, const IndexPair& rc) { trail.emplace_back(m, std::get<1>(rc)); }; - - ReductionIP::reduce(chain, - chains, lows, - field_, addto, row_cmp); - -#if !(__DIONYSUS_USE_VECTOR_CHAINS) - return Column(std::begin(chain), std::end(chain)); -#else - return chain; -#endif -} - -template class Col> -typename dionysus::SparseRowMatrix::Index -dionysus::SparseRowMatrix:: -set(Index col, Column&& chain) -{ - Column& column = columns_.emplace(col, std::move(chain)).first->second; - - fix(col, column); - - Index r = std::get<0>(column.back().index()); - Index res; - if (is_low(r)) - res = low(r); - else - res = col; - lows_[r] = col; - - return res; -} - -template class Col> -void -dionysus::SparseRowMatrix:: -fix(Index col, Column& column) -{ - for (auto& x : column) - { - std::get<1>(x.index()) = col; - Index r = std::get<0>(x.index()); - row(r).push_back(x); - } -} - -template class Col> -const typename dionysus::SparseRowMatrix::Row& -dionysus::SparseRowMatrix:: -prepend_row(Index r, FieldElement m, const Row& chain) -{ - Row& new_row = row(r); - - for (auto& x : chain) - { - Index c = std::get<1>(x.index()); - Column& column = col(c); - auto it = column.emplace(column.begin(), field().mul(x.element(), m), r, c); - new_row.push_back(*it); - } - - return new_row; -} diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/standard-reduction.h b/src/Zigzag_persistence/example/ext_zz/dionysus/standard-reduction.h deleted file mode 100644 index 0477d4683f..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/standard-reduction.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef DIONYSUS_STANDARD_REDUCTION_H -#define DIONYSUS_STANDARD_REDUCTION_H - -namespace dionysus -{ - -// Mid-level interface -template -class StandardReduction -{ - public: - typedef Persistence_ Persistence; - typedef typename Persistence::Field Field; - typedef typename Persistence::Index Index; - - public: - StandardReduction(Persistence& persistence): - persistence_(persistence) {} - - template - void operator()(const Filtration& f, const Relative& relative, const ReportPair& report_pair, const Progress& progress); - - template - void operator()(const Filtration& f, const ReportPair& report_pair); - - template - void operator()(const Filtration& f) { return (*this)(f, &no_report_pair); } - - static void no_report_pair(int, Index, Index) {} - static void no_progress() {} - - const Persistence& - persistence() const { return persistence_; } - Persistence& persistence() { return persistence_; } - - private: - Persistence& persistence_; -}; - -} - -#include "standard-reduction.hpp" - -#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/standard-reduction.hpp b/src/Zigzag_persistence/example/ext_zz/dionysus/standard-reduction.hpp deleted file mode 100644 index 9aa3396a8c..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/standard-reduction.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#include -namespace ba = boost::adaptors; - -template -template -void -dionysus::StandardReduction

:: -operator()(const Filtration& filtration, const ReportPair& report_pair) -{ - using Cell = typename Filtration::Cell; - (*this)(filtration, [](const Cell&) { return false; }, report_pair, no_progress); -} - -template -template -void -dionysus::StandardReduction

:: -operator()(const Filtration& filtration, const Relative& relative, const ReportPair& report_pair, const Progress& progress) -{ - persistence_.reserve(filtration.size()); - - typedef typename Filtration::Cell Cell; - typedef ChainEntry CellChainEntry; - typedef ChainEntry ChainEntry; - - unsigned i = 0; - for(auto& c : filtration) - { - progress(); - - if (relative(c)) - { - ++i; - persistence_.add_skip(); - continue; - } - - //std::cout << "Adding: " << c << " : " << boost::distance(c.boundary(persistence_.field())) << std::endl; - Index pair = persistence_.add(c.boundary(persistence_.field()) | - ba::filtered([relative](const CellChainEntry& e) { return !relative(e.index()); }) | - ba::transformed([this,&filtration](const CellChainEntry& e) - { return ChainEntry(e.element(), filtration.index(e.index())); })); - if (pair != persistence_.unpaired()) - report_pair(c.dimension(), pair, i); - ++i; - } -} diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/trails-chains.h b/src/Zigzag_persistence/example/ext_zz/dionysus/trails-chains.h deleted file mode 100644 index f18ff897e4..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/trails-chains.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef DIONYSUS_TRAILS_CHAINS_H -#define DIONYSUS_TRAILS_CHAINS_H - -#include "ordinary-persistence.h" - -template -struct ChainsVisitor: public EmptyVisitor -{ - template - void chain_initialized(Chain& c) { } - - void addto(typename Field::Element m, Index cl) {} - void reduction_finished() {} -}; - - -#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/zigzag-persistence.h b/src/Zigzag_persistence/example/ext_zz/dionysus/zigzag-persistence.h deleted file mode 100644 index e9423099aa..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/zigzag-persistence.h +++ /dev/null @@ -1,142 +0,0 @@ -#ifndef DIONYSUS_ZIGZAG_PERSISTENCE_H -#define DIONYSUS_ZIGZAG_PERSISTENCE_H - -#include -#include -#include - -#include -#include - -#include "sparse-row-matrix.h" - -namespace dionysus -{ - -namespace ba = boost::adaptors; - -template> -class ZigzagPersistence -{ - static_assert(std::is_signed::value, "Index type used in ZigzagPersistence must be a *signed* integer"); - - public: - typedef Field_ Field; - typedef Index_ Index; - typedef Comparison_ Comparison; - - typedef SparseRowMatrix RowMatrix; - typedef SparseRowMatrix DequeRowMatrix; - typedef typename RowMatrix::IndexPair IndexPair; - typedef typename RowMatrix::FieldElement FieldElement; - typedef typename RowMatrix::IndexChain IndexChain; - typedef typename RowMatrix::Column Column; - typedef typename RowMatrix::Row Row; - typedef typename DequeRowMatrix::Column DequeColumn; - typedef typename DequeRowMatrix::Row DequeRow; - - typedef std::unordered_map BirthIndexMap; - - - ZigzagPersistence(const Field& field, - const Comparison& cmp = Comparison()): - Z(field, cmp), C(field, cmp), B(field, cmp), - operations(0), - cell_indices(0), - z_indicies_last(0), - z_indicies_first(-1), - b_indices(0) {} - - template - Index add(const ChainRange& chain) // returns the id of the dying cycle (or unpaired) - { - Index res = add_impl(chain); -#ifdef DIONYSUS_ZIGZAG_DEBUG - check_sorted(); - check_b_cols(); - Z.check_columns(); -#endif - return res; - } - Index remove(Index cell) - { - Index res = remove_impl(cell); -#ifdef DIONYSUS_ZIGZAG_DEBUG - check_sorted(); - check_b_cols(); - Z.check_columns(); -#endif - return res; - } - - struct IsAlive - { - IsAlive(const ZigzagPersistence& zz_): zz(&zz_) {} - bool operator()(const std::pair& x) const { return zz->is_alive(x.first); } - const ZigzagPersistence* zz; - }; - - bool is_alive(Index x) const { return !B.is_low(x); } - - auto alive_ops() const -> decltype(BirthIndexMap() | ba::filtered(IsAlive(*this)) | ba::map_values) - { return birth_index | ba::filtered(IsAlive(*this)) | ba::map_values; } - - auto alive_cycles() const -> decltype(BirthIndexMap() | ba::filtered(IsAlive(*this)) | ba::map_keys) - { return birth_index | ba::filtered(IsAlive(*this)) | ba::map_keys; } - - size_t alive_size() const { return Z.columns().size() - B.columns().size(); } - - void reserve(size_t) {} // here for compatibility only - const Field& field() const { return Z.field(); } - const Comparison& cmp() const { return Z.cmp(); } - - template - static Index row(const Entry& e) { return std::get<0>(e.index()); } - template - static Index col(const Entry& e) { return std::get<1>(e.index()); } - - static - const Index unpaired() { return Reduction::unpaired; } - - const Column& cycle(Index i) const { return Z.col(i); } - - // debug - void check_b_cols() const; - - template - void check_boundaries(const SimplexToIndex& s2i, const IndexToSimplex& i2s) const; - template - void check_cycles(const SimplexToIndex& s2i, const IndexToSimplex& i2s) const; - - Column zb_dot(Index c) const; - - template - Column dc_dot(Index c, const SimplexToIndex& s2i, const IndexToSimplex& i2s) const; - - template - Column boundary(Index i, const SimplexToIndex& s2i, const IndexToSimplex& i2s) const; - - void check_sorted() const; - - private: - template - Index add_impl(const ChainRange& chain); - Index remove_impl(Index cell); - - private: - RowMatrix Z, C; - DequeRowMatrix B; - - BirthIndexMap birth_index; - Index operations; - Index cell_indices; - Index z_indicies_last, z_indicies_first; - Index b_indices; -}; - -} - -#include "zigzag-persistence.hpp" - -#endif diff --git a/src/Zigzag_persistence/example/ext_zz/dionysus/zigzag-persistence.hpp b/src/Zigzag_persistence/example/ext_zz/dionysus/zigzag-persistence.hpp deleted file mode 100644 index 96331a3b15..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/dionysus/zigzag-persistence.hpp +++ /dev/null @@ -1,541 +0,0 @@ -#include - -template -template -typename dionysus::ZigzagPersistence::Index -dionysus::ZigzagPersistence:: -add_impl(const ChainRange& chain_) -{ - // std::cout << "add(" << cell_indices << ")" << std::endl; - Index op = operations++; - - IndexChain cycles; // chain_ -> Z*cycles - Column z_remainder = Z.reduce(chain_, cycles); - // std::cout << "cycle: "; - // for (auto& v : cycles){ - // std::cout << v.index() << " "; - // } - // std::cout << "\n"; - assert(z_remainder.empty()); - - IndexChain boundaries; - DequeColumn b_remainder = B.reduce(cycles, boundaries); - - // add up columns of C indexed by boundaries - typedef typename Column::value_type Entry; - auto row_cmp = [this](const Entry& e1, const Entry& e2) - { return this->cmp()(row(e1), row(e2)); }; - Column chain; - for (auto& x : boundaries) - Chain::addto(chain, x.element(), C.col(x.index()), field(), row_cmp); - chain.push_back(Entry(field().neg(field().id()), IndexPair(cell_indices++,0))); - - if (b_remainder.empty()) // birth - { - // std::cout << " birth" << std::endl; - Index z_col = z_indicies_last++; - Z.set(z_col, std::move(chain)); - birth_index[z_col] = op; - return unpaired(); - } - else // death - { - // std::cout << " death" << std::endl; - Index b_col = b_indices++; - Index pair = row(b_remainder.back()); - B.set(b_col, std::move(b_remainder)); - C.set(b_col, std::move(chain)); - return birth_index[pair]; - } -} - -template -typename dionysus::ZigzagPersistence::Index -dionysus::ZigzagPersistence:: -remove_impl(Index cell) -{ - //std::cout << "remove(" << cell << ")" << std::endl; - - Index op = operations++; - - typedef typename Column::value_type Entry; - auto row_cmp = [this](const Entry& e1, const Entry& e2) - { return this->cmp()(row(e1), row(e2)); }; - typedef typename DequeColumn::value_type DequeEntry; - auto b_row_cmp = [this](const DequeEntry& e1, const DequeEntry& e2) - { return this->cmp()(row(e1), row(e2)); }; - - IndexChain z_row; - for (auto& x : Z.row(cell)) - z_row.emplace_back(x.element(), col(x)); - - if (z_row.empty()) // birth - { - //std::cout << " birth" << std::endl; - Row& c_row = C.row(cell); - // c_row.front() may not be the first column in order, but that doesn't really matter, does it? (TODO) - auto& c_front = c_row.front(); - - Index j = col(c_front); - Index l = row(B.col(j).back()); - - //std::cout << j << ' ' << l << std::endl; - - // cycle = ZB[j] = DC[j] - Column cycle; - for (auto& x : B.col(j)) - Chain::addto(cycle, x.element(), Z.col(row(x)), field(), row_cmp); - - //std::cout << "Cycle:" << std::endl; - //for (auto& x : cycle) - // std::cout << x.element() << ' ' << row(x) << std::endl; - - // 1: prepend the cycle - Index znew = z_indicies_first--; - Index oth = Z.set(znew, std::move(cycle)); // oth records our collision (used in step 6) - birth_index[znew] = op; - - //std::cout << "znew oth: " << znew << ' ' << oth << std::endl; - //std::cout << "oth column:" << std::endl; - //for (auto& x : Z.col(oth)) - // std::cout << x.element() << ' ' << row(x) << std::endl; - - // 2: prepend the row to B - FieldElement m = field().neg(field().inv(c_front.element())); // m = -1/c - const DequeRow& b_row = B.prepend_row(znew, m, c_row); - //std::cout << "Prepended row with multiplier: " << m << " (" << b_row.size() << ")" << std::endl; - - // 3: subtract C[j] from every C[k] - const Column& Cj = C.col(j); - - // use the copy of c_row in B, since c_row will be modified in the following loop - for (auto it = std::next(b_row.begin()); it != b_row.end(); ++it) - { - Index c = col(*it); - assert(c != j); - //std::cout << "adding to " << c << " in C" << std::endl; - Chain::addto(C.col(c), it->element(), Cj, field(), row_cmp); // using it->element() since b_row = m*c_row - C.fix(c); // old elements got removed via auto_unlink_hook - // we don't need lows in C, so not updating them - } - //std::cout << "Done with step 3" << std::endl; - - // 4: subtract B[j] from every B[k] that has l - // (we don't need to update C because ZB[j] = 0 after step 2) - DequeColumn& Bj = B.col(j); - FieldElement bm = field().neg(field().inv(Bj.back().element())); // bm = -1/B[l,j] - IndexChain Bl_row; // make a copy of Bl_row, since it will be changing - for (auto& x : B.row(l)) - { - if (col(x) == j) - continue; - Bl_row.emplace_back(x.element(), col(x)); - } - for (auto& x : Bl_row) - { - Index c = x.index(); - assert(c != j); - Chain::addto(B.col(c), field().mul(bm, x.element()), Bj, field(), b_row_cmp); - B.fix(c); // old elements got removed via auto_unlink_hook - // l cannot be the low in c, so no need to update lows - } - //std::cout << "Done with step 4" << std::endl; - - // 5: drop row l and column j from B; drop column l from Z; drop column j from C - B.drop_col(j); - assert(B.row(l).empty()); - B.drop_row(l); - Index Zl_low = row(Z.col(l).back()); - Z.drop_col(l); - birth_index.erase(l); - C.drop_col(j); - assert(Z.row(cell).empty()); - assert(C.row(cell).empty()); - C.drop_row(cell); - Z.drop_row(cell); - //std::cout << "Done with step 5" << std::endl; - if (oth == l) // we just dropped our collision in Z - oth = znew; - else - Z.drop_low(Zl_low); - - // 6: reduce Z - std::unordered_map b_changes; // the columns to add in B to apply row changes - Index cur = znew; - while (oth != cur) - { - Column& cur_col = Z.col(cur); - Column& oth_col = Z.col(oth); - assert(row(cur_col.back()) == row(oth_col.back())); - //std::cout << "--- " << cur << " (" << cur_col.size() << ") " << oth << " (" << oth_col.size() << ")" << std::endl; - FieldElement m1 = cur_col.back().element(); - FieldElement m2 = oth_col.back().element(); - FieldElement m2_div_m1 = field().div(m2, m1); - Chain::addto(oth_col, field().neg(m2_div_m1), cur_col, field(), row_cmp); - Z.fix(oth, oth_col); - - // record the changes we need to make in B; - // because there is only one collision in the matrix during the reduction, - // once we use a row as the source, we never revisit it. This means once the row is updated in B, - // we never touch it again, so below record is fine. - for (auto& x : this->B.row(oth)) - b_changes[col(x)].emplace_back(field().mul(x.element(), m2_div_m1), cur, col(x)); - - cur = oth; - Index low = row(oth_col.back()); - if (Z.is_low(low)) - oth = Z.low(low); - //std::cout << "--- -- new low: " << low << ' ' << cur << ' ' << oth << std::endl; - - if (cmp()(oth, cur)) - std::swap(oth, cur); - else - Z.update_low(cur); - } - - // apply changes in B (the complexity here could get ugly) - for (auto& bx : b_changes) - { - std::sort(bx.second.begin(), bx.second.end(), b_row_cmp); - Chain::addto(B.col(bx.first), field().id(), bx.second, field(), b_row_cmp); - B.fix(bx.first); - // no need to update low (additions from bottom up) - } - //std::cout << "Done with step 6" << std::endl; - - return unpaired(); - } - else // death - { - //std::cout << " death" << std::endl; - - auto index_chain_cmp = [this](const typename IndexChain::value_type& e1, const typename IndexChain::value_type& e2) - { return this->cmp()(e1.index(), e2.index()); }; - - // 1: change basis to clear z_row - std::sort(z_row.begin(), z_row.end(), index_chain_cmp); // this adds a log factor, but it makes life easier - Index j = z_row.front().index(); - FieldElement e = z_row.front().element(); - - if (z_row.size() > 1) - { - // figure out the columns we use for reduction - typedef typename IndexChain::const_iterator RowIterator; - std::vector reducers; - reducers.push_back(z_row.begin()); - for (RowIterator it = std::next(z_row.begin()); it != z_row.end(); ++it) - { - Index c = it->index(); - - assert(Z.col_exists(c)); - assert(Z.col_exists(reducers.back()->index())); - if (cmp()(row(Z.col(c).back()), - row(Z.col(reducers.back()->index()).back()))) - reducers.push_back(it); - } - reducers.push_back(z_row.end()); - //std::cout << "reducers.size(): " << reducers.size() << std::endl; - //std::cout << "z_row.size(): " << z_row.size() << std::endl; - - - std::map b_changes; // the rows to add to B - auto add_in_z = [this,&b_changes,&row_cmp,&index_chain_cmp](Index to, Index from, FieldElement m, FieldElement e) - { - //std::cout << " add_in_z: " << from << ' ' << to << std::endl; - - FieldElement mult = this->field().mul(m, e); - assert(Z.col_exists(to)); - assert(Z.col_exists(from)); - Chain::addto(Z.col(to), mult, Z.col(from), this->field(), row_cmp); - assert(!Z.col(to).empty()); - this->Z.fix(to); // NB: rows will be linked in the back, so the iterators are Ok - this->Z.update_low(to); - - // subtract B.row(to) from B.row(from) - IndexChain Bto_row; - for (auto& x : this->B.row(to)) - Bto_row.emplace_back(x.element(), col(x)); - std::sort(Bto_row.begin(), Bto_row.end(), index_chain_cmp); - -#if 0 - for (auto& x : this->B.row(to)) - std::cout << x.element() << ' ' << row(x) << ' ' << col(x) << std::endl; - - std::cout << "---\n"; - - for (auto& x : this->B.row(from)) - std::cout << x.element() << ' ' << row(x) << ' ' << col(x) << std::endl; -#endif - - Chain::addto(b_changes[from], this->field().neg(mult), Bto_row, this->field(), index_chain_cmp); - - // if there is b_changes[to] add it, too - auto it = b_changes.find(to); - if (it != b_changes.end()) - Chain::addto(b_changes[from], this->field().neg(mult), it->second, this->field(), index_chain_cmp); - }; - Index last_low = row(Z.col(reducers[reducers.size() - 2]->index()).back()); - for (int i = reducers.size() - 2; i >= 0; --i) - { - auto rit = reducers[i]; - FieldElement m = field().neg(field().inv(rit->element())); - - for (auto it = std::next(rit); it != reducers[i+1]; ++it) - add_in_z(it->index(), rit->index(), m, it->element()); - - if (static_cast(i + 1) != reducers.size() - 1) - { - auto it = reducers[i+1]; - add_in_z(it->index(), rit->index(), m, it->element()); - } - } - if (reducers.size() > 2) - Z.drop_low(last_low); - - // apply changes in b (the complexity here could get ugly) - // Specifically, transpose b_changes and add it in - std::unordered_map b_changes_transposed; - for (auto& b_row : b_changes) - for (auto& bx : b_row.second) - b_changes_transposed[bx.index()].emplace_back(bx.element(), b_row.first, bx.index()); - - for (auto& b_col : b_changes_transposed) - { -#if 0 - std::cout << "Adding:" << std::endl; - for (auto& x : b_col.second) - std::cout << x.element() << ' ' << row(x) << ' ' << col(x) << std::endl; -#endif - Chain::addto(B.col(b_col.first), field().id(), b_col.second, field(), b_row_cmp); - assert(!B.col(b_col.first).empty()); - B.fix(b_col.first); - // no need to update low (additions from bottom up) - } - } // z_row.size() > 1 - - // 2: subtract cycle from every chain in C - const Column& Zj = Z.col(j); - //std::cout << "Zj:" << std::endl; - //for (auto& x : Zj) - // std::cout << x.element() << " * " << row(x) << std::endl; - - IndexChain Ccols; // save the columns in C, we'll be modifying C.row(cell) - for (auto& x : C.row(cell)) - Ccols.emplace_back(x.element(), col(x)); - - for (auto& x : Ccols) - { - Index c = x.index(); - FieldElement m = field().neg(field().div(x.element(), e)); // m = -C[k][cell]/Z[j][cell] - //std::cout << "Adding to C: " << c << std::endl; - Chain::addto(C.col(c), m, Zj, field(), row_cmp); - C.fix(c); - // we don't care about lows in C, so don't update them - } - - // 3: drop - assert(Z.row(cell).size() == 1); - Z.drop_col(j); - assert(Z.row(cell).empty()); - assert(C.row(cell).empty()); - Z.drop_row(cell); - C.drop_row(cell); - assert(B.row(j).empty()); - B.drop_row(j); - - Index birth = birth_index[j]; - birth_index.erase(j); - - return birth; - } -} - - -/* debug routines */ -template -void -dionysus::ZigzagPersistence:: -check_b_cols() const -{ - // check that entries in B refer to existing Z columns - bool stop = false; - for (auto& b : B.columns()) - for (auto& x : b.second) - if (!Z.col_exists(row(x))) - { - std::cout << "B refers to a non-existent column in Z: " << row(x) << std::endl; - stop = true; - } - if (stop) - assert(0); -} - -template -template -void -dionysus::ZigzagPersistence:: -check_cycles(const SimplexToIndex& s2i, const IndexToSimplex& i2s) const -{ - typedef typename Column::value_type Entry; - auto row_cmp = [this](const Entry& e1, const Entry& e2) - { return this->cmp()(row(e1), row(e2)); }; - - for (auto& z : Z.columns()) - { - Column res; - for (auto& x : z.second) - { - Column bdry = boundary(row(x), s2i, i2s); - Chain::addto(res, x.element(), bdry, field(), row_cmp); - } - assert(res.empty()); - } -} - -template -template -void -dionysus::ZigzagPersistence:: -check_boundaries(const SimplexToIndex& s2i, const IndexToSimplex& i2s) const -{ - check_cycles(s2i, i2s); - - for (auto& x : B.columns()) - if (!C.col_exists(x.first)) - { - std::cout << x.first << " in B, but not in C" << std::endl; - assert(0); - } - - for (auto& x : C.columns()) - if (!B.col_exists(x.first)) - { - std::cout << x.first << " in B, but not in C" << std::endl; - assert(0); - } - - for (auto& x : B.columns()) - { - auto zb = zb_dot(x.first); - auto dc = dc_dot(x.first, s2i, i2s); - - auto it_zb = zb.begin(), - it_dc = dc.begin(); - for (; it_zb != zb.end(); ++it_zb, ++it_dc) - { - if (it_zb->element() != it_dc->element() || row(*it_zb) != row(*it_dc)) - { - std::cout << "Boundary mismatch: " << x.first << std::endl; - std::cout << "===" << std::endl; - for (auto& x : zb) - std::cout << " " << x.element() << ' ' << row(x) << std::endl; - for (auto& y : B.col(x.first)) - { - std::cout << " " << y.element() << " * " << row(y) << std::endl; - for (auto& z : Z.col(row(y))) - std::cout << " " << z.element() << ' ' << row(z) << std::endl; - std::cout << " ---" << std::endl; - } - std::cout << "===" << std::endl; - for (auto& x : dc) - std::cout << " " << x.element() << ' ' << row(x) << std::endl; - for (auto& y : C.col(x.first)) - { - std::cout << " " << y.element() << " * " << row(y) << std::endl; - for (auto& z : boundary(row(y), s2i, i2s)) - std::cout << " " << z.element() << ' ' << row(z) << std::endl; - std::cout << " ---" << std::endl; - } - assert(0); - } - } - if (it_zb != zb.end() || it_dc != dc.end()) - { - std::cout << "zb.end() doesn't match dc.end()" << std::endl; - assert(0); - } - } -} - -template -typename dionysus::ZigzagPersistence::Column -dionysus::ZigzagPersistence:: -zb_dot(Index c) const -{ - typedef typename Column::value_type Entry; - auto row_cmp = [this](const Entry& e1, const Entry& e2) - { return this->cmp()(row(e1), row(e2)); }; - Column res; - for (auto& x : B.col(c)) - Chain::addto(res, x.element(), Z.col(row(x)), field(), row_cmp); - - return res; -} - -template -template -typename dionysus::ZigzagPersistence::Column -dionysus::ZigzagPersistence:: -dc_dot(Index c, const SimplexToIndex& s2i, const IndexToSimplex& i2s) const -{ - typedef typename Column::value_type Entry; - auto row_cmp = [this](const Entry& e1, const Entry& e2) - { return this->cmp()(row(e1), row(e2)); }; - Column res; - for (auto& x : C.col(c)) - { - Column bdry = boundary(row(x), s2i, i2s); - Chain::addto(res, x.element(), bdry, field(), row_cmp); - } - return res; -} - -template -template -typename dionysus::ZigzagPersistence::Column -dionysus::ZigzagPersistence:: -boundary(Index i, const SimplexToIndex& s2i, const IndexToSimplex& i2s) const -{ - typedef typename Column::value_type Entry; - auto row_cmp = [this](const Entry& e1, const Entry& e2) - { return this->cmp()(row(e1), row(e2)); }; - Column bdry; - auto s = i2s(i); - for (auto y : s.boundary(field())) - bdry.emplace_back(y.element(), s2i(y.index()), 0); - std::sort(bdry.begin(), bdry.end(), row_cmp); - return bdry; -} - -template -void -dionysus::ZigzagPersistence:: -check_sorted() const -{ - typedef typename Column::value_type Entry; - auto row_cmp = [this](const Entry& e1, const Entry& e2) - { return this->cmp()(row(e1), row(e2)); }; - typedef typename DequeColumn::value_type DequeEntry; - auto b_row_cmp = [this](const DequeEntry& e1, const DequeEntry& e2) - { return this->cmp()(row(e1), row(e2)); }; - - for (auto& x : Z.columns()) - if (!std::is_sorted(x.second.begin(), x.second.end(), row_cmp)) - { - std::cout << "Z column not sorted: " << x.first << std::endl; - assert(0); - } - for (auto& x : C.columns()) - if (!std::is_sorted(x.second.begin(), x.second.end(), row_cmp)) - { - std::cout << "C column not sorted: " << x.first << std::endl; - assert(0); - } - for (auto& x : B.columns()) - if (!std::is_sorted(x.second.begin(), x.second.end(), b_row_cmp)) - { - std::cout << "B column not sorted: " << x.first << std::endl; - assert(0); - } -} - diff --git a/src/Zigzag_persistence/example/ext_zz/fzz/fzz.cpp b/src/Zigzag_persistence/example/ext_zz/fzz/fzz.cpp deleted file mode 100644 index 5217a4f38c..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/fzz/fzz.cpp +++ /dev/null @@ -1,206 +0,0 @@ -#include "fzz.h" - -#include - -// phat headers -// wrapper algorithm that computes the persistence pairs of a given boundary matrix using a specified algorithm -#include - -// main data structure (choice affects performance) -#include -#include - -// algorithm (choice affects performance) -#include -#include -#include -#include - -namespace FZZ { - -template -class VecHash { -public: - size_t operator()(const std::vector& v) const; -}; - -template -size_t VecHash - ::operator()(const std::vector& v) const { - - std::size_t seed = 0; - - for (auto e : v) { boost::hash_combine(seed, e); } - - return seed; -} - -template -class VecEqual { -public: - bool operator()(const std::vector& v1, - const std::vector& v2) const; -}; - -template -bool VecEqual - ::operator()(const std::vector& v1, - const std::vector& v2) const { - - if (v1.size() != v2.size()) { return false; } - - for (unsigned int i = 0; i < v1.size(); i ++) { - if (v1[i] != v2[i]) { - return false; - } - } - - return true; -} - -typedef std::unordered_map< Simplex, Integer, - VecHash, VecEqual > SimplexIdMap; - -void getBoundaryChainPhat(const std::vector &id_maps, - const Simplex &simp, std::vector &bound_c) { - - bound_c.clear(); - - if (simp.size() <= 1) { return; } - - bound_c.reserve(simp.size()); - - Simplex bound_simp(simp.begin()+1, simp.end()); - bound_c.push_back(id_maps.at(bound_simp.size() - 1).at(bound_simp)); - - for (unsigned int i = 0; i < simp.size()-1; ++i) { - bound_simp[i] = simp[i]; - bound_c.push_back(id_maps.at(bound_simp.size() - 1).at(bound_simp)); - } - - std::sort(bound_c.begin(), bound_c.end()); -} - -inline Integer getDim(const std::vector &bound_c) { - if (bound_c.size() == 0) { return 0; } - return bound_c.size() - 1; -} - -void FastZigzag::compute(const std::vector &filt_simp, - const std::vector &filt_op, - std::vector< std::tuple > *persistence) { - - orig_f_add_id.clear(); - orig_f_del_id.clear(); - persistence->clear(); - - simp_num = 0; - Integer max_dim = 0; - for (unsigned int i = 0; i < filt_op.size(); ++i) { - if (filt_op[i]) { - ++simp_num; - if (static_cast(filt_simp[i].size()) - 1 > max_dim) { max_dim = filt_simp[i].size() - 1; } - } - } - - std::vector bound_c; - // phat::boundary_matrix< phat::vector_vector > bound_chains; - phat::boundary_matrix< phat::bit_tree_pivot_column > bound_chains; - bound_chains.set_num_cols(simp_num * 2 + 1); - - // Add the Omega vertex for the coning - bound_chains.set_col(0, bound_c); - bound_chains.set_dim(0, 0); - - orig_f_add_id.reserve(simp_num); - orig_f_del_id.reserve(simp_num); - - std::vector del_ids; - del_ids.reserve(simp_num); - - std::vector *p_id_maps = new std::vector(max_dim+1); - std::vector &id_maps = *p_id_maps; - - Integer orig_f_id = 0; - Integer s_id = 1; - - for (unsigned int i = 0; i < filt_simp.size(); ++i) { - const Simplex &simp = filt_simp[i]; - - if (filt_op[i]) { - getBoundaryChainPhat(id_maps, simp, bound_c); - bound_chains.set_col(s_id, bound_c); - bound_chains.set_dim(s_id, getDim(bound_c)); - - // assert(s_id == bound_chains.size()-1); - id_maps.at(simp.size() - 1)[simp] = s_id; - orig_f_add_id.push_back(orig_f_id); - s_id ++; - } else { - del_ids.push_back(id_maps.at(simp.size() - 1)[simp]); - id_maps.at(simp.size() - 1).erase(simp); - orig_f_del_id.push_back(orig_f_id); - } - - orig_f_id ++; - } - - for (Integer i = id_maps.size() - 1; i >= 0; -- i) { - for (const auto &it : id_maps.at(i)) { - del_ids.push_back(it.second); - orig_f_del_id.push_back(orig_f_id); - orig_f_id ++; - } - } - - assert(del_ids.size() == s_id-1); - delete p_id_maps; - - assert(simp_num == del_ids.size()); - - std::vector cone_sid(simp_num+1); - - for (auto del_id_it = del_ids.rbegin(); del_id_it != del_ids.rend(); ++del_id_it) { - bound_c.clear(); - bound_c.push_back(*del_id_it); - - std::vector orig_bound_c; - bound_chains.get_col(*del_id_it, orig_bound_c); - - if (orig_bound_c.size() == 0) { - bound_c.push_back(0); - } else { - for (auto bsimp : orig_bound_c) { - // assert(cone_sid[bsimp] >= 0); - bound_c.push_back(cone_sid[bsimp]); - } - } - - std::sort(bound_c.begin(), bound_c.end()); - - bound_chains.set_col(s_id, bound_c); - bound_chains.set_dim(s_id, getDim(bound_c)); - - cone_sid[*del_id_it] = s_id; - - s_id ++; - } - - phat::persistence_pairs pairs; - phat::compute_persistence_pairs< phat::twist_reduction >( pairs, bound_chains ); - - for (phat::index idx = 0; idx < pairs.get_num_pairs(); idx++) { - Integer b = pairs.get_pair(idx).first; - Integer d = pairs.get_pair(idx).second - 1; - Integer p = bound_chains.get_dim(b); - - if (d < simp_num) { mapOrdIntv(b, d); } - else { mapRelExtIntv(p, b, d); } - - if (b > static_cast(filt_simp.size())) { continue; } - if (d > static_cast(filt_simp.size())) { d = filt_simp.size(); } - persistence->emplace_back(b, d, p); - } -} - -} // namespace FZZ { diff --git a/src/Zigzag_persistence/example/ext_zz/fzz/fzz.h b/src/Zigzag_persistence/example/ext_zz/fzz/fzz.h deleted file mode 100644 index 8613bc3793..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/fzz/fzz.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef _FZZ_H_ -#define _FZZ_H_ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace FZZ { - -typedef int Integer; -typedef std::vector Simplex; - -class FastZigzag { -public: - /* - 'filt_simp' and 'filt_op' should have the same length which altogether - specify the input zigzag filtration. 'filt_simp' specifies the simplices - being added or deleted (following the order of the filtration) and - 'filt_op' specifies whether it's an addition (true) or deletion (false). - 'persistence' returns the barcode, with the first element of the tuple - being the birth, the second element being the death, and the third - being the dimension. - */ - void compute( - const std::vector &filt_simp, - const std::vector &filt_op, - std::vector< std::tuple > *persistence); - -private: - void mapOrdIntv(Integer &b, Integer &d) { - // assert(b-1 > 0); - // assert(d < orig_f_add_id.size()); - - // Up-down interval is same, - // so directly map to interval of input filtration - b = orig_f_add_id[b-1] + 1; - d = orig_f_add_id[d]; - } - - void mapRelExtIntv(Integer &p, Integer &b, Integer &d) { - // assert(d >= simp_num); - - if (b > simp_num) { // Open-closed - // Map to up-down interval - std::swap(b, d); - b = 3*simp_num - b; - d = 3*simp_num - d; - p --; - - // Map to interval of input filtration - b = orig_f_del_id[b-1-simp_num] + 1; - d = orig_f_del_id[d-simp_num]; - } else { // Closed-closed - // Map to up-down interval - d = 3*simp_num - d-1; - - // Map to interval of input filtration - b = orig_f_add_id[b-1]; - d = orig_f_del_id[d-simp_num]; - - if (b < d) { - b = b+1; - } else { - std::swap(b, d); - b = b+1; - p = p-1; - } - } - } - -private: - // 'orig_f_add_id' and 'orig_f_del_id' form a mapping - // from the up-down filtration to the original filtration - std::vector orig_f_add_id; - std::vector orig_f_del_id; - - Integer simp_num; -}; - -} - -#endif diff --git a/src/Zigzag_persistence/example/ext_zz/phat/algorithms/chunk_reduction.h b/src/Zigzag_persistence/example/ext_zz/phat/algorithms/chunk_reduction.h deleted file mode 100644 index 179702312f..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/phat/algorithms/chunk_reduction.h +++ /dev/null @@ -1,223 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - class chunk_reduction { - public: - enum column_type { GLOBAL - , LOCAL_POSITIVE - , LOCAL_NEGATIVE }; - - public: - template< typename Representation > - void operator() ( boundary_matrix< Representation >& boundary_matrix ) { - - - const index nr_columns = boundary_matrix.get_num_cols(); - if( omp_get_max_threads( ) > nr_columns ) - omp_set_num_threads( 1 ); - - const dimension max_dim = boundary_matrix.get_max_dim(); - - std::vector< index > lowest_one_lookup( nr_columns, -1 ); - std::vector < column_type > column_type( nr_columns, GLOBAL ); - std::vector< char > is_active( nr_columns, false ); - - const index chunk_size = omp_get_max_threads() == 1 ? (index)sqrt( (double)nr_columns ) : nr_columns / omp_get_max_threads(); - - std::vector< index > chunk_boundaries; - for( index cur_boundary = 0; cur_boundary < nr_columns; cur_boundary += chunk_size ) - chunk_boundaries.push_back( cur_boundary ); - chunk_boundaries.push_back( nr_columns ); - - for( dimension cur_dim = max_dim; cur_dim >= 1; cur_dim-- ) { - // Phase 1: Reduce chunks locally -- 1st pass - #pragma omp parallel for schedule( guided, 1 ) - for( index chunk_id = 0; chunk_id < (index)chunk_boundaries.size() - 1; chunk_id++ ) - _local_chunk_reduction( boundary_matrix, lowest_one_lookup, column_type, cur_dim, - chunk_boundaries[ chunk_id ], chunk_boundaries[ chunk_id + 1 ], chunk_boundaries[ chunk_id ] ); - boundary_matrix.sync(); - - // Phase 1: Reduce chunks locally -- 2nd pass - #pragma omp parallel for schedule( guided, 1 ) - for( index chunk_id = 1; chunk_id < (index)chunk_boundaries.size( ) - 1; chunk_id++ ) - _local_chunk_reduction( boundary_matrix, lowest_one_lookup, column_type, cur_dim, - chunk_boundaries[ chunk_id ], chunk_boundaries[ chunk_id + 1 ], chunk_boundaries[ chunk_id - 1 ] ); - boundary_matrix.sync( ); - } - - // get global columns - std::vector< index > global_columns; - for( index cur_col_idx = 0; cur_col_idx < nr_columns; cur_col_idx++ ) - if( column_type[ cur_col_idx ] == GLOBAL ) - global_columns.push_back( cur_col_idx ); - - // get active columns - #pragma omp parallel for - for( index idx = 0; idx < (index)global_columns.size(); idx++ ) - is_active[ global_columns[ idx ] ] = true; - _get_active_columns( boundary_matrix, lowest_one_lookup, column_type, global_columns, is_active ); - - // Phase 2+3: Simplify columns and reduce them - for( dimension cur_dim = max_dim; cur_dim >= 1; cur_dim-- ) { - // Phase 2: Simplify columns - std::vector< index > temp_col; - #pragma omp parallel for schedule( guided, 1 ), private( temp_col ) - for( index idx = 0; idx < (index)global_columns.size(); idx++ ) - if( boundary_matrix.get_dim( global_columns[ idx ] ) == cur_dim ) - _global_column_simplification( global_columns[ idx ], boundary_matrix, lowest_one_lookup, column_type, is_active, temp_col ); - boundary_matrix.sync(); - - // Phase 3: Reduce columns - for( index idx = 0; idx < (index)global_columns.size(); idx++ ) { - index cur_col = global_columns[ idx ]; - if( boundary_matrix.get_dim( cur_col ) == cur_dim && column_type[ cur_col ] == GLOBAL ) { - index lowest_one = boundary_matrix.get_max_index( cur_col ); - while( lowest_one != -1 && lowest_one_lookup[ lowest_one ] != -1 ) { - boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); - lowest_one = boundary_matrix.get_max_index( cur_col ); - } - if( lowest_one != -1 ) { - lowest_one_lookup[ lowest_one ] = cur_col; - boundary_matrix.clear( lowest_one ); - } - boundary_matrix.finalize( cur_col ); - } - } - } - - boundary_matrix.sync(); - } - - protected: - template< typename Representation > - void _local_chunk_reduction( boundary_matrix< Representation >& boundary_matrix - , std::vector& lowest_one_lookup - , std::vector< column_type >& column_type - , const dimension cur_dim - , const index chunk_begin - , const index chunk_end - , const index row_begin ) { - - for( index cur_col = chunk_begin; cur_col < chunk_end; cur_col++ ) { - if( column_type[ cur_col ] == GLOBAL && boundary_matrix.get_dim( cur_col ) == cur_dim ) { - index lowest_one = boundary_matrix.get_max_index( cur_col ); - while( lowest_one != -1 && lowest_one >= row_begin && lowest_one_lookup[ lowest_one ] != -1 ) { - boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); - lowest_one = boundary_matrix.get_max_index( cur_col ); - } - if( lowest_one >= row_begin ) { - lowest_one_lookup[ lowest_one ] = cur_col; - column_type[ cur_col ] = LOCAL_NEGATIVE; - column_type[ lowest_one ] = LOCAL_POSITIVE; - boundary_matrix.clear( lowest_one ); - boundary_matrix.finalize( cur_col ); - } - } - } - } - - template< typename Representation > - void _get_active_columns( const boundary_matrix< Representation >& boundary_matrix - , const std::vector< index >& lowest_one_lookup - , const std::vector< column_type >& column_type - , const std::vector< index >& global_columns - , std::vector< char >& is_active ) { - - const index nr_columns = boundary_matrix.get_num_cols(); - std::vector< char > finished( nr_columns, false ); - - std::vector< std::pair < index, index > > stack; - std::vector< index > cur_col_values; - #pragma omp parallel for schedule( guided, 1 ), private( stack, cur_col_values ) - for( index idx = 0; idx < (index)global_columns.size(); idx++ ) { - bool pop_next = false; - index start_col = global_columns[ idx ]; - stack.push_back( std::pair< index, index >( start_col, -1 ) ); - while( !stack.empty() ) { - index cur_col = stack.back().first; - index prev_col = stack.back().second; - if( pop_next ) { - stack.pop_back(); - pop_next = false; - if( prev_col != -1 ) { - if( is_active[ cur_col ] ) { - is_active[ prev_col ] = true; - } - if( prev_col == stack.back().first ) { - finished[ prev_col ] = true; - pop_next = true; - } - } - } else { - pop_next = true; - boundary_matrix.get_col( cur_col, cur_col_values ); - for( index idx = 0; idx < (index) cur_col_values.size(); idx++ ) { - index cur_row = cur_col_values[ idx ]; - if( ( column_type[ cur_row ] == GLOBAL ) ) { - is_active[ cur_col ] = true; - } else if( column_type[ cur_row ] == LOCAL_POSITIVE ) { - index next_col = lowest_one_lookup[ cur_row ]; - if( next_col != cur_col && !finished[ cur_col ] ) { - stack.push_back( std::make_pair( next_col, cur_col ) ); - pop_next = false; - } - } - } - } - } - } - } - - template< typename Representation > - void _global_column_simplification( const index col_idx - , boundary_matrix< Representation >& boundary_matrix - , const std::vector< index >& lowest_one_lookup - , const std::vector< column_type >& column_type - , const std::vector< char >& is_active - , std::vector< index >& temp_col ) - { - temp_col.clear(); - while( !boundary_matrix.is_empty( col_idx ) ) { - index cur_row = boundary_matrix.get_max_index( col_idx ); - switch( column_type[ cur_row ] ) { - case GLOBAL: - temp_col.push_back( cur_row ); - boundary_matrix.remove_max( col_idx ); - break; - case LOCAL_NEGATIVE: - boundary_matrix.remove_max( col_idx ); - break; - case LOCAL_POSITIVE: - if( is_active[ lowest_one_lookup[ cur_row ] ] ) - boundary_matrix.add_to( lowest_one_lookup[ cur_row ], col_idx ); - else - boundary_matrix.remove_max( col_idx ); - break; - } - } - std::reverse( temp_col.begin(), temp_col.end() ); - boundary_matrix.set_col( col_idx, temp_col ); - } - }; -} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/algorithms/row_reduction.h b/src/Zigzag_persistence/example/ext_zz/phat/algorithms/row_reduction.h deleted file mode 100644 index cdd1a8fd18..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/phat/algorithms/row_reduction.h +++ /dev/null @@ -1,56 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - class row_reduction { - public: - template< typename Representation > - void operator() ( boundary_matrix< Representation >& boundary_matrix ) { - - const index nr_columns = boundary_matrix.get_num_cols(); - std::vector< std::vector< index > > lowest_one_lookup( nr_columns ); - - for( index cur_col = nr_columns - 1; cur_col >= 0; cur_col-- ) { - if( !boundary_matrix.is_empty( cur_col ) ) - lowest_one_lookup[ boundary_matrix.get_max_index( cur_col ) ].push_back( cur_col ); - - if( !lowest_one_lookup[ cur_col ].empty() ) { - boundary_matrix.clear( cur_col ); - boundary_matrix.finalize( cur_col ); - std::vector< index >& cols_with_cur_lowest = lowest_one_lookup[ cur_col ]; - index source = *min_element( cols_with_cur_lowest.begin(), cols_with_cur_lowest.end() ); - for( index idx = 0; idx < (index)cols_with_cur_lowest.size(); idx++ ) { - index target = cols_with_cur_lowest[ idx ]; - if( target != source && !boundary_matrix.is_empty( target ) ) { - boundary_matrix.add_to( source, target ); - if( !boundary_matrix.is_empty( target ) ) { - index lowest_one_of_target = boundary_matrix.get_max_index( target ); - lowest_one_lookup[ lowest_one_of_target ].push_back( target ); - } - } - } - } - } - } - }; -} \ No newline at end of file diff --git a/src/Zigzag_persistence/example/ext_zz/phat/algorithms/spectral_sequence_reduction.h b/src/Zigzag_persistence/example/ext_zz/phat/algorithms/spectral_sequence_reduction.h deleted file mode 100644 index bf442e6089..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/phat/algorithms/spectral_sequence_reduction.h +++ /dev/null @@ -1,80 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - class spectral_sequence_reduction { - public: - template< typename Representation > - void operator () ( boundary_matrix< Representation >& boundary_matrix ) { - - const index nr_columns = boundary_matrix.get_num_cols(); - std::vector< index > lowest_one_lookup( nr_columns, -1 ); - - //const index num_stripes = (index) sqrt( (double)nr_columns ); - const index num_stripes = omp_get_max_threads(); - - index block_size = ( nr_columns % num_stripes == 0 ) ? nr_columns / num_stripes : block_size = nr_columns / num_stripes + 1; - - std::vector< std::vector< index > > unreduced_cols_cur_pass( num_stripes ); - std::vector< std::vector< index > > unreduced_cols_next_pass( num_stripes ); - - for( index cur_dim = boundary_matrix.get_max_dim(); cur_dim >= 1 ; cur_dim-- ) { - #pragma omp parallel for schedule( guided, 1 ) - for( index cur_stripe = 0; cur_stripe < num_stripes; cur_stripe++ ) { - index col_begin = cur_stripe * block_size; - index col_end = std::min( (cur_stripe+1) * block_size, nr_columns ); - for( index cur_col = col_begin; cur_col < col_end; cur_col++ ) - if( boundary_matrix.get_dim( cur_col ) == cur_dim && boundary_matrix.get_max_index( cur_col ) != -1 ) - unreduced_cols_cur_pass[ cur_stripe ].push_back( cur_col ); - } - for( index cur_pass = 0; cur_pass < num_stripes; cur_pass++ ) { - boundary_matrix.sync(); - #pragma omp parallel for schedule( guided, 1 ) - for( int cur_stripe = 0; cur_stripe < num_stripes; cur_stripe++ ) { - index row_begin = (cur_stripe - cur_pass) * block_size; - index row_end = row_begin + block_size; - unreduced_cols_next_pass[ cur_stripe ].clear(); - for( index idx = 0; idx < (index)unreduced_cols_cur_pass[ cur_stripe ].size(); idx++ ) { - index cur_col = unreduced_cols_cur_pass[ cur_stripe ][ idx ]; - index lowest_one = boundary_matrix.get_max_index( cur_col ); - while( lowest_one != -1 && lowest_one >= row_begin && lowest_one < row_end && lowest_one_lookup[ lowest_one ] != -1 ) { - boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); - lowest_one = boundary_matrix.get_max_index( cur_col ); - } - if( lowest_one != -1 ) { - if( lowest_one >= row_begin && lowest_one < row_end ) { - lowest_one_lookup[ lowest_one ] = cur_col; - boundary_matrix.clear( lowest_one ); - boundary_matrix.finalize( cur_col ); - } else { - unreduced_cols_next_pass[ cur_stripe ].push_back( cur_col ); - } - } - } - unreduced_cols_next_pass[ cur_stripe ].swap( unreduced_cols_cur_pass[ cur_stripe ] ); - } - } - } - } - }; -} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/algorithms/standard_reduction.h b/src/Zigzag_persistence/example/ext_zz/phat/algorithms/standard_reduction.h deleted file mode 100644 index e490a5e0d1..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/phat/algorithms/standard_reduction.h +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - class standard_reduction { - public: - template< typename Representation > - void operator() ( boundary_matrix< Representation >& boundary_matrix ) { - - const index nr_columns = boundary_matrix.get_num_cols(); - std::vector< index > lowest_one_lookup( nr_columns, -1 ); - - for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { - index lowest_one = boundary_matrix.get_max_index( cur_col ); - while( lowest_one != -1 && lowest_one_lookup[ lowest_one ] != -1 ) { - boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); - lowest_one = boundary_matrix.get_max_index( cur_col ); - } - if( lowest_one != -1 ) { - lowest_one_lookup[ lowest_one ] = cur_col; - } - boundary_matrix.finalize( cur_col ); - } - } - }; -} - diff --git a/src/Zigzag_persistence/example/ext_zz/phat/algorithms/twist_reduction.h b/src/Zigzag_persistence/example/ext_zz/phat/algorithms/twist_reduction.h deleted file mode 100644 index 2357df0256..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/phat/algorithms/twist_reduction.h +++ /dev/null @@ -1,51 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - class twist_reduction { - public: - template< typename Representation > - void operator () ( boundary_matrix< Representation >& boundary_matrix ) { - - const index nr_columns = boundary_matrix.get_num_cols(); - std::vector< index > lowest_one_lookup( nr_columns, -1 ); - - for( index cur_dim = boundary_matrix.get_max_dim(); cur_dim >= 1 ; cur_dim-- ) { - for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { - if( boundary_matrix.get_dim( cur_col ) == cur_dim ) { - index lowest_one = boundary_matrix.get_max_index( cur_col ); - while( lowest_one != -1 && lowest_one_lookup[ lowest_one ] != -1 ) { - boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); - lowest_one = boundary_matrix.get_max_index( cur_col ); - } - if( lowest_one != -1 ) { - lowest_one_lookup[ lowest_one ] = cur_col; - boundary_matrix.clear( lowest_one ); - } - boundary_matrix.finalize( cur_col ); - } - } - } - } - }; -} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/boundary_matrix.h b/src/Zigzag_persistence/example/ext_zz/phat/boundary_matrix.h deleted file mode 100644 index 10c66cca13..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/phat/boundary_matrix.h +++ /dev/null @@ -1,343 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -// interface class for the main data structure -- implementations of the interface can be found in ./representations -namespace phat { - template< class Representation = bit_tree_pivot_column > - class boundary_matrix - { - - protected: - Representation rep; - - // interface functions -- actual implementation and complexity depends on chosen @Representation template - public: - // get overall number of columns in boundary_matrix - index get_num_cols() const { return rep._get_num_cols(); } - - // set overall number of columns in boundary_matrix - void set_num_cols( index nr_of_columns ) { rep._set_num_cols( nr_of_columns ); } - - // get dimension of given index - dimension get_dim( index idx ) const { return rep._get_dim( idx ); } - - // set dimension of given index - void set_dim( index idx, dimension dim ) { rep._set_dim( idx, dim ); } - - // replaces content of @col with boundary of given index - void get_col( index idx, column& col ) const { col.clear(); rep._get_col( idx, col ); } - - // set column @idx to the values contained in @col - void set_col( index idx, const column& col ) { rep._set_col( idx, col ); } - - // true iff boundary of given column is empty - bool is_empty( index idx ) const { return rep._is_empty( idx ); } - - // largest index of given column (new name for lowestOne()) -- NOT thread-safe - index get_max_index( index idx ) const { return rep._get_max_index( idx ); } - - // removes maximal index from given column - void remove_max( index idx ) { rep._remove_max( idx ); } - - // adds column @source to column @target' - void add_to( index source, index target ) { rep._add_to( source, target ); } - - // clears given column - void clear( index idx ) { rep._clear( idx ); } - - // finalizes given column - void finalize( index idx ) { rep._finalize( idx ); } - - // syncronizes all internal data structures -- has to be called before and after any multithreaded access! - void sync() { rep._sync(); } - - // info functions -- independent of chosen 'Representation' - public: - // maximal dimension - dimension get_max_dim() const { - dimension cur_max_dim = 0; - for( index idx = 0; idx < get_num_cols(); idx++ ) - cur_max_dim = get_dim( idx ) > cur_max_dim ? get_dim( idx ) : cur_max_dim; - return cur_max_dim; - } - - // number of nonzero rows for given column @idx - index get_num_rows( index idx ) const { - column cur_col; - get_col( idx, cur_col ); - return cur_col.size(); - } - - // maximal number of nonzero rows of all columns - index get_max_col_entries() const { - index max_col_entries = -1; - const index nr_of_columns = get_num_cols(); - for( index idx = 0; idx < nr_of_columns; idx++ ) - max_col_entries = get_num_rows( idx ) > max_col_entries ? get_num_rows( idx ) : max_col_entries; - return max_col_entries; - } - - // maximal number of nonzero cols of all rows - index get_max_row_entries() const { - size_t max_row_entries = 0; - const index nr_of_columns = get_num_cols(); - std::vector< std::vector< index > > transposed_matrix( nr_of_columns ); - column temp_col; - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { - get_col( cur_col, temp_col ); - for( index idx = 0; idx < (index)temp_col.size(); idx++) - transposed_matrix[ temp_col[ idx ] ].push_back( cur_col ); - } - for( index idx = 0; idx < nr_of_columns; idx++ ) - max_row_entries = transposed_matrix[ idx ].size() > max_row_entries ? transposed_matrix[ idx ].size() : max_row_entries; - return max_row_entries; - } - - // overall number of entries in the matrix - index get_num_entries() const { - index number_of_nonzero_entries = 0; - const index nr_of_columns = get_num_cols(); - for( index idx = 0; idx < nr_of_columns; idx++ ) - number_of_nonzero_entries += get_num_rows( idx ); - return number_of_nonzero_entries; - } - - // operators / constructors - public: - boundary_matrix() {}; - - template< class OtherRepresentation > - boundary_matrix( const boundary_matrix< OtherRepresentation >& other ) { - *this = other; - } - - template< typename OtherRepresentation > - bool operator==( const boundary_matrix< OtherRepresentation >& other_boundary_matrix ) const { - const index number_of_columns = this->get_num_cols(); - - if( number_of_columns != other_boundary_matrix.get_num_cols() ) - return false; - - column temp_col; - column other_temp_col; - for( index idx = 0; idx < number_of_columns; idx++ ) { - this->get_col( idx, temp_col ); - other_boundary_matrix.get_col( idx, other_temp_col ); - if( temp_col != other_temp_col || this->get_dim( idx ) != other_boundary_matrix.get_dim( idx ) ) - return false; - } - return true; - } - - template< typename OtherRepresentation > - bool operator!=( const boundary_matrix< OtherRepresentation >& other_boundary_matrix ) const { - return !( *this == other_boundary_matrix ); - } - - template< typename OtherRepresentation > - boundary_matrix< Representation >& operator=( const boundary_matrix< OtherRepresentation >& other ) - { - const index nr_of_columns = other.get_num_cols(); - this->set_num_cols( nr_of_columns ); - column temp_col; - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { - this->set_dim( cur_col, other.get_dim( cur_col ) ); - other.get_col( cur_col, temp_col ); - this->set_col( cur_col, temp_col ); - } - - // by convention, always return *this - return *this; - } - - // I/O -- independent of chosen 'Representation' - public: - - // initializes boundary_matrix from (vector, vector) pair -- untested - template< typename index_type, typename dimemsion_type > - void load_vector_vector( const std::vector< std::vector< index_type > >& input_matrix, const std::vector< dimemsion_type >& input_dims ) { - const index nr_of_columns = (index)input_matrix.size(); - this->set_num_cols( nr_of_columns ); - column temp_col; - #pragma omp parallel for private( temp_col ) - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { - this->set_dim( cur_col, (dimension)input_dims[ cur_col ] ); - - index num_rows = input_matrix[ cur_col ].size(); - temp_col.resize( num_rows ); - for( index cur_row = 0; cur_row < num_rows; cur_row++ ) - temp_col[ cur_row ] = (index)input_matrix[ cur_col ][ cur_row ]; - this->set_col( cur_col, temp_col ); - } - } - - template< typename index_type, typename dimemsion_type > - void save_vector_vector( std::vector< std::vector< index_type > >& output_matrix, std::vector< dimemsion_type >& output_dims ) { - const index nr_of_columns = get_num_cols(); - output_matrix.resize( nr_of_columns ); - output_dims.resize( nr_of_columns ); - column temp_col; - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { - output_dims[ cur_col ] = (dimemsion_type)get_dim( cur_col ); - get_col( cur_col, temp_col ); - index num_rows = temp_col.size(); - output_matrix[ cur_col ].clear(); - output_matrix[ cur_col ].resize( num_rows ); - for( index cur_row = 0; cur_row < num_rows; cur_row++ ) - output_matrix[ cur_col ][ cur_row ] = (index_type)temp_col[ cur_row ]; - } - } - - - // Loads the boundary_matrix from given file in ascii format - // Format: each line represents a column, first number is dimension, other numbers are the content of the column. - // Ignores empty lines and lines starting with a '#'. - bool load_ascii( std::string filename ) { - // first count number of columns: - std::string cur_line; - std::ifstream dummy( filename .c_str() ); - if( dummy.fail() ) - return false; - - index number_of_columns = 0; - while( getline( dummy, cur_line ) ) { - cur_line.erase(cur_line.find_last_not_of(" \t\n\r\f\v") + 1); - if( cur_line != "" && cur_line[ 0 ] != '#' ) - number_of_columns++; - - } - this->set_num_cols( number_of_columns ); - dummy.close(); - - std::ifstream input_stream( filename.c_str() ); - if( input_stream.fail() ) - return false; - - column temp_col; - index cur_col = -1; - while( getline( input_stream, cur_line ) ) { - cur_line.erase(cur_line.find_last_not_of(" \t\n\r\f\v") + 1); - if( cur_line != "" && cur_line[ 0 ] != '#' ) { - cur_col++; - std::stringstream ss( cur_line ); - - int64_t temp_dim; - ss >> temp_dim; - this->set_dim( cur_col, (dimension) temp_dim ); - - int64_t temp_index; - temp_col.clear(); - while( ss.good() ) { - ss >> temp_index; - temp_col.push_back( (index)temp_index ); - } - std::sort( temp_col.begin(), temp_col.end() ); - this->set_col( cur_col, temp_col ); - } - } - - input_stream.close(); - return true; - } - - // Saves the boundary_matrix to given file in ascii format - // Format: each line represents a column, first number is dimension, other numbers are the content of the column - bool save_ascii( std::string filename ) { - std::ofstream output_stream( filename.c_str() ); - if( output_stream.fail() ) - return false; - - const index nr_columns = this->get_num_cols(); - column tempCol; - for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { - output_stream << (int64_t)this->get_dim( cur_col ); - this->get_col( cur_col, tempCol ); - for( index cur_row_idx = 0; cur_row_idx < (index)tempCol.size(); cur_row_idx++ ) - output_stream << " " << tempCol[ cur_row_idx ]; - output_stream << std::endl; - } - - output_stream.close(); - return true; - } - - // Loads boundary_matrix from given file - // Format: nr_columns % dim1 % N1 % row1 row2 % ...% rowN1 % dim2 % N2 % ... - bool load_binary( std::string filename ) - { - std::ifstream input_stream( filename.c_str( ), std::ios_base::binary | std::ios_base::in ); - if( input_stream.fail( ) ) - return false; - - int64_t nr_columns; - input_stream.read( (char*)&nr_columns, sizeof( int64_t ) ); - this->set_num_cols( (index)nr_columns ); - - column temp_col; - for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { - int64_t cur_dim; - input_stream.read( (char*)&cur_dim, sizeof( int64_t ) ); - this->set_dim( cur_col, (dimension)cur_dim ); - int64_t nr_rows; - input_stream.read( (char*)&nr_rows, sizeof( int64_t ) ); - temp_col.resize( ( std::size_t )nr_rows ); - for( index idx = 0; idx < nr_rows; idx++ ) { - int64_t cur_row; - input_stream.read( (char*)&cur_row, sizeof( int64_t ) ); - temp_col[ idx ] = (index)cur_row; - } - this->set_col( cur_col, temp_col ); - } - - input_stream.close( ); - return true; - } - - // Saves the boundary_matrix to given file in binary format - // Format: nr_columns % dim1 % N1 % row1 row2 % ...% rowN1 % dim2 % N2 % ... - bool save_binary( std::string filename ) - { - std::ofstream output_stream( filename.c_str( ), std::ios_base::binary | std::ios_base::out ); - if( output_stream.fail( ) ) - return false; - - const int64_t nr_columns = this->get_num_cols( ); - output_stream.write( (char*)&nr_columns, sizeof( int64_t ) ); - column tempCol; - for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { - int64_t cur_dim = this->get_dim( cur_col ); - output_stream.write( (char*)&cur_dim, sizeof( int64_t ) ); - this->get_col( cur_col, tempCol ); - int64_t cur_nr_rows = tempCol.size( ); - output_stream.write( (char*)&cur_nr_rows, sizeof( int64_t ) ); - for( index cur_row_idx = 0; cur_row_idx < (index)tempCol.size( ); cur_row_idx++ ) { - int64_t cur_row = tempCol[ cur_row_idx ]; - output_stream.write( (char*)&cur_row, sizeof( int64_t ) ); - } - } - - output_stream.close( ); - return true; - } - }; -} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/compute_persistence_pairs.h b/src/Zigzag_persistence/example/ext_zz/phat/compute_persistence_pairs.h deleted file mode 100644 index 48be65c28e..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/phat/compute_persistence_pairs.h +++ /dev/null @@ -1,128 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include -#include -#include - -namespace phat { - // Extracts persistence pairs in separate dimensions from a reduced - // boundary matrix representing ``double`` filtration. The pairs - // give persistent relative homology of the pair of filtrations. - // TODO: Use it with standard reduction algorithm (no template option). - template< typename ReductionAlgorithm, typename Representation > - void compute_relative_persistence_pairs(std::vector& pairs, boundary_matrix& boundary_matrix, const std::map& L) { - ReductionAlgorithm reduce; - reduce(boundary_matrix); - std::map free; - std::map invL; - for (std::map::const_iterator it = L.begin(); it != L.end(); ++it) { invL[it->second] = it->first; } - for (std::vector::iterator it = pairs.begin(); it != pairs.end(); ++it) { it->clear(); } - for (index idx = 0; idx < boundary_matrix.get_num_cols(); ++idx) { - int dimension = boundary_matrix.get_dim(idx); - if (L.find(idx) != L.end()) { ++dimension; } - free[idx] = true; - if (!boundary_matrix.is_empty(idx)) { - index birth = boundary_matrix.get_max_index(idx); - index death = idx; - pairs[dimension-1].append_pair(birth, death); - free[birth] = false; - free[death] = false; - } else { - // This is an L-simplex and a (dimension+1)-dimensional cycle - if (L.find(idx) != L.end()) { - assert(dimension < pairs.size()); - pairs[dimension].append_pair(idx, -1); - } - } - } - for (std::map::iterator it = free.begin(); it != free.end(); ++it) { - if (it->second) { - int dimension = boundary_matrix.get_dim(it->first); - if (invL.find(it->first) == invL.end() && L.find(it->first) == L.end()) { - assert(dimension < pairs.size()); - pairs[dimension].append_pair(it->first, -1); - } - } - } - } - - // Extracts persistence pairs in separate dimensions; expects a d-dimensional vector of persistent_pairs - template< typename ReductionAlgorithm, typename Representation > - void compute_persistence_pairs(std::vector& pairs, boundary_matrix& boundary_matrix) { - ReductionAlgorithm reduce; - reduce(boundary_matrix); - std::map free; - for (std::vector::iterator it = pairs.begin(); it != pairs.end(); ++it) { it->clear(); } - for (index idx = 0; idx < boundary_matrix.get_num_cols(); ++idx) { - int dimension = boundary_matrix.get_dim(idx); - free[idx] = true; - if (!boundary_matrix.is_empty(idx)) { - index birth = boundary_matrix.get_max_index(idx); - index death = idx; - pairs[dimension-1].append_pair(birth, death); - // Cannot be of the form (a, infinity) - free[birth] = false; - free[death] = false; - } - } - for (std::map::iterator it = free.begin(); it != free.end(); ++it) { - if (it->second) { - int dimension = boundary_matrix.get_dim(it->first); - pairs[dimension].append_pair(it->first, -1); - } - } - } - - template< typename ReductionAlgorithm, typename Representation > - void compute_persistence_pairs( persistence_pairs& pairs, boundary_matrix< Representation >& boundary_matrix ) { - ReductionAlgorithm reduce; - reduce( boundary_matrix ); - pairs.clear(); - for( index idx = 0; idx < boundary_matrix.get_num_cols(); idx++ ) { - if( !boundary_matrix.is_empty( idx ) ) { - index birth = boundary_matrix.get_max_index( idx ); - index death = idx; - pairs.append_pair( birth, death ); - } - } - } - - template< typename ReductionAlgorithm, typename Representation > - void compute_persistence_pairs_dualized( persistence_pairs& pairs, boundary_matrix< Representation >& boundary_matrix ) { - - dualize( boundary_matrix ); - compute_persistence_pairs< ReductionAlgorithm >( pairs, boundary_matrix ); - dualize_persistence_pairs( pairs, boundary_matrix.get_num_cols() ); - } - - template< typename Representation > - void compute_persistence_pairs( persistence_pairs& pairs, boundary_matrix< Representation >& boundary_matrix ) { - phat::compute_persistence_pairs< twist_reduction >( pairs, boundary_matrix ); - } - - - template< typename Representation > - void compute_persistence_pairs_dualized( persistence_pairs& pairs, boundary_matrix< Representation >& boundary_matrix ) { - compute_persistence_pairs_dualized< twist_reduction >( pairs, boundary_matrix ); - } - -} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/helpers/dualize.h b/src/Zigzag_persistence/example/ext_zz/phat/helpers/dualize.h deleted file mode 100644 index 3ffedf875f..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/phat/helpers/dualize.h +++ /dev/null @@ -1,74 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include -#include - - -namespace phat { - template< typename Representation > - void dualize( boundary_matrix< Representation >& boundary_matrix ) { - - std::vector< dimension > dual_dims; - std::vector< std::vector< index > > dual_matrix; - - index nr_of_columns = boundary_matrix.get_num_cols(); - dual_matrix.resize( nr_of_columns ); - dual_dims.resize( nr_of_columns ); - - std::vector< index > dual_sizes( nr_of_columns, 0 ); - - column temp_col; - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { - boundary_matrix.get_col( cur_col, temp_col ); - for( index idx = 0; idx < (index)temp_col.size(); idx++) - dual_sizes[ nr_of_columns - 1 - temp_col[ idx ] ]++; - } - - #pragma omp parallel for - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) - dual_matrix[cur_col].reserve(dual_sizes[cur_col]); - - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { - boundary_matrix.get_col( cur_col, temp_col ); - for( index idx = 0; idx < (index)temp_col.size(); idx++) - dual_matrix[ nr_of_columns - 1 - temp_col[ idx ] ].push_back( nr_of_columns - 1 - cur_col ); - } - - const dimension max_dim = boundary_matrix.get_max_dim(); - #pragma omp parallel for - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) - dual_dims[ nr_of_columns - 1 - cur_col ] = max_dim - boundary_matrix.get_dim( cur_col ); - - #pragma omp parallel for - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) - std::reverse( dual_matrix[ cur_col ].begin(), dual_matrix[ cur_col ].end() ); - - boundary_matrix.load_vector_vector( dual_matrix, dual_dims ); - } - - void dualize_persistence_pairs( persistence_pairs& pairs, const index n ) { - for (index i = 0; i < pairs.get_num_pairs(); ++i) { - std::pair< index, index > pair = pairs.get_pair( i ); - pairs.set_pair( i , n - 1 - pair.second, n - 1 - pair.first); - } - } -} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/helpers/misc.h b/src/Zigzag_persistence/example/ext_zz/phat/helpers/misc.h deleted file mode 100644 index fb5c07acb0..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/phat/helpers/misc.h +++ /dev/null @@ -1,75 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -// STL includes -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// VS2008 and below unfortunately do not support stdint.h -#if defined(_MSC_VER)&& _MSC_VER < 1600 - typedef __int8 int8_t; - typedef unsigned __int8 uint8_t; - typedef __int16 int16_t; - typedef unsigned __int16 uint16_t; - typedef __int32 int32_t; - typedef unsigned __int32 uint32_t; - typedef __int64 int64_t; - typedef unsigned __int64 uint64_t; -#else - #include -#endif - -// basic types. index can be changed to int32_t to save memory on small instances -namespace phat { - typedef int64_t index; - typedef int8_t dimension; - typedef std::vector< index > column; -} - -// OpenMP (proxy) functions -#if defined _OPENMP - #include -#else - #define omp_get_thread_num() 0 - #define omp_get_max_threads() 1 - #define omp_get_num_threads() 1 - void omp_set_num_threads( int ) {}; - #include - #define omp_get_wtime() (float)clock() / (float)CLOCKS_PER_SEC -#endif - -#include - - - diff --git a/src/Zigzag_persistence/example/ext_zz/phat/helpers/thread_local_storage.h b/src/Zigzag_persistence/example/ext_zz/phat/helpers/thread_local_storage.h deleted file mode 100644 index d0b5332bc1..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/phat/helpers/thread_local_storage.h +++ /dev/null @@ -1,52 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include - -// should ideally be equal to the cache line size of the CPU -#define PHAT_TLS_SPACING_FACTOR 64 - -// ThreadLocalStorage with some spacing to avoid "false sharing" (see wikipedia) -template< typename T > -class thread_local_storage -{ -public: - - thread_local_storage() : per_thread_storage( omp_get_max_threads() * PHAT_TLS_SPACING_FACTOR ) {}; - - T& operator()() { - return per_thread_storage[ omp_get_thread_num() * PHAT_TLS_SPACING_FACTOR ]; - } - - const T& operator()() const { - return per_thread_storage[ omp_get_thread_num() * PHAT_TLS_SPACING_FACTOR ]; - } - - T& operator[]( int tid ) { - return per_thread_storage[ tid * PHAT_TLS_SPACING_FACTOR ]; - } - - const T& operator[]( int tid ) const { - return per_thread_storage[ tid * PHAT_TLS_SPACING_FACTOR ]; - } - -protected: - std::vector< T > per_thread_storage; -}; diff --git a/src/Zigzag_persistence/example/ext_zz/phat/persistence_pairs.h b/src/Zigzag_persistence/example/ext_zz/phat/persistence_pairs.h deleted file mode 100644 index eafc6389e2..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/phat/persistence_pairs.h +++ /dev/null @@ -1,155 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include - -namespace phat { - class persistence_pairs { - - protected: - std::vector< std::pair< index, index > > pairs; - - public: - index get_num_pairs() const { - return (index)pairs.size(); - } - - void append_pair( index birth, index death ) { - pairs.push_back( std::make_pair( birth, death ) ); - } - - std::pair< index, index > get_pair( index idx ) const { - return pairs[ idx ]; - } - - void set_pair( index idx, index birth, index death ) { - pairs[ idx ] = std::make_pair( birth, death ); - } - - void clear() { - pairs.clear(); - } - - void sort() { - std::sort( pairs.begin(), pairs.end() ); - } - - // Loads the persistence pairs from given file in asci format - // Format: nr_pairs % newline % birth1 % death1 % newline % birth2 % death2 % newline ... - bool load_ascii( std::string filename ) { - std::ifstream input_stream( filename.c_str() ); - if( input_stream.fail() ) - return false; - - int64_t nr_pairs; - input_stream >> nr_pairs; - pairs.clear(); - for( index idx = 0; idx < nr_pairs; idx++ ) { - int64_t birth; - input_stream >> birth; - int64_t death; - input_stream >> death; - append_pair( (index)birth, (index)death ); - } - - input_stream.close(); - return true; - } - - // Saves the persistence pairs to given file in binary format - // Format: nr_pairs % newline % birth1 % death1 % newline % birth2 % death2 % newline ... - bool save_ascii( std::string filename ) { - std::ofstream output_stream( filename.c_str() ); - if( output_stream.fail() ) - return false; - - this->sort(); - output_stream << get_num_pairs() << std::endl; - for( std::size_t idx = 0; idx < pairs.size(); idx++ ) { - output_stream << pairs[idx].first << " " << pairs[idx].second << std::endl; - } - - output_stream.close(); - return true; - } - - // Loads the persistence pairs from given file in binary format - // Format: nr_pairs % birth1 % death1 % birth2 % death2 ... - bool load_binary( std::string filename ) { - std::ifstream input_stream( filename.c_str(), std::ios_base::binary | std::ios_base::in ); - if( input_stream.fail() ) - return false; - - int64_t nr_pairs; - input_stream.read( (char*)&nr_pairs, sizeof( int64_t ) ); - for( index idx = 0; idx < nr_pairs; idx++ ) { - int64_t birth; - input_stream.read( (char*)&birth, sizeof( int64_t ) ); - int64_t death; - input_stream.read( (char*)&death, sizeof( int64_t ) ); - append_pair( (index)birth, (index)death ); - } - - input_stream.close(); - return true; - } - - // Saves the persistence pairs to given file in binary format - // Format: nr_pairs % birth1 % death1 % birth2 % death2 ... - bool save_binary( std::string filename ) { - std::ofstream output_stream( filename.c_str(), std::ios_base::binary | std::ios_base::out ); - if( output_stream.fail() ) - return false; - - this->sort(); - int64_t nr_pairs = get_num_pairs(); - output_stream.write( (char*)&nr_pairs, sizeof( int64_t ) ); - for( std::size_t idx = 0; idx < pairs.size(); idx++ ) { - int64_t birth = pairs[ idx ].first; - output_stream.write( (char*)&birth, sizeof( int64_t ) ); - int64_t death = pairs[ idx ].second; - output_stream.write( (char*)&death, sizeof( int64_t ) ); - } - - output_stream.close(); - return true; - } - - bool operator==( persistence_pairs& other_pairs ) { - this->sort(); - other_pairs.sort(); - if( pairs.size() != (std::size_t)other_pairs.get_num_pairs() ) - return false; - - for( index idx = 0; idx < (index)pairs.size(); idx++ ) - if( get_pair( idx ) != other_pairs.get_pair( idx ) ) - return false; - - return true; - } - - bool operator!=( persistence_pairs& other_pairs ) { - return !( *this == other_pairs ); - } - }; - - - -} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/representations/abstract_pivot_column.h b/src/Zigzag_persistence/example/ext_zz/phat/representations/abstract_pivot_column.h deleted file mode 100644 index e16d7a5d13..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/phat/representations/abstract_pivot_column.h +++ /dev/null @@ -1,102 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - - // Note: We could even make the rep generic in the underlying Const representation - // But I cannot imagine that anything else than vector> would - // make sense - template< typename PivotColumn > - class abstract_pivot_column : public vector_vector { - - protected: - typedef vector_vector Base; - typedef PivotColumn pivot_col; - - // For parallization purposes, it could be more than one full column - mutable thread_local_storage< pivot_col > pivot_cols; - mutable thread_local_storage< index > idx_of_pivot_cols; - - pivot_col& get_pivot_col() const { - return pivot_cols(); - } - - bool is_pivot_col( index idx ) const { - return idx_of_pivot_cols() == idx; - } - - void release_pivot_col() { - index idx = idx_of_pivot_cols(); - if( idx != -1 ) { - this->matrix[ idx ].clear(); - pivot_cols().get_col_and_clear( this->matrix[ idx ] ); - } - idx_of_pivot_cols() = -1; - } - - void make_pivot_col( index idx ) { - release_pivot_col(); - idx_of_pivot_cols() = idx; - get_pivot_col().add_col( matrix[ idx ] ); - } - - public: - - void _set_num_cols( index nr_of_cols ) { - #pragma omp parallel for - for( int tid = 0; tid < omp_get_num_threads(); tid++ ) { - pivot_cols[ tid ].init( nr_of_cols ); - idx_of_pivot_cols[ tid ] = -1; - } - Base::_set_num_cols( nr_of_cols ); - } - - void _add_to( index source, index target ) { - if( !is_pivot_col( target ) ) - make_pivot_col( target ); - get_pivot_col().add_col( matrix[source] ); - } - - void _sync() { - #pragma omp parallel for - for( int tid = 0; tid < omp_get_num_threads(); tid++ ) - release_pivot_col(); - } - - void _get_col( index idx, column& col ) const { is_pivot_col( idx ) ? get_pivot_col().get_col( col ) : Base::_get_col( idx, col ); } - - bool _is_empty( index idx ) const { return is_pivot_col( idx ) ? get_pivot_col().is_empty() : Base::_is_empty( idx ); } - - index _get_max_index( index idx ) const { return is_pivot_col( idx ) ? get_pivot_col().get_max_index() : Base::_get_max_index( idx ); } - - void _clear( index idx ) { is_pivot_col( idx ) ? get_pivot_col().clear() : Base::_clear( idx ); } - - void _set_col( index idx, const column& col ) { is_pivot_col( idx ) ? get_pivot_col().set_col( col ) : Base::_set_col( idx, col ); } - - void _remove_max( index idx ) { is_pivot_col( idx ) ? get_pivot_col().remove_max() : Base::_remove_max( idx ); } - - void finalize( index idx ) { Base::_finalize( idx ); } - }; -} - - diff --git a/src/Zigzag_persistence/example/ext_zz/phat/representations/bit_tree_pivot_column.h b/src/Zigzag_persistence/example/ext_zz/phat/representations/bit_tree_pivot_column.h deleted file mode 100644 index 4d48e8853d..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/phat/representations/bit_tree_pivot_column.h +++ /dev/null @@ -1,165 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Hubert Wagner - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - - // This is a bitset indexed with a 64-ary tree. Each node in the index - // has 64 bits; i-th bit says that the i-th subtree is non-empty. - // Supports practically O(1), inplace, zero-allocation: insert, remove, max_element - // and clear in O(number of ones in the bitset). - // 'add_index' is still the real bottleneck in practice. - class bit_tree_column - { - protected: - - size_t offset; // data[i + offset] = ith block of the data-bitset - typedef uint64_t block_type; - std::vector< block_type > data; - - - size_t debrujin_magic_table[ 64 ]; - - enum { block_size_in_bits = 64 }; - enum { block_shift = 6 }; - - // Some magic: http://graphics.stanford.edu/~seander/bithacks.html - // Gets the position of the rightmost bit of 'x'. 0 means the most significant bit. - // (-x)&x isolates the rightmost bit. - // The whole method is much faster than calling log2i, and very comparable to using ScanBitForward/Reverse intrinsic, - // which should be one CPU instruction, but is not portable. - size_t rightmost_pos( const block_type value ) const { - return 64 - 1 - debrujin_magic_table[ ( (value & (-(int64_t)value) ) * 0x07EDD5E59A4E28C2 ) >> 58 ]; - } - - public: - - void init( index num_cols ) { - int64_t n = 1; // in case of overflow - int64_t bottom_blocks_needed = ( num_cols + block_size_in_bits - 1 ) / block_size_in_bits; - int64_t upper_blocks = 1; - - // How many blocks/nodes of index needed to index the whole bitset? - while( n * block_size_in_bits < bottom_blocks_needed ) { - n *= block_size_in_bits; - upper_blocks += n; - } - - offset = upper_blocks; - data.resize( upper_blocks + bottom_blocks_needed, 0 ); - - std::size_t temp_array[ 64 ] = { - 63, 0, 58, 1, 59, 47, 53, 2, - 60, 39, 48, 27, 54, 33, 42, 3, - 61, 51, 37, 40, 49, 18, 28, 20, - 55, 30, 34, 11, 43, 14, 22, 4, - 62, 57, 46, 52, 38, 26, 32, 41, - 50, 36, 17, 19, 29, 10, 13, 21, - 56, 45, 25, 31, 35, 16, 9, 12, - 44, 24, 15, 8, 23, 7, 6, 5 }; - - std::copy( &temp_array[ 0 ], &temp_array[ 64 ], &debrujin_magic_table[ 0 ] ); - } - - index get_max_index() const { - if( !data[ 0 ] ) - return -1; - - size_t n = 0; - size_t newn = 0; - size_t index = 0; - while( newn < data.size() ) { - n = newn; - index = rightmost_pos( data[ n ] ); - newn = ( n << block_shift ) + index + 1; - } - - return ( ( n - offset ) << block_shift ) + index; - } - - bool is_empty() const { - return data[ 0 ] == 0; - } - - void add_index( const size_t entry ) { - const block_type ONE = 1; - const block_type block_modulo_mask = ( ONE << block_shift ) - 1; - size_t index_in_level = entry >> block_shift; - size_t address = index_in_level + offset; - size_t index_in_block = entry & block_modulo_mask; - - block_type mask = ( ONE << ( block_size_in_bits - index_in_block - 1 ) ); - - data[ address ] ^= mask; - - // Check if we reached the root. Also, if anyone else was in this block, we don't need to update the path up. - while( address && !( data[ address ] & ~mask ) ) { - index_in_block = index_in_level & block_modulo_mask; - index_in_level >>= block_shift; - --address; - address >>= block_shift; - mask = ( ONE << ( block_size_in_bits - index_in_block - 1 ) ); - data[ address ] ^= mask; - } - } - - void get_col_and_clear( column &out ) { - index mx = this->get_max_index(); - while( mx != -1 ) { - out.push_back( mx ); - add_index( mx ); - mx = this->get_max_index(); - } - - std::reverse( out.begin(), out.end() ); - } - - void add_col(const column &col) { - for( size_t i = 0; i < col.size(); ++i ) - add_index(col[i]); - } - - void clear() { - index mx = this->get_max_index(); - while( mx != -1 ) { - add_index( mx ); - mx = this->get_max_index(); - } - } - - void remove_max() { - add_index( get_max_index() ); - } - - void set_col( const column& col ) { - clear(); - add_col( col ); - } - - void get_col( column& col ) { - get_col_and_clear( col ); - add_col( col ); - } - }; - - typedef abstract_pivot_column bit_tree_pivot_column; -} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/representations/full_pivot_column.h b/src/Zigzag_persistence/example/ext_zz/phat/representations/full_pivot_column.h deleted file mode 100644 index c2e9e3c574..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/phat/representations/full_pivot_column.h +++ /dev/null @@ -1,100 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - class full_column { - - protected: - std::priority_queue< index > history; - std::vector< char > is_in_history; - std::vector< char > col_bit_field; - - public: - void init( const index total_size ) { - col_bit_field.resize( total_size, false ); - is_in_history.resize( total_size, false ); - } - - void add_col( const column& col ) { - for( index idx = 0; idx < (index) col.size(); idx++ ) { - add_index( col[ idx ] ); - } - } - - void add_index( const index idx ) { - if( !is_in_history[ idx ] ) { - history.push( idx ); - is_in_history[ idx ] = true; - } - - col_bit_field[ idx ] = !col_bit_field[ idx ]; - } - - index get_max_index() { - while( history.size() > 0 ) { - index topIndex = history.top(); - if( col_bit_field[ topIndex ] ) { - return topIndex; - } else { - history.pop(); - is_in_history[ topIndex ] = false; - } - } - - return -1; - } - - void get_col_and_clear( column& col ) { - while( !is_empty() ) { - col.push_back( get_max_index() ); - add_index( get_max_index() ); - } - std::reverse( col.begin(), col.end() ); - } - - bool is_empty() { - return (get_max_index() == -1); - } - - void clear() { - while( !is_empty() ) - add_index( get_max_index() ); - } - - void remove_max() { - add_index( get_max_index() ); - } - - void set_col( const column& col ) { - clear(); - add_col( col ); - } - - void get_col( column& col ) { - get_col_and_clear( col ); - add_col( col ); - } - }; - - typedef abstract_pivot_column< full_column > full_pivot_column; -} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/representations/heap_pivot_column.h b/src/Zigzag_persistence/example/ext_zz/phat/representations/heap_pivot_column.h deleted file mode 100644 index 33cd07b40d..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/phat/representations/heap_pivot_column.h +++ /dev/null @@ -1,126 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - class heap_column { - - protected: - std::priority_queue< index > data; - - column temp_col; - index inserts_since_last_prune; - - void prune() - { - temp_col.clear( ); - index max_index = pop_max_index( ); - while( max_index != -1 ) { - temp_col.push_back( max_index ); - max_index = pop_max_index( ); - } - - for( index idx = 0; idx < (index)temp_col.size( ); idx++ ) - data.push( temp_col[ idx ] ); - - inserts_since_last_prune = 0; - } - - index pop_max_index() - { - if( data.empty( ) ) - return -1; - else { - index max_element = data.top( ); - data.pop(); - while( !data.empty( ) && data.top( ) == max_element ) { - data.pop( ); - if( data.empty( ) ) - return -1; - else { - max_element = data.top( ); - data.pop( ); - } - } - return max_element; - } - } - - public: - void init( const index total_size ) { - inserts_since_last_prune = 0; - clear(); - } - - void add_col( const column& col ) { - for( index idx = 0; idx < (index) col.size(); idx++ ) - data.push( col[ idx ] ); - inserts_since_last_prune += col.size( ); - if( 2 * inserts_since_last_prune >( index ) data.size( ) ) - prune(); - } - - index get_max_index() { - index max_element = pop_max_index( ); - if( max_element == -1 ) - return -1; - else { - data.push( max_element ); - return max_element; - } - } - - void get_col_and_clear( column& col ) { - col.clear(); - index max_index = pop_max_index( ); - while( max_index != -1 ) { - col.push_back( max_index ); - max_index = pop_max_index( ); - } - std::reverse( col.begin(), col.end() ); - } - - bool is_empty() { - return get_max_index() == -1; - } - - void clear() { - data = std::priority_queue< index >(); - } - - void remove_max() { - pop_max_index(); - } - - void set_col( const column& col ) { - clear(); - add_col( col ); - } - - void get_col( column& col ) { - get_col_and_clear( col ); - add_col( col ); - } - }; - - typedef abstract_pivot_column< heap_column > heap_pivot_column; -} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/representations/sparse_pivot_column.h b/src/Zigzag_persistence/example/ext_zz/phat/representations/sparse_pivot_column.h deleted file mode 100644 index 390fd91a99..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/phat/representations/sparse_pivot_column.h +++ /dev/null @@ -1,79 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - class sparse_column { - - protected: - std::set< index > data; - - void add_index( const index idx ) { - std::pair< std::set< index >::iterator, bool > result = data.insert( idx ); - if( result.second == false ) - data.erase( result.first ); - } - - public: - void init( const index total_size ) { - data.clear(); - } - - void add_col( const column& col ) { - for( index idx = 0; idx < (index) col.size(); idx++ ) - add_index( col[ idx ] ); - } - - index get_max_index() { - return data.empty() ? -1 : *data.rbegin(); - } - - void get_col_and_clear( column& col ) { - col.assign( data.begin(), data.end() ); - data.clear(); - } - - bool is_empty() { - return data.empty(); - } - - void clear() { - data.clear(); - } - - void remove_max() { - add_index( get_max_index() ); - } - - void set_col( const column& col ) { - clear(); - add_col( col ); - } - - void get_col( column& col ) { - get_col_and_clear( col ); - add_col( col ); - } - }; - - typedef abstract_pivot_column< sparse_column > sparse_pivot_column; -} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/representations/vector_heap.h b/src/Zigzag_persistence/example/ext_zz/phat/representations/vector_heap.h deleted file mode 100644 index db0420ff23..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/phat/representations/vector_heap.h +++ /dev/null @@ -1,170 +0,0 @@ -/* Copyright 2013 IST Austria -Contributed by: Jan Reininghaus - -This file is part of PHAT. - -PHAT is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -PHAT 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 Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with PHAT. If not, see . */ - -#pragma once - -#include - -namespace phat { - class vector_heap { - - protected: - std::vector< dimension > dims; - std::vector< column > matrix; - - std::vector< index > inserts_since_last_prune; - - mutable thread_local_storage< column > temp_column_buffer; - - protected: - void _prune( index idx ) - { - column& col = matrix[ idx ]; - column& temp_col = temp_column_buffer(); - temp_col.clear(); - index max_index = _pop_max_index( col ); - while( max_index != -1 ) { - temp_col.push_back( max_index ); - max_index = _pop_max_index( col ); - } - col = temp_col; - std::reverse( col.begin( ), col.end( ) ); - std::make_heap( col.begin( ), col.end( ) ); - inserts_since_last_prune[ idx ] = 0; - } - - index _pop_max_index( index idx ) - { - return _pop_max_index( matrix[ idx ] ); - } - - index _pop_max_index( column& col ) const - { - if( col.empty( ) ) - return -1; - else { - index max_element = col.front( ); - std::pop_heap( col.begin( ), col.end( ) ); - col.pop_back( ); - while( !col.empty( ) && col.front( ) == max_element ) { - std::pop_heap( col.begin( ), col.end( ) ); - col.pop_back( ); - if( col.empty( ) ) - return -1; - else { - max_element = col.front( ); - std::pop_heap( col.begin( ), col.end( ) ); - col.pop_back( ); - } - } - return max_element; - } - } - - public: - // overall number of cells in boundary_matrix - index _get_num_cols( ) const - { - return (index)matrix.size( ); - } - void _set_num_cols( index nr_of_columns ) - { - dims.resize( nr_of_columns ); - matrix.resize( nr_of_columns ); - inserts_since_last_prune.assign( nr_of_columns, 0 ); - } - - // dimension of given index - dimension _get_dim( index idx ) const - { - return dims[ idx ]; - } - void _set_dim( index idx, dimension dim ) - { - dims[ idx ] = dim; - } - - // replaces(!) content of 'col' with boundary of given index - void _get_col( index idx, column& col ) const - { - temp_column_buffer( ) = matrix[ idx ]; - - index max_index = _pop_max_index( temp_column_buffer() ); - while( max_index != -1 ) { - col.push_back( max_index ); - max_index = _pop_max_index( temp_column_buffer( ) ); - } - std::reverse( col.begin( ), col.end( ) ); - } - void _set_col( index idx, const column& col ) - { - matrix[ idx ] = col; - std::make_heap( matrix[ idx ].begin( ), matrix[ idx ].end( ) ); - } - - // true iff boundary of given idx is empty - bool _is_empty( index idx ) const - { - return _get_max_index( idx ) == -1; - } - - // largest row index of given column idx (new name for lowestOne()) - index _get_max_index( index idx ) const - { - column& col = const_cast< column& >( matrix[ idx ] ); - index max_element = _pop_max_index( col ); - col.push_back( max_element ); - std::push_heap( col.begin( ), col.end( ) ); - return max_element; - } - - // removes the maximal index of a column - void _remove_max( index idx ) - { - _pop_max_index( idx ); - } - - // clears given column - void _clear( index idx ) - { - matrix[ idx ].clear( ); - } - - // syncronizes all data structures (essential for openmp stuff) - void _sync( ) {} - - // adds column 'source' to column 'target' - void _add_to( index source, index target ) - { - for( index idx = 0; idx < (index)matrix[ source ].size( ); idx++ ) { - matrix[ target ].push_back( matrix[ source ][ idx ] ); - std::push_heap( matrix[ target ].begin(), matrix[ target ].end() ); - } - inserts_since_last_prune[ target ] += matrix[ source ].size(); - - if( 2 * inserts_since_last_prune[ target ] > ( index )matrix[ target ].size() ) - _prune( target ); - } - - // finalizes given column - void _finalize( index idx ) { - _prune( idx ); - } - - }; -} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/representations/vector_list.h b/src/Zigzag_persistence/example/ext_zz/phat/representations/vector_list.h deleted file mode 100644 index ca0b5b8e79..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/phat/representations/vector_list.h +++ /dev/null @@ -1,101 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include - -namespace phat { - class vector_list { - - protected: - std::vector< dimension > dims; - std::vector< std::list< index > > matrix; - - public: - // overall number of cells in boundary_matrix - index _get_num_cols() const { - return (index)matrix.size(); - } - void _set_num_cols( index nr_of_columns ) { - dims.resize( nr_of_columns ); - matrix.resize( nr_of_columns ); - } - - // dimension of given index - dimension _get_dim( index idx ) const { - return dims[ idx ]; - } - void _set_dim( index idx, dimension dim ) { - dims[ idx ] = dim; - } - - // replaces(!) content of 'col' with boundary of given index - void _get_col( index idx, column& col ) const { - col.clear(); - col.reserve( matrix[idx].size() ); - std::copy (matrix[idx].begin(), matrix[idx].end(), std::back_inserter(col) ); - } - - void _set_col( index idx, const column& col ) { - matrix[ idx ].clear(); - matrix[ idx ].resize( col.size() ); - std::copy (col.begin(), col.end(), matrix[ idx ].begin() ); - } - - // true iff boundary of given idx is empty - bool _is_empty( index idx ) const { - return matrix[ idx ].empty(); - } - - // largest row index of given column idx (new name for lowestOne()) - index _get_max_index( index idx ) const { - return matrix[ idx ].empty() ? -1 : *matrix[ idx ].rbegin(); - } - - // removes the maximal index of a column - void _remove_max( index idx ) { - std::list< index >::iterator it = matrix[ idx ].end(); - it--; - matrix[ idx ].erase( it ); - } - - // clears given column - void _clear( index idx ) { - matrix[ idx ].clear(); - } - - // syncronizes all data structures (essential for openmp stuff) - void _sync() {} - - // adds column 'source' to column 'target' - void _add_to( index source, index target ) { - std::list< index >& source_col = matrix[ source ]; - std::list< index >& target_col = matrix[ target ]; - std::list< index > temp_col; - target_col.swap( temp_col ); - std::set_symmetric_difference( temp_col.begin(), temp_col.end(), - source_col.begin(), source_col.end(), - std::back_inserter( target_col ) ); - } - - // finalizes given column - void _finalize( index idx ) { - } - }; -} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/representations/vector_set.h b/src/Zigzag_persistence/example/ext_zz/phat/representations/vector_set.h deleted file mode 100644 index 6878a270c0..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/phat/representations/vector_set.h +++ /dev/null @@ -1,99 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include - -namespace phat { - class vector_set { - - protected: - std::vector< dimension > dims; - std::vector< std::set< index > > matrix; - - public: - // overall number of cells in boundary_matrix - index _get_num_cols() const { - return (index)matrix.size(); - } - void _set_num_cols( index nr_of_columns ) { - dims.resize( nr_of_columns ); - matrix.resize( nr_of_columns ); - } - - // dimension of given index - dimension _get_dim( index idx ) const { - return dims[ idx ]; - } - void _set_dim( index idx, dimension dim ) { - dims[ idx ] = dim; - } - - // replaces(!) content of 'col' with boundary of given index - void _get_col( index idx, column& col ) const { - col.clear(); - col.reserve( matrix[idx].size() ); - std::copy (matrix[idx].begin(), matrix[idx].end(), std::back_inserter(col) ); - } - void _set_col( index idx, const column& col ) { - matrix[ idx ].clear(); - matrix[ idx ].insert( col.begin(), col.end() ); - } - - // true iff boundary of given idx is empty - bool _is_empty( index idx ) const { - return matrix[ idx ].empty(); - } - - // largest row index of given column idx (new name for lowestOne()) - index _get_max_index( index idx ) const { - return matrix[ idx ].empty() ? -1 : *matrix[ idx ].rbegin(); - } - - // removes the maximal index of a column - void _remove_max( index idx ) { - std::set< index >::iterator it = matrix[ idx ].end(); - it--; - matrix[ idx ].erase( it ); - } - - // clears given column - void _clear( index idx ) { - matrix[ idx ].clear(); - } - - // syncronizes all data structures (essential for openmp stuff) - void _sync() {} - - // adds column 'source' to column 'target' - void _add_to( index source, index target ) { - for( std::set< index >::iterator it = matrix[ source ].begin(); it != matrix[ source ].end(); it++ ) { - std::set< index >& col = matrix[ target ]; - std::pair< std::set< index >::iterator, bool > result = col.insert( *it ); - if( !result.second ) - col.erase( result.first ); - } - } - - // finalizes given column - void _finalize( index idx ) { - } - - }; -} diff --git a/src/Zigzag_persistence/example/ext_zz/phat/representations/vector_vector.h b/src/Zigzag_persistence/example/ext_zz/phat/representations/vector_vector.h deleted file mode 100644 index f111d6b572..0000000000 --- a/src/Zigzag_persistence/example/ext_zz/phat/representations/vector_vector.h +++ /dev/null @@ -1,107 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include - -namespace phat { - class vector_vector { - - protected: - std::vector< dimension > dims; - std::vector< column > matrix; - - thread_local_storage< column > temp_column_buffer; - - public: - // overall number of cells in boundary_matrix - index _get_num_cols() const { - return (index)matrix.size(); - } - void _set_num_cols( index nr_of_columns ) { - dims.resize( nr_of_columns ); - matrix.resize( nr_of_columns ); - } - - // dimension of given index - dimension _get_dim( index idx ) const { - return dims[ idx ]; - } - void _set_dim( index idx, dimension dim ) { - dims[ idx ] = dim; - } - - // replaces(!) content of 'col' with boundary of given index - void _get_col( index idx, column& col ) const { - col = matrix[ idx ]; - } - void _set_col( index idx, const column& col ) { - matrix[ idx ] = col; - } - - // true iff boundary of given idx is empty - bool _is_empty( index idx ) const { - return matrix[ idx ].empty(); - } - - // largest row index of given column idx (new name for lowestOne()) - index _get_max_index( index idx ) const { - return matrix[ idx ].empty() ? -1 : matrix[ idx ].back(); - } - - // removes the maximal index of a column - void _remove_max( index idx ) { - matrix[ idx ].pop_back(); - } - - // clears given column - void _clear( index idx ) { - matrix[ idx ].clear(); - } - - // syncronizes all data structures (essential for openmp stuff) - void _sync() {} - - // adds column 'source' to column 'target' - void _add_to( index source, index target ) { - column& source_col = matrix[ source ]; - column& target_col = matrix[ target ]; - column& temp_col = temp_column_buffer(); - - - size_t new_size = source_col.size() + target_col.size(); - - if (new_size > temp_col.size()) temp_col.resize(new_size); - - std::vector::iterator col_end = std::set_symmetric_difference( target_col.begin(), target_col.end(), - source_col.begin(), source_col.end(), - temp_col.begin() ); - temp_col.erase(col_end, temp_col.end()); - - - target_col.swap(temp_col); - } - - // finalizes given column - void _finalize( index idx ) { - column& col = matrix[ idx ]; - column(col.begin(), col.end()).swap(col); - } - }; -} diff --git a/src/Zigzag_persistence/example/rips-zigzag-dionysus.h b/src/Zigzag_persistence/example/rips-zigzag-dionysus.h deleted file mode 100644 index 62db6d834d..0000000000 --- a/src/Zigzag_persistence/example/rips-zigzag-dionysus.h +++ /dev/null @@ -1,211 +0,0 @@ -#include -#include -#include -#include - -#include -namespace ba = boost::adaptors; - -#include -#include -#include -namespace d = dionysus; - -#include - -typedef std::vector Point; -typedef std::vector PointContainer; - -typedef d::PairwiseDistances> PairDistances; -typedef PairDistances::DistanceType DistanceType; -typedef PairDistances::IndexType Vertex; - -typedef d::Rips Generator; -typedef Generator::Simplex Simplex; -typedef std::set SimplexSet; - -typedef std::vector VertexVector; -typedef std::vector EpsilonVector; -typedef std::tuple Edge; -typedef std::vector EdgeVector; - -inline PointContainer compute_points(unsigned int numberOfPoints, int seed = -1) -{ - PointContainer finalPoints; - std::set points; - std::random_device dev; - std::mt19937 rng(dev()); - if (seed > -1) rng.seed(seed); - std::uniform_real_distribution dist(0,10); - - for (unsigned int i = 0; i < numberOfPoints; ++i){ - auto res = points.insert({dist(rng), dist(rng)}); - while(!res.second){ - res = points.insert({dist(rng), dist(rng)}); - } - finalPoints.push_back(*res.first); - } - - return finalPoints; -} - -inline void compute_vertices_and_epsilons(const PairDistances& distances, - VertexVector& vertices, - EpsilonVector& epsilons) -{ - DistanceType inf = std::numeric_limits::infinity(); - EpsilonVector dist(distances.size(), inf); - - vertices.push_back(distances.begin()); - // epsilons.push_back(inf); - while (vertices.size() < distances.size()) { - for (Vertex v = distances.begin(); v != distances.end(); ++v) - dist[v] = std::min(dist[v], distances(v, vertices.back())); - auto max = std::max_element(dist.begin(), dist.end()); - vertices.push_back(max - dist.begin()); - epsilons.push_back(*max); - } - epsilons.push_back(0); -} - -inline void compute_edges(const PairDistances& distances, - const VertexVector& vertices, - const EpsilonVector& epsilons, - const DistanceType& multiplier, - EdgeVector& edges) -{ - for (unsigned i = 0; i != vertices.size(); ++i) - for (unsigned j = i + 1; j != vertices.size(); ++j) { - Vertex u = vertices[i]; - Vertex v = vertices[j]; - if (distances(u, v) <= multiplier * epsilons[j - 1]) edges.emplace_back(u, v); - } - std::sort(edges.begin(), edges.end(), - [&distances](const Edge& e1, const Edge& e2) { - return distances(std::get<0>(e1), std::get<1>(e1)) < - distances(std::get<0>(e2), std::get<1>(e2)); - }); -} - -inline void compute_positive_cofaces( - const PairDistances& distances, - const VertexVector& vertices, - const EdgeVector& edges, - const EpsilonVector& epsilons, - const DistanceType& multiplier, - Generator& rips, - short unsigned& skeleton, - unsigned& ce, - unsigned& i, - SimplexSet& cofaces) -{ - cofaces.clear(); - - // Add anything else that needs to be inserted into the complex - while (ce < edges.size()) { - Vertex u, v; - std::tie(u, v) = edges[ce]; - if (distances(u, v) <= multiplier * epsilons[i - 1]) - ++ce; - else - break; - // std::cout << "Adding cofaces of " << u << ' ' << v << std::endl; - rips.edge_cofaces( - u, v, - skeleton, - multiplier * epsilons[i - 1], - [&cofaces](Simplex&& s) { cofaces.insert(s); }, - vertices.begin(), - vertices.begin() + i + 1); - } -} - -inline void compute_negative_cofaces( - const VertexVector& vertices, - const EpsilonVector& epsilons, - const DistanceType& multiplier, - Generator& rips, - short unsigned& skeleton, - unsigned& i, - SimplexSet& cofaces) -{ - cofaces.clear(); - rips.vertex_cofaces( - vertices[i], - skeleton, - multiplier * epsilons[i - 1], - [&cofaces](Simplex&& s) { cofaces.insert(s); }, - vertices.begin(), - vertices.begin() + i + 1); - // std::cout << "Total cofaces: " << cofaces.size() << std::endl; -} - -inline unsigned int build_rips_zigzag_filtration(std::vector > &simpls, - std::vector& dirs, - unsigned int numberOfPoints, - int seed = -1, - short unsigned skeleton = 2, - DistanceType multiplier = 6) -{ - // std::cout << "Building filtration" << std::endl; - unsigned int numberOfSimplices = 0; - - PointContainer points = compute_points(numberOfPoints, seed); - - // Construct distances and Rips generator - PairDistances distances(points); - Generator rips(distances); - - // Order vertices and epsilons (in maxmin fashion) - VertexVector vertices; - EpsilonVector epsilons; - EdgeVector edges; - - compute_vertices_and_epsilons(distances, vertices, epsilons); - - // Generate and sort all the edges - compute_edges(distances, vertices, epsilons, multiplier, edges); - - // Insert vertices - for (auto v : vertices) { - // Add a vertex - simpls.push_back({static_cast(v)}); - dirs.push_back(true); - ++numberOfSimplices; - } - - // Process vertices - dlog::progress progress(vertices.size()); - unsigned ce = 0; // index of the current one past last edge in the complex - SimplexSet cofaces; // record the cofaces of all the simplices that need to be removed and reinserted - - for (unsigned stage = 0; stage != vertices.size() - 1; ++stage) { - unsigned i = vertices.size() - 1 - stage; - - /* Increase epsilon */ - compute_positive_cofaces(distances, vertices, edges, epsilons, multiplier, rips, skeleton, ce, i, cofaces); - - for (auto& s : cofaces) { - // std::cout << "Inserting: " << s << std::endl; - simpls.emplace_back(s.begin(), s.end()); - dirs.push_back(true); - ++numberOfSimplices; - } - - /* Remove the vertex */ - // std::cout << "Removing vertex: " << vertices[i] << std::endl; - compute_negative_cofaces(vertices, epsilons, multiplier, rips, skeleton, i, cofaces); - - for (auto& s : cofaces | ba::reversed) { - // std::cout << "Removing: " << s << std::endl; - simpls.emplace_back(s.begin(), s.end()); - dirs.push_back(false); - } - - ++progress; - } - - std::cout << std::endl; - return numberOfSimplices; -} diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence_old.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence_old.h deleted file mode 100644 index aa93d75a21..0000000000 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence_old.h +++ /dev/null @@ -1,1326 +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): Clément Maria - * - * Copyright (C) 2021 Inria - * - * Modification(s): - * - 2023/05 Hannah Schreiber: Rework of the interface, reorganization and debug - * - 2023/05 Hannah Schreiber: Addition of infinit bars - * - YYYY/MM Author: Description of the modification - */ - -#ifndef ZIGZAG_PERSISTENCE_H_ -#define ZIGZAG_PERSISTENCE_H_ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace Gudhi { -namespace zigzag_persistence { -//represent matrix columns with sets. -struct Zigzag_persistence_colset; -//---------------------------------------------------------------------------------- -/** \class Zigzag_persistence Zigzag_persistence.h gudhi/Zigzag_persistence.h - * \brief Computation of the zigzag persistent homology of a zigzag - * filtered complex. - * - * \details The type ZigzagFilteredComplex::Simplex_key counts the number of - * insertions and - * deletions of simplices, which may be large in zigzag persistence and require - * more than 32 bits of storage. The type used (int, long, etc) should be chosen in - * consequence. Simplex_key must be signed. - * - * Over all insertions, the Simplex_key must be positive and strictly increasing - * when forward iterating along the zigzag filtration. - */ -template < typename ZigzagFilteredComplex - , typename ZigzagPersistenceOptions = Zigzag_persistence_colset > -class Zigzag_persistence { -public: - typedef ZigzagFilteredComplex Complex; - typedef ZigzagPersistenceOptions Options; - /*** Types defined in the complex ***/ - // Data attached to each simplex to interface with a Property Map. - typedef typename Complex::Simplex_key Simplex_key;//must be signed - typedef typename Complex::Simplex_handle Simplex_handle; - typedef typename Complex::Vertex_handle Vertex_handle; - typedef typename Complex::Filtration_value Filtration_value; - // -private: - /*** Matrix cells and columns types ***/ - struct matrix_row_tag; // for horizontal traversal in the persistence matrix - struct matrix_column_tag; // for vertical traversal in the persistence matrix - typedef boost::intrusive::list_base_hook< - boost::intrusive::tag < matrix_row_tag > //allows .unlink() - , boost::intrusive::link_mode < boost::intrusive::auto_unlink > - > base_hook_matrix_row_list; - //hook for a column represented by an intrusive list - typedef boost::intrusive::list_base_hook < //faster hook, less safe - boost::intrusive::tag < matrix_column_tag > - //, boost::intrusive::link_mode < boost::intrusive::auto_unlink > - , boost::intrusive::link_mode < boost::intrusive::safe_link > - > base_hook_matrix_column_list; - //hook for a column represented by an intrusive set - typedef boost::intrusive::set_base_hook < //faster hook, less safe - boost::intrusive::tag < matrix_column_tag > - , boost::intrusive::optimize_size - //, boost::intrusive::link_mode < boost::intrusive::auto_unlink > - , boost::intrusive::link_mode < boost::intrusive::safe_link > - > base_hook_matrix_column_set; - //the data structure for columns is selected in Options::searchable_column - typedef typename std::conditional::type base_hook_matrix_column; - //the only option for rows is the intrusive list - typedef base_hook_matrix_row_list base_hook_matrix_row; - - /* Cell for the persistence matrix. Contains a key for the simplex index, and - * horizontal and vertical hooks for connections within sparse rows and columns. - */ - struct matrix_chain;//defined below, a chain contains a row, a column, and more - /** Type of cell in the sparse homology matrix. - * For now, only coefficients in Z/2Z, so only the row index (called key) is - * stored in the cell. - */ - struct Zigzag_persistence_cell - : public base_hook_matrix_row, public base_hook_matrix_column - { - Zigzag_persistence_cell(Simplex_key key, matrix_chain *self_chain) - : key_(key) - , self_chain_(self_chain) - {} - - Simplex_key key() const { return key_; } - //compare by increasing key value - friend bool operator<( const Zigzag_persistence_cell& c1 - , const Zigzag_persistence_cell& c2) { - return c1.key() < c2.key(); - } - /* In a matrix M, if M[i][j] == x not 0, we represent a cell with key_=i - * (the row index), - * self_chain_ points to the chain corresponding the j-th column, and x_=x. - * Currently, only Z/2Z coefficients are implemented, so x=1. - * - * A cell is connected to all cells of the same row, and all cells of the same - * column, via the two boost::intrusive hooks (row and column). - */ - Simplex_key key_; - matrix_chain * self_chain_; - }; - - //Homology matrix cell - typedef Zigzag_persistence_cell Cell; - // Remark: constant_time_size must be false because base_hook_matrix_row and - // base_hook_matrix_column have auto_unlink link_mode - //vertical list of cells, forming a matrix column stored as an intrusive list - typedef boost::intrusive::list < - Cell - , boost::intrusive::constant_time_size - , boost::intrusive::base_hook< base_hook_matrix_column_list > > Column_list; - //vertical list of cells, forming a matrix column stored as an intrusive set - typedef boost::intrusive::set < - Cell - , boost::intrusive::constant_time_size - , boost::intrusive::base_hook< base_hook_matrix_column_set > > Column_set; - //choice encoded in Options::searchable_column. a column can be - //iterated through, and keys are read in strictly increasing natural order. - typedef typename std::conditional< - Options::searchable_column, - Column_set, - Column_list >::type Column; - //horizontal list of cells, forming a matrix row, no particular order on keys. - typedef boost::intrusive::list < - Cell - , boost::intrusive::constant_time_size - , boost::intrusive::base_hook< base_hook_matrix_row > > Row_list; - //rows are encoded by lists. need to be sorted and traversed - typedef Row_list Row; - - /* Chain for zigzag persistence. A chain stores: - * - a matrix column (col_i) that represents the chain as a sum of simplices - * (represented by their unique key, stored in the cells of the sparse column), - * - a matrix row of all elements of index the lowest index of the column (row_i), - * - is paired with another chain, indicating its type F, G, or H, - * - has a direct access to its lowest index. - */ - struct matrix_chain { - /* Trivial constructor, birth == -3 */ - matrix_chain() : column_(nullptr), row_(nullptr), paired_col_(nullptr), - birth_(-3), lowest_idx_(-1) {} - - /* Creates a matrix chain of type F with one cell of index 'key'. */ - matrix_chain(Simplex_key key) - : paired_col_(nullptr), birth_(key), lowest_idx_(key) - { - // Cell *new_cell = new Cell(key, this); - Cell *new_cell = cellPool_.construct(key, this); - if constexpr(Options::searchable_column) { column_.insert(*new_cell); } - else { column_.push_back(*new_cell); } - row_.push_back(*new_cell); - } - /* Creates a matrix chain of type F with new cells of key indices given by a - * range. Birth and lowest indices are given by 'key'. - * The range [beg,end) must be sorted by increasing key values, the same - * order as the column_ when read from column_.begin() to column_.end(). - * - * SimplexKeyIterator value_type must be Simplex_key. - * KeyToMatrixChain must be of type - * std::map< Simplex_key, typename std::list::iterator > - */ - template< typename SimplexKeyIterator, typename KeyToMatrixChain > - matrix_chain(Simplex_key key, SimplexKeyIterator beg, SimplexKeyIterator end, KeyToMatrixChain &lowidx_to_matidx) - : paired_col_(nullptr), birth_(key), lowest_idx_(key) - { - for(SimplexKeyIterator it = beg; it != end; ++it) - { - // Cell *new_cell = new Cell(*it, this);//create a new cell - Cell *new_cell = cellPool_.construct(*it, this); - //insertion in the column - if constexpr(Options::searchable_column) { - column_.insert(column_.end(), *new_cell); //ordered range - } - else { column_.push_back(*new_cell); } - //insertion in a row, not corresponding to the row stored in this->row_. - lowidx_to_matidx[*it]->row_.push_back( *new_cell ); - } - //Add the bottom coefficient for the chain - // Cell *new_cell = new Cell(key, this); - Cell *new_cell = cellPool_.construct(key, this); - //insertion in the column, key is larger than any *it in [beg, end) above. - if constexpr(Options::searchable_column) { - column_.insert(column_.end(), *new_cell); - } - else { column_.push_back(*new_cell); } - //insertion of row_, that stores no particular order. - row_.push_back( *new_cell ); - } - - /* Creates a matrix chain of type H with new cells of key indices given by a - * range. Birth and lowest indices are given by 'key'. - * The range [beg,end) must be sorted by increasing key values, the same - * order as the column_ when read from column_.begin() to column_.end(). - * - * SimplexKeyIterator value_type must be Simplex_key. - * KeyToMatrixChain must be of type - * std::map< Simplex_key, typename std::list::iterator > - */ - template< typename SimplexKeyIterator, typename KeyToMatrixChain > - matrix_chain(Simplex_key key, matrix_chain *paired_col, SimplexKeyIterator beg, SimplexKeyIterator end, KeyToMatrixChain &lowidx_to_matidx) - : paired_col_(paired_col), birth_(-2), lowest_idx_(key) - { - for(SimplexKeyIterator it = beg; it != end; ++it) - { - // Cell * new_cell = new Cell(*it, this);//create a new cell - Cell *new_cell = cellPool_.construct(*it, this); - //insertion in the column - if constexpr(Options::searchable_column) { - column_.insert(column_.end(), *new_cell); //ordered range - } - else { column_.push_back(*new_cell); } - //insertion in a row, not corresponding to the row stored in this->row_. - lowidx_to_matidx[*it]->row_.push_back( *new_cell ); - } - //Add the bottom coefficient for the chain - // Cell * new_cell = new Cell(key, this); - Cell *new_cell = cellPool_.construct(key, this); - //insertion in the column, key is larger than any *it in [beg, end) above. - if constexpr(Options::searchable_column) { - column_.insert(column_.end(), *new_cell); - } - else { column_.push_back(*new_cell); } - //insertion of row_, that stores no particular order. - row_.push_back( *new_cell ); - } - - /* Erase the chain, all cells were allocated with operator new. */ - ~matrix_chain() - { //empty the column, call delete on all cells - for(typename Column::iterator c_it = column_.begin(); c_it != column_.end(); ) - { - auto tmp_it = c_it; ++c_it; - Cell * tmp_cell = &(*tmp_it); - tmp_it->base_hook_matrix_row::unlink(); //rm from row - column_.erase(tmp_it); - cellPool_.destroy(tmp_cell); - // delete tmp_cell; - } - } - - /* Returns the chain with which *this is paired in the F,G,H classification. - * If in F (i.e., paired with no other column), return nullptr.*/ - matrix_chain * paired_chain() const { return paired_col_; } - /* Assign a paired chain. */ - void assign_paired_chain(matrix_chain *other_col) { paired_col_ = other_col; } - /* Access the column. */ - Column & column() { return column_; } - /* Returns the birth index (b >= 0) of the chain if the column is in F. - * Returns -2 if the chain is in H, and -1 if the chain is in G. */ - Simplex_key birth() const { return birth_; } - /* Assign a birth index to the chain. */ - void assign_birth(Simplex_key b) { birth_ = b; } - void assign_birth(matrix_chain *other) { birth_ = other->birth_; } - /* Returns true iff the chain is indexed in F. */ - bool inF() const { return birth_ > -1; } - /* Returns true iff the chain is indexed in G. */ - bool inG() const { return birth_ == -1; } - /* Returns true iff the chain is indexed in H. */ - bool inH() const { return birth_ == -2; } - Simplex_key lowest_idx() const { return lowest_idx_; } - - Column column_ ; //col at index i, with lowest index i - Row row_ ; //row at index i - matrix_chain * paired_col_ ; //\in F -> nullptr, \in H -> g, \in G -> h - Simplex_key birth_ ; //\in F -> b, \in H -> -2 \in G -> -1 - Simplex_key lowest_idx_ ; //lowest_idx_ = i (upper triangular matrix) - inline static Simple_object_pool cellPool_; - }; - -public: - /** \brief Structure to store persistence intervals by their filtration values. - * - * \details By convention, interval \f$[b;d]\f$ are - * closed for finite indices b and d, and open for left-infinite and/or - * right-infinite endpoints.*/ - struct fil_interval { - fil_interval() {} - fil_interval(int dim, Filtration_value b, Filtration_value d) : dim_(dim), b_(b), d_(d) {} - /** Returns the absolute length of the interval \f$|d-b|\f$. */ - Filtration_value length() { - if(b_ == d_) { return 0; } //otherwise inf - inf would return nan. - return std::abs(b_ - d_); - } - /** Returns the absolute length of the log values of birth and death, i.e. \f$|\log d - \log b|\f$.. */ - Filtration_value log_length() {//return the log-length - if(b_ == d_) { return 0; } //otherwise inf - inf would return nan. - return std::abs(log2((double)b_) - log2((double)d_)); - } - /** Returns the dimension of the homological feature corresponding to the - * interval. */ - int dim() const { return dim_; }//return the homological dimension of the interval - /** Returns the birth of the interval.*/ - Filtration_value birth() const { return b_; }//return the birth value - /** Returns the death of the interval.*/ - Filtration_value death() const { return d_; }//return the death value - /** Swaps the values of birth and death.*/ - void swap_birth_death() { std::swap(b_,d_); } - - private://note that we don't assume b_ <= d_ - int dim_; //homological dimension - Filtration_value b_; //filtration value associated to birth index - Filtration_value d_; //filtration value associated to death index - }; - - /** \brief Structure to store persistence intervals by their index values. - * - * \details By convention, interval [b;d] are - * closed for finite indices b and d, and open for left-infinite and/or - * right-infinite endpoints. - */ - struct interval_index { - interval_index() {} - interval_index(int dim, Simplex_key b, Simplex_key d) : dim_(dim), b_(b), d_(d) {} - /** Returns the dimension of the homological feature corresponding to the - * interval. */ - int dim() const { return dim_; }//return the homological dimension of the interval - /** Returns the birth index of the interval.*/ - Filtration_value birth() const { return b_; }//return the birth value - /** Returns the death index of the interval.*/ - Filtration_value death() const { return d_; }//return the death value - - private://note that we don't assume b_ <= d_ - int dim_; //homological dimension - Simplex_key b_; //filtration value associated to birth index - Simplex_key d_; //filtration value associated to death index - }; - -private: - /* Comparison function to sort intervals by decreasing log-length in the - * output persistence diagram, i.e., - * [f(b),f(d)]<[f(b'),f(d')] iff |log2(f(b))-log2(f(d))|> |log2(f(b'))-log2(f(d'))| - */ - struct cmp_intervals_by_log_length { - cmp_intervals_by_log_length(){} - bool operator()( fil_interval p, fil_interval q) - { - if(p.dim() != q.dim()) {return p.dim() < q.dim();}//lower dimension first - if(p.log_length() != q.log_length()) {return p.log_length() > q.log_length();} - if(p.birth() != q.birth()) {return p.birth() < q.birth();}//lex order - return p.death() < q.death(); - } - }; - /* Comparison function to sort intervals by decreasing length in the - * output persistence diagram, i.e., - * [f(b),f(d)]<[f(b'),f(d')] iff |f(b)-f(d)| > |f(b')-f(d')| - */ - struct cmp_intervals_by_length { - cmp_intervals_by_length(){} - bool operator()( fil_interval p, fil_interval q) - { - if(p.length() != q.length()) { return p.length() > q.length(); }//longest 1st - if(p.dim() != q.dim()) {return p.dim() < q.dim();}//lower dimension first - if(p.birth() != q.birth()) {return p.birth() < q.birth();}//lex order - return p.death() < q.death(); - } - }; - -public: - /** \brief Initialization of the Zigzag_persistence class. - * - * \param[in] cpx A model of ZigzagFilteredComplex. - * */ - Zigzag_persistence(int ignore_cycles_above_dim = -1) - : cpx_() - , dim_max_(ignore_cycles_above_dim) - , lowidx_to_matidx_() - , matrix_() - , birth_ordering_() - , persistence_diagram_() - , num_arrow_(-1) - , previous_filtration_value_(std::numeric_limits::infinity()) - , filtration_values_() {} - -private: - /* Set c1 <- c1 + c2, assuming canonical order of indices induced by the order in - * the vertical lists. self1 is the matrix_chain whose column is c1, for self - * reference of the new cells. - */ - void plus_equal_column(matrix_chain * self1, Column & c1, Column & c2) - { - //insert all elements of c2 in c1, in O(|c2| * log(|c1|+|c2|)) - if constexpr (Options::searchable_column) { - for(auto &cell : c2) { - auto it1 = c1.find(cell); - if(it1 != c1.end()) {//already there => remove as 1+1=0 - Cell * tmp_ptr = &(*it1); - it1->base_hook_matrix_row::unlink(); //unlink from row - c1.erase(it1); //remove from col - matrix_chain::cellPool_.destroy(tmp_ptr); - // delete tmp_ptr; - } - else {//not there, insert new cell - // Cell *new_cell = new Cell(cell.key(), self1); - Cell *new_cell = matrix_chain::cellPool_.construct(cell.key(), self1); - c1.insert(*new_cell); - lowidx_to_matidx_[cell.key()]->row_.push_back(*new_cell);//row link,no order - } - } - } - else {//traverse both columns doing a standard column addition, in O(|c1|+|c2|) - auto it1 = c1.begin(); auto it2 = c2.begin(); - while(it1 != c1.end() && it2 != c2.end()) - { - if(it1->key() < it2->key()) { ++it1; } - else { - if(it1->key() > it2->key()) { - // Cell * new_cell = new Cell(it2->key(), self1); - Cell *new_cell = matrix_chain::cellPool_.construct(it2->key(), self1); - c1.insert(it1, *new_cell); //col link, in order - lowidx_to_matidx_[it2->key()]->row_.push_back(*new_cell);//row link,no order - ++it2; - } - else { //it1->key() == it2->key() - auto tmp_it = it1; ++it1; ++it2; - Cell * tmp_ptr = &(*tmp_it); - tmp_it->base_hook_matrix_row::unlink(); //unlink from row - c1.erase(tmp_it); //remove from col - matrix_chain::cellPool_.destroy(tmp_ptr); - // delete tmp_ptr; - } - } - } - while(it2 != c2.end()) {//if it1 reached the end of its column, but not it2 - // Cell * new_cell = new Cell(it2->key(),self1); - Cell *new_cell = matrix_chain::cellPool_.construct(it2->key(), self1); - lowidx_to_matidx_[it2->key()]->row_.push_back(*new_cell); //row links - c1.push_back(*new_cell); - ++it2; - } - } - } - - /** Maintains the birth ordering <=b. Contains an std::map of size the number of - * non-zero rows of the homology matrix, at any time during the computation of - * zigzag persistence. - * - * By construction, we maintain the map satisfying - * 'birth_to_pos_[i] < birth_to_pos_[j]', - * with 0 <= i,j <= k indices in the quiver '0 \leftrightarrow ... \leftrightarrow i \leftrightarrow .. \leftrightarrow k' - * visited at time k of the algorithm (prefix of length k of the full zigzag - * filtration '0 \leftrightarrow ... \leftrightarrow i \leftrightarrow .. \leftrightarrow k \leftrightarrow ... \leftrightarrow n' that is studied), - * iff i k+1 forward, then j 0 -> 1 -> 2 <- 3 <- 4 -> 5 <- 6 etc - birth_ordering() : birth_to_pos_(), max_birth_pos_(0), min_birth_pos_(-1) {} - - //when the arrow key-1 -> key is forward, key is larger than any other index - //i < key in the birth ordering b k2 - bool reverse_birth_order(Simplex_key k1, Simplex_key k2) { - return birth_to_pos_[k1] > birth_to_pos_[k2]; - } - - private: - //birth_to_pos_[i] < birth_to_pos_[j] iff i birth_to_pos_; - //by construction, max_birth_pos_ (resp. min_birth_pos_) is strictly larger - //(resp. strictly smaller) than any value assigned to a key so far. - Simplex_key max_birth_pos_; - Simplex_key min_birth_pos_; - }; - -public: - /** \brief Computes the zigzag persistent homology of a zigzag filtered complex, - * using the reflection and transposition algorithm of \cite zigzag_reflection. - * - * \details After computation, the persistence diagram can be accessed via - * member method persistence_diagram, for the diagram with filtration - * values, or member method index_persistence_diagram, for the - * diagram with - * indices of paired simplices. - * - * - * matrix_, originally empty, maintains the set of chains, with a - * partition \f$ F \sqcup G \sqcup H\f$ - * representing a compatible homology basis as in \cite zigzag_reflection. - * - * Each simplex in the complex stores a key field that stores the index of - * its insertion in the zigzag filtration. - * - * The algorithm maintains a compatible homology basis for the zigzag filtration. - * - * \f$$\emptyset = K_0 \leftrightarrow (...) \leftrightarrow K_i \leftarrow ... \leftarrow \emptyset\f$$ - * - * where the prefix from \f$K_0\f$ to \f$K_i\f$ is equal to the i-th prefix of - * the input zigzag - * filtration given by cpx_.filtration_simplex_range(), and - * the suffix - * (from \f$K_i\f$ - * to the right) is a sequence of simplex removals. Due to the structure of - * reflection diamonds, the removals are in reverse order of the insertions, to - * reduce the amount of transposition diamonds. - * - * Consequently, using cpx_.key(zzsh) as indexing for the matrix - * rows/cells, - * with the natural order on integers, makes our homology matrix matrix_ upper - * triangular for the suffix \f$K_i \leftarrow ... \leftarrow 0\f$, seen as a - * standard persistence - * filtration. At \f$K_i\f$, the natural order on integers is also equivalent to the - * death-order \f$\leq_d\f$ (because all arrows in the suffix are backward). - * - * Insertion: cpx_.key(*zzit) is a strictly increasing sequence - * for zzit - * insertion of cells (does not need to be contiguous). However, for every forward - * arrow, we have cpx_.key(*zzit) == num_arrows_. - * Removal: cpx_.key(*zzit) gives the assigned key (during past - * insertion) of a - * cell == *zzit during a removal. We use num_arrows_ - * to record the deaths in the - * persistence diagram. - * Insertion and Removal: zzit.filtration() is totally monotone. - * Note that the - * iterator encodes the filtration, and not the cells within the complex structure. - */ -// void zigzag_persistent_homology() -// { //compute index persistence, interval are closed, i.e., [b,d) is stored as -// //[b,d-1]. The filtration values are maintained in field filtration_values_ -// Filtration_value prev_fil_, curr_fil_; - -// assert(num_arrow_ == 0); -// auto zzrg = cpx_.filtration_simplex_range(); -// auto zzit = zzrg.begin(); -// dim_max_ = zzit.dim_max(); - -// num_arrow_ = cpx_.key(*zzit);//should be 0 - -// prev_fil_ = zzit.filtration(); -// filtration_values_.emplace_back(num_arrow_, prev_fil_); - -// while( zzit != zzrg.end() ) -// { //insertion of a simplex -// if(zzit.arrow_direction()) { num_arrow_ = cpx_.key(*zzit); } -// else { ++num_arrow_; } //removal of a simplex, a simplex key corresponds to the index of its INSERTION -// curr_fil_ = zzit.filtration();//cpx_.filtration(*zzit) is invalid for (<-); -// if(curr_fil_ != prev_fil_) //check whether the filt value has changed -// { //consecutive pairs (i,f), (j,f') mean simplices of index k in [i,j-1] have -// prev_fil_ = curr_fil_; //filtration value f -// filtration_values_.emplace_back(num_arrow_, prev_fil_); -// } -// if(zzit.arrow_direction()) { //forward arrow, only consider critical cells -// forward_arrow(*zzit); -// } -// else { //backward arrow -// backward_arrow(*zzit); -// } -// ++zzit; -// } - -//// if(!matrix_.empty()) { -//// std::cout << "There remain " << matrix_.size() << " columns in the matrix.\n"; -//// } -// } - - template> - void insert_simplex(const VertexRange& simplex, Filtration_value filtration_value) - { - if (dim_max_ != -1 && simplex.size() > static_cast(dim_max_) + 1) return; - - ++num_arrow_; - - if (filtration_value != previous_filtration_value_) //check whether the filt value has changed - { //consecutive pairs (i,f), (j,f') mean simplices of index k in [i,j-1] have - previous_filtration_value_ = filtration_value; //filtration value f - filtration_values_.emplace_back(num_arrow_, previous_filtration_value_); - } - - std::pair res = cpx_.insert_simplex(simplex, filtration_value); - GUDHI_CHECK(res.second, "Zigzag_persistence::insert_simplex - insertion of a simplex already in the complex"); - cpx_.assign_key(res.first, num_arrow_); - _process_forward_arrow(res.first); - } - - template> - void remove_simplex(const VertexRange& simplex, Filtration_value filtration_value) - { - if (dim_max_ != -1 && simplex.size() > static_cast(dim_max_) + 1) return; - - ++num_arrow_; - - Simplex_handle sh = cpx_.find(simplex); - GUDHI_CHECK(sh != cpx_.null_simplex(), "Zigzag_persistence::remove_simplex - removal of a simplex not in the complex"); - - if (filtration_value != previous_filtration_value_) //check whether the filt value has changed - { //consecutive pairs (i,f), (j,f') mean simplices of index k in [i,j-1] have - previous_filtration_value_ = filtration_value; //filtration value f - filtration_values_.emplace_back(num_arrow_, previous_filtration_value_); - } - - _process_backward_arrow(sh); - cpx_.remove_maximal_simplex(sh); - } - - template>, - class FiltrationRange = std::initializer_list> - void insert_simplices_contiguously(const SimplexRange& simplices, const FiltrationRange& filtration_values) - { - auto simplexIt = simplices.begin(); - auto filIt = filtration_values.begin(); - for (; simplexIt != simplices.end(); ++simplexIt, ++filIt) { - insert_simplex(*simplexIt, *filIt); - } - } - - template>, - class FiltrationRange = std::initializer_list> - void remove_simplices_contiguously(const SimplexRange& simplices, const FiltrationRange& filtration_values) - { - auto simplexIt = simplices.begin(); - auto filIt = filtration_values.begin(); - for (; simplexIt != simplices.end(); ++simplexIt, ++filIt) { - remove_simplex(*simplexIt, *filIt); - } - } - - template - void insert_simplices_contiguously(SimplexRangeIterators simplex_range_start, - SimplexRangeIterators simplex_range_end, - FiltrationRangeIterators filtration_range_start) - { - for (; simplex_range_start != simplex_range_end; ++simplex_range_start, ++filtration_range_start) { - insert_simplex(*simplex_range_start, *filtration_range_start); - } - } - - template - void remove_simplices_contiguously(SimplexRangeIterators simplex_range_start, - SimplexRangeIterators simplex_range_end, - FiltrationRangeIterators filtration_range_start) - { - for (; simplex_range_start != simplex_range_end; ++simplex_range_start, ++filtration_range_start) { - remove_simplex(*simplex_range_start, *filtration_range_start); - } - } - - void print_current_complex(){ - for (auto& sh : cpx_.complex_simplex_range()){ - for (auto v : cpx_.simplex_vertex_range(sh)){ - std::cout << v << " "; - } - std::cout << " - " << cpx_.filtration(sh) << "\n"; - } - } - -private: - /** \brief Computes the boundary cycle of the new simplex zzsh, and express it as a - * sum of cycles. If all cycles are boundary cycles, i.e., columns with G-index - * in the matrix, then [\partial zzsh] = 0 and we apply an injective diamond to - * the zigzag module. Otherwise, we keep reducing with boundary- and live- cycles, - * i.e., columns with (F \cup G)-indices, and then apply a surjective diamond to - * the zigzag module. - */ - void _process_forward_arrow( Simplex_handle zzsh ) - { //maintain the <=b order - birth_ordering_.add_birth_forward(num_arrow_); - - //Reduce the boundary of zzsh in the basis of cycles. - //Compute the simplex keys of the simplices of the boundary of zzsh. - std::set< Simplex_key > col_bsh; //set maintains the natural order on indices - for( auto b_sh : cpx_.boundary_simplex_range(zzsh) ) - { col_bsh.insert(cpx_.key(b_sh)); } - - //If empty boundary (e.g., when zzsh is a vertex in a simplicial complex) - //Add a non-trivial cycle [c = zzsh] to the matrix, lowidx_to_matidx_make it a creator in F. - if(col_bsh.empty()) // -> creator - { //New row and column with a bottom-right non-zero element, at index key(zzsh) - //i.e., create a new cycle in F, equal to *zzsh alone. - matrix_.emplace_front(num_arrow_); - auto new_chain_it = matrix_.begin();//the new chain - //Update the map [index idx -> chain with lowest index idx] in matrix_ - lowidx_to_matidx_[num_arrow_] = new_chain_it; - return; - } - - // col_bsh.rbegin()) is idx of lowest element in col_bsh, because it is a set. - matrix_chain *col_low = &(*lowidx_to_matidx_[*(col_bsh.rbegin())]); - auto paired_idx = col_low->paired_col_; //col with which col_low is paired - std::vector< matrix_chain * > chains_in_H; //for corresponding indices in H - std::vector< matrix_chain * > chains_in_G; - - //Reduce col_bsh with boundary cycles, i.e., indices in G. - std::pair< typename std::set< Simplex_key >::iterator, bool > res_insert; - while( paired_idx != nullptr ) - { - chains_in_H.push_back(paired_idx);//keep the col_h with which col_g is paired - chains_in_G.push_back(col_low); //keep the col_g - for(auto &cell : (col_low->column())) { //Reduce with the column col_g - res_insert = col_bsh.insert(cell.key()); - if( !res_insert.second ) { col_bsh.erase(res_insert.first); } //1+1 = 0 - //o.w. insertion has succeeded. - } - //If col_bsh is entirely reduced, \partial zzsh is a boundary cycle. - if(col_bsh.empty()) { - // if(cpx_.dimension(zzsh) >= max_dim_) {return;} we need max_dim creators - injective_reflection_diamond(zzsh, chains_in_H); - return; - } - //Continue the reduction - col_low = &(*lowidx_to_matidx_[*(col_bsh.rbegin())]);//curr low index col - paired_idx = col_low->paired_col_;//col with which col_low is paired - } - - //Continue reducing with boundary and 'live' cycles, i.e., indices in G U F. - std::vector< matrix_chain * > chains_in_F; - while(true) - { - if(paired_idx == nullptr) { chains_in_F.push_back(col_low); }//col_low is in F - else { chains_in_H.push_back(paired_idx); } //col_low in G, paired_idx is in H - //Reduce with the column col_g or col_f - for(auto &cell : (col_low->column())) { - res_insert = col_bsh.insert(cell.key()); - if( !res_insert.second ) { col_bsh.erase(res_insert.first); } //1+1 = 0 - //o.w. insertion has succeeded. - } - //If col_bsh is entirely reduced, i.e. col_bsh == \emptyset. - if(col_bsh.empty()) - { - surjective_reflection_diamond(zzsh, chains_in_F, chains_in_H); - return; - } - //Else, keep reducing. - col_low = &(*lowidx_to_matidx_[*(col_bsh.rbegin())]); //curr low index col - paired_idx = col_low->paired_col_;//col with which col_low is paired - } - } - - /** \brief Computes an injective diamond in the zigzag module, by inserting a new - * column for the chain zzsh - \sum col_h, for all col_h in chains_in_H, and a - * new row for the simplex zzsh. - */ - void injective_reflection_diamond ( Simplex_handle zzsh - , std::vector< matrix_chain * > & chains_in_H ) - { //Compute the chain zzsh + \sum col_h, for col_h \in chains_in_H - std::set< Simplex_key > col_bsh; - std::pair< typename std::set< Simplex_key >::iterator, bool > res_insert; - //produce the sum of all col_h in chains_in_H - for( matrix_chain *idx_h : chains_in_H ) { - for(auto &cell : (idx_h->column()) ) { - res_insert = col_bsh.insert(cell.key()); - if( !res_insert.second ) { col_bsh.erase(res_insert.first); } - } - } - //create a new cycle (in F) sigma - \sum col_h - matrix_.emplace_front(num_arrow_, col_bsh.begin(), col_bsh.end(), - lowidx_to_matidx_); - //Update the map 'index idx -> chain with lowest index idx' in matrix_ - auto chain_it = matrix_.begin(); - lowidx_to_matidx_[num_arrow_] = chain_it; - } - - /** The vector chains_in_F is sorted by decreasing lowest index values in the - * columns corresponding to the chains, due to its computation in the reduction of - * \partial zzsh in forward_arrow(...). It is equivalent to decreasing death index - * order w.r.t. the & chains_in_F - , std::vector< matrix_chain * > & chains_in_H ) - { //fp is the largest death index for <=d - //Set col_fp: col_fp <- col_f1+...+col_fp (now in G); preserves lowest idx - auto chain_fp = *(chains_in_F.begin()); //col_fp, with largest death column(), (*other_col_it)->column()); } - //doesn't change the lowest idx as chain_fp has maximal lowest idx of all - - //chains_in_F is ordered, from .begin() to end(), by decreasing lowest_idx_. The - //lowest_idx_ is also the death of the chain in the right suffix of the - //filtration (all backward arrows). Consequently, the chains in F are ordered by - //decreasing death for bool - { return birth_ordering_.reverse_birth_order(k1,k2); };//true iff b(k1) >b b(k2) - - //available_birth: for all i by >d value of the d_i, - //contains at step i all b_j, j > i, and maybe b_i if not stolen - std::set< Simplex_key, decltype(cmp_birth) > available_birth(cmp_birth); - //for f1 to f_{p} (i by <=d), insertion in available_birth_to_fidx sorts by >=b - for(auto &chain_f : chains_in_F) { available_birth.insert(chain_f->birth()); } - - auto maxb_it = available_birth.begin();//max birth cycle - auto maxb = *maxb_it; //max birth value, for persistence diagram - available_birth.erase(maxb_it); //remove max birth cycle (stolen) - - auto last_modified_chain_it = chains_in_F.rbegin(); - - //consider all death indices by increasing birth()); - if(birth_it == available_birth.end()) //birth is not available. *chain_f_it - { //must become the sum of all chains in F with smaller death index. - //this gives as birth the maximal birth of all chains with strictly larger - //death <=> the maximal availabe death. - //Let c_1 ... c_f be the chains s.t. <[c_1+...+c_f]> is the kernel and - // death(c_i) >d death(c_i-1). If the birth of c_i is not available, we set - //c_i <- c_i + c_i-1 + ... + c_1, which is [c_i + c_i-1 + ... + c_1] on - //the right (of death the maximali <=> the maxj>k, are indeed c_j - //set c_i <- c_i + (c_i-1) + ... + (c_k+1) + (c_k + ... + c_1) - for(auto chain_passed_it = last_modified_chain_it;//all with smaller column() - , (*chain_passed_it)->column() ); - } - last_modified_chain_it = chain_f_it;//new cumulated c_i+...+c_1 - //remove the max available death - auto max_avail_b_it = available_birth.begin();//max because order by deacr assign_birth(max_avail_b); //give new birth - available_birth.erase(max_avail_b_it); //remove birth from availability - } - else { available_birth.erase(birth_it); } //birth not available anymore, do not - } //modify *chain_f_it. - //Compute the new column zzsh + \sum col_h, for col_h in chains_in_H - std::set< Simplex_key > col_bsh; - std::pair< typename std::set< Simplex_key >::iterator, bool > res_insert; - for(auto other_col : chains_in_H) - { //Compute (\sum col_h) in a set - for(auto &cell : (other_col->column())) - { - res_insert = col_bsh.insert(cell.key()); - if( !res_insert.second ) { col_bsh.erase(res_insert.first); } //1+1=0 - } - } - //Create and insert (\sum col_h) + sigma (in H, paired with chain_fp) in matrix_ - matrix_.emplace_front(cpx_.key(zzsh), chain_fp, col_bsh.begin(), col_bsh.end(), lowidx_to_matidx_); - //record that the chain with lowest index key(zzsh) is the one just created - auto chain_it = matrix_.begin(); - lowidx_to_matidx_[cpx_.key(zzsh)] = chain_it;//new row - - chain_fp->assign_paired_chain( &(*chain_it) );//pair chain_fp with the new chain - chain_fp->assign_birth(-1); //now belongs to G now -> right interval [m-1,g] - - //Update persistence diagram with left interval [fil(b_max) ; fil(m)) - persistence_diagram_.emplace_back( cpx_.dimension(zzsh)-1 - , maxb - , cpx_.key(zzsh));//-1);// - } - - //cpx_.key(zzsh) is the key of the simplex we remove, not a new one - void _process_backward_arrow( Simplex_handle zzsh ) - { - //maintain the <=b order - birth_ordering_.add_birth_backward(num_arrow_); - //column whose key is the one of the removed simplex - auto curr_col_it = lowidx_to_matidx_.find(cpx_.key(zzsh)); - //corresponding chain - matrix_chain * curr_col = &(*(curr_col_it->second)); - //Record all columns that get affected by the transpositions, i.e., have a coeff - std::vector< matrix_chain * > modified_columns;//in the row of idx key(zzsh) - for(auto & hcell : (curr_col->row_)) { - modified_columns.push_back(hcell.self_chain_); - } - //Sort by left-to-right order in the matrix_ (no order maintained in rows) - std::stable_sort( modified_columns.begin(),modified_columns.end() - , [](matrix_chain *mc1, matrix_chain *mc2) - { return mc1->lowest_idx_ < mc2->lowest_idx_;} ); - - //Modifies the pointer curr_col, not the other one. - for(auto other_col_it = modified_columns.begin()+1; - other_col_it != modified_columns.end(); ++other_col_it) { - curr_col = arrow_transposition_case_study(curr_col, *other_col_it); - } - - //curr_col points to the column to remove by restriction of K to K-{\sigma} - if( curr_col->paired_col_ == nullptr ) { // in F - int dim_zzsh = cpx_.dimension(zzsh); - if(dim_max_ == -1 || (dim_max_ != -1 && dim_zzsh < dim_max_)) { //don't record intervals of max dim - persistence_diagram_.emplace_back( dim_zzsh - , curr_col->birth() - , num_arrow_);// -1); - } - } - else { //in H -> paired with c_g, that now belongs to F now - curr_col->paired_col_->assign_paired_chain(nullptr); - curr_col->paired_col_->assign_birth(num_arrow_); //closed interval - } - - //cannot be in G as the removed simplex is maximal - matrix_.erase(curr_col_it->second); - lowidx_to_matidx_.erase(curr_col_it); - } - - /* Exchanges members of matrix_chains, except the column_ pointer. Modify - * also the lowidx_to_matidx_ data structure, considering that the matrix chains - * also exchange their lowest_idx_. Specifically, it is called by - * arrow_transposition_case_study when: - * c_s has originally birth b_s and low idx s, and c_t has birth b_t and low idx t - * however, c_s becomes c_s <- c_s+c_t with b_t lowest_idx_); - auto it_t = lowidx_to_matidx_.find(other_col->lowest_idx_); - - std::swap(it_s->second, it_t->second);//swap matrix_chain* in lowidx_to_matidx_ - std::swap(curr_col->row_, other_col->row_);//swap associated row of lowest idx - std::swap(curr_col->lowest_idx_, other_col->lowest_idx_);//swap lowest idx. - } - - /** - * Permutes s and t, s goes up, whose insertions are adjacent, i.e., the following - * transformation (from (a) to (b)) in the filtration: - * from (a) ... \leftrightarrow K \leftarrow ... \leftarrow K' U {s,t} \leftarrow K' U {s} \leftarrow K' \leftarrow ..., - * where K' is at matrix index i, K' U {s} at matrix index i+1 and K' U {s,t} at - * matrix index i+2, - * - * to (b) ... \leftrightarrow K \leftarrow ... \leftarrow K' U {s,t} \leftarrow K' U {t} \leftarrow K' \leftarrow ..., - * - * and the chain c_t has a non-trivial coefficient for s, i.e., - * the bloc matrix gives (and becomes): - * c_t c_t - * + + - * c_s c_t c_s c_s c_s c_t - * s 1 1 t 1 0 t 1 1 - * t 0 1 --> either s 0 1 or s 0 1 - * - * By construction, s is a simplex that we want to remove in the complex K. It is - * consequently maximal in K, and all complexes between K and K' U {s} in filtration - * (a). - * - * If c_s and c_t are both cycles (in F)that, before the permutation, are carried by - * respectively the closed intervals [b_s, i+1] and [b_t, i+2], then the sum - * c_s + c_t is a cycle carried by the interval - * [maxd i (i+1 \leftarrow i backward). - * If j \leftarrow ... \leftarrow k are both birth indices on the right part of the quiver (all - * backward arrows) then systematically k inF()) - {//case F x * - if(other_col->inH()) { // case F x H - plus_equal_column( other_col, other_col->column()//c_t <- c_s+c_t still in H - , curr_col->column() );//(birth -2) and low idx t - return curr_col; - }//end case F x H - else // case F x F - { //in F x F: c_s+c_t has max<=b birth between b_t and b_s: - if(birth_ordering_.birth_order(curr_col->birth(), other_col->birth())) - { //max<=b is the birth of other_col i.e., b_s column()//c_t <- c_s+c_t of birth - , curr_col->column() );//b_t and lowest idx t. (same) - //c_s still has birth b_s (minimal) and lowest idx s - return curr_col;//continue with c_s of smaller birth b_s and lowest idx s - }//endif - else - { //max<=b is the birth of curr_col, i.e., b_t column()//c_s <- c_s+c_t of birth - , other_col->column() );//b_s and of NEW lowest idx t - //now c_t has (same) birth b_t (minimal) but NEW lowest idx s, so - //exchange lowest_idx, the rows, and update lowidx_to_matidx structure - exchange_lowest_indices_chains(curr_col, other_col); - return other_col;//continue with c_t of (smaller) birth b_t and low idx s - }//end else - }//end case F x F - }//end case F x * - else {//curr_col->inH() == true, case H x * - if(other_col->inH()) {// case H x H - //Case H x H, c_s+c_t paired w/ c_gs+c_gt, of death - //maxpaired_col_; //c_s paired with c_gs, death d_gs - auto other_p_col = other_col->paired_col_;//c_t paired with c_gt, death d_gt - if( curr_p_col->lowest_idx_ < other_p_col->lowest_idx_)//<=> d_gs column()//c_gt <- c_gs+c_gt, - , curr_p_col->column() );//of death d_gt, low idx d_gt - //(same because bigger), paired with c_s+c_t (now &c_t, updated below) - plus_equal_column( other_col, other_col->column()//c_t <- c_t+c_s, still - , curr_col->column() );//in H, low idx t (same) - return curr_col;//continue with c_s, paired with c_gs of min death d_gs - } - else - {// d_gt column()//c_gs <- c_gs+c_gt, - , other_p_col->column() );//of death d_gs, low idx d_gs - //(same because bigger), paired with c_s+c_t (now &c_s, updated below) - plus_equal_column( curr_col, curr_col->column()//c_s <- c_s+c_t, of NEW - , other_col->column());//low idx t (still in H) - //now c_s is still in H (birth -2) but has NEW lowest idx t, and c_t has - //low idx s after transposition. - //exchange lowest_idx, the rows, and update lowidx_to_matidx structure - exchange_lowest_indices_chains(curr_col, other_col); - return other_col; //continue with c_t, paired w. c_g' of min death g' - } - }//end case H x H - else {//other_col->inF() == true, case H x F - plus_equal_column( curr_col, curr_col->column() //c_s <- c_s+c_t still in H, - , other_col->column()); //(birth -2) and NEW low idx t - //now c_t, still in F, has (same) birth b_t but NEW lowest idx s, so - //exchange lowest_idx, the rows, and update lowidx_to_matidx structure - exchange_lowest_indices_chains(curr_col, other_col); - return other_col; //continue with c_t, still in F, of birth b_t and low idx s - } - } - } - - -public: - /** \brief Returns the index persistence diagram as an std::list of intervals.*/ - const std::list< interval_index > & get_index_persistence_diagram() const - { - return persistence_diagram_; - } - - /** \brief Returns the filtration values \f$[f(b),f(d)]\f$ (generally real-valued) - * associated to the indices \f$[b,d]\f$ (integer valued) of the insertion or - * deletion of a simplex in the zigzag filtration. - * - * \details Used to convert a persistent interval \f$[b,d]\f$, computed by the - * persistent homology algorithm, into its filtration valued version - * \f$[f(b),f(d)]\f$ used in the persistence barcode. The information - * index->filtration is stored in the member filtration_values_ of - * the class Zigzag_persistence. - * - * @param[in] b_key, d_key The indices of birth and death of a persistent - * interval. - * - * @param[out] std::pair A pair of real values \f$(f(b),f(d))\f$. - */ - std::pair map_index_to_filtration_value( - Simplex_key b_key, Simplex_key d_key) { - // filtration_values_ must be sorted by increasing keys. - auto it_b = //lower_bound(x) returns leftmost y s.t. x <= y - std::lower_bound( filtration_values_.begin(), filtration_values_.end() - , std::pair(b_key - , std::numeric_limits::infinity() ) - , []( std::pair p1 - , std::pair p2) - { return p1.first < p2.first; } - ); - if(it_b == filtration_values_.end() || it_b->first > b_key) { --it_b; } - //it points to the rightmost z such that z <= x - - auto it_d = // - std::lower_bound( filtration_values_.begin(), filtration_values_.end() - , std::pair(d_key - , std::numeric_limits::infinity() ) - , []( std::pair p1 - , std::pair p2) - { return p1.first < p2.first; } - ); - if(it_d == filtration_values_.end() || it_d->first > d_key) { --it_d; } - - return std::make_pair(it_b->second, it_d->second); - } - - /** \brief Writes the persistence diagram in a file. - * - * \details The filtration values are given by the zigzag persistence iterator, that assigns - * to any insertion or deletion of a simplex a filtration value ; we say that an - * arrow has an index \f$i\f$, and a corresponding filtration value \f$f(i)\f$. - * Reading a zigzag filtration from left to right, indices are strictly - * monotonically increasing, and the associated filtration values are monotonous - * (not necessarily - * strictly, either increasing or decreasing). - * - * Consider two consecutive arrows (insertion or deletion): - * - * \f$$K_1 \leftrightarrow K_2 \leftrightarrow K_3\f$$ - * - * with respectively indices \f$i\f$ (left) and \f$i+1\f$ (right), and associated - * filtration values \f$f(i)\f$ and \f$f(i+1)\f$ respectively. - * - * If, the arrow \f$K_2 \leftrightarrow K_3\f$ leads to the creation of a new - * homology feature in \f$K_3\f$, it creates an (indexed) persistent interval - * \f$[\f$i+1; \cdot\f$, and a corresponding (filtration) persistent interval - * \f$[f(i+1); \cdot]\f$ in the persistence diagram. - * - * If a homology feature in \f$K_2\f$ is destroyed by the arrow \f$K_2 \leftrightarrow K_3\f$, it closes an (indexed) - * interval \f$[\cdot ; i+1]\f$, and a corresponding (filtration) persistent - * interval \f$[\cdot ; f(i+1)]\f$ in the persistence diagram. - * - * For example, in an oscillating Rips zigzag filtration, if, in the following - * chunk of filtration: - * - * \f$R_{\eta \varepsilon_i}(P_i) \rightarrow \cdots \leftarrow R_{\eta \varepsilon_{i+1}}(P_{i+1}),\f$ - * - * if anything is created by any of the arrows above, it leads to an interval - * \f$[\varepsilon_{i+1}; \cdot]\f$. If anything is destroyed by any of the arrows - * above, if leads to an interval \f$[\cdot;\varepsilon_i]\f$. Note that we may - * have \f$\varepsilon_i > \varepsilon_{i+1}\f$. - * - * The bars are ordered by decreasing length. - * - * @param[in] os the output stream in which the diagram is written. - * @param[in] shortest_interval all intervals of lenght smaller or equal to - * this value are ignore. Default is 0. - */ - void persistence_diagram( std::ostream& os - , Filtration_value shortest_interval = 0.) { - - std::stable_sort(filtration_values_.begin(), filtration_values_.end(), - []( std::pair< Simplex_key, Filtration_value > p1 - , std::pair< Simplex_key, Filtration_value > p2 ) - { return p1.first < p2.first; } - ); - - std::vector< fil_interval > tmp_diag; - tmp_diag.reserve(persistence_diagram_.size()); - for(auto bar : persistence_diagram_) - { - Filtration_value birth,death; - std::tie(birth,death) = map_index_to_filtration_value(bar.birth(), bar.death()); - - if( std::abs(birth - death) > shortest_interval ) { - tmp_diag.emplace_back(bar.dim(), birth, death ); - } - } - // cmp_intervals_by_length cmp; - std::stable_sort(tmp_diag.begin(), tmp_diag.end(), cmp_intervals_by_length()); - - os << "# dim birth death [length]\n"; - for(auto bar : tmp_diag) { - if(bar.birth() > bar.death()) { bar.swap_birth_death(); } - os << bar.dim() << " " << bar.birth() << " " << bar.death() << - " - [" << bar.length() << "] \n"; - } - } - - /** \brief Returns the persistence diagram as a vector of real-valued intervals. */ - std::vector< fil_interval > - get_persistence_diagram(Filtration_value shortest_interval = 0., bool include_infinit_bars = false) - { - std::stable_sort(filtration_values_.begin(), filtration_values_.end(), - []( std::pair< Simplex_key, Filtration_value > p1 - , std::pair< Simplex_key, Filtration_value > p2 ) - { return p1.first < p2.first; } - ); - - std::vector< fil_interval > diag; - diag.reserve(persistence_diagram_.size()); - for(auto bar : persistence_diagram_) - { - Filtration_value birth,death; - std::tie(birth,death) = map_index_to_filtration_value(bar.birth(), bar.death()); - - if( std::abs(birth - death) > shortest_interval ) { - diag.emplace_back(bar.dim(), birth, death ); - } - } - //put lower value as birth - for(auto &bar : diag) { - if( bar.birth() > bar.death() ) { bar.swap_birth_death(); } - } - std::stable_sort(diag.begin(), diag.end(), cmp_intervals_by_length()); - - auto birth = - [this](Simplex_key b_key) { - auto it_b = // lower_bound(x) returns leftmost y s.t. x <= y - std::lower_bound(filtration_values_.begin(), filtration_values_.end(), - std::pair( - b_key, std::numeric_limits::infinity()), - [](std::pair p1, - std::pair p2) { return p1.first < p2.first; }); - if (it_b == filtration_values_.end() || it_b->first > b_key) { - --it_b; - } - return it_b->second; - }; - - //TODO: dimension value - if (include_infinit_bars) { - for (const matrix_chain &col : matrix_) { - if (col.inF()) - diag.emplace_back(-1, birth(col.birth()), std::numeric_limits::infinity()); - } - } - - return diag; - } - -private: - Complex cpx_; // complex - int dim_max_;//max dim complex - //idx -> chain with lowest element at index idx in matrix_ - std::map< Simplex_key, typename std::list::iterator > - lowidx_to_matidx_; - //arbitrary order for the matrix chains - std::list< matrix_chain > matrix_; // 0 ... m-1 - // birth_vector birth_vector_; //<=b order - birth_ordering birth_ordering_; - std::list< interval_index > persistence_diagram_; - Simplex_key num_arrow_; //current index - Filtration_value previous_filtration_value_; - // filtration_values stores consecutive pairs (i,f) , (j,f') with f != f', - // meaning that all inserted simplices with key in [i;j-1] have filtration value f - //i is the smallest simplex index whose simplex has filtration value f. - std::vector< std::pair< Simplex_key, Filtration_value > > filtration_values_; -};//end class Zigzag_persistence - - -/** ZigzagPersistenceOptions, represents matrix columns by intrusive lists.*/ -struct Zigzag_persistence_collist { - static const bool searchable_column = false; -}; -/** ZigzagPersistenceOptions, represents matrix columns by intrusive sets.*/ -struct Zigzag_persistence_colset { - static const bool searchable_column = true; -}; - -} //namespace zigzag_persistence - -} //namespace Gudhi - -#endif //ZIGZAG_PERSISTENCE_H_ - From bfc564291dbafc1290c8bfe0fa7f77e488eebcb2 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Fri, 21 Jul 2023 19:29:21 +0200 Subject: [PATCH 04/51] doc main page --- biblio/bibliography.bib | 34 ++++++++++-- src/Zigzag_persistence/doc/COPYRIGHT | 12 ++++ .../doc/Intro_zigzag_persistence.h | 52 ++++++++++++++++++ src/Zigzag_persistence/doc/zigzag_ex.png | Bin 0 -> 2547 bytes .../include/gudhi/Zigzag_persistence.h | 2 + src/common/doc/examples.h | 3 + src/common/doc/main_page.md | 25 +++++++++ 7 files changed, 124 insertions(+), 4 deletions(-) create mode 100644 src/Zigzag_persistence/doc/COPYRIGHT create mode 100644 src/Zigzag_persistence/doc/Intro_zigzag_persistence.h create mode 100644 src/Zigzag_persistence/doc/zigzag_ex.png diff --git a/biblio/bibliography.bib b/biblio/bibliography.bib index 03b46ff564..907391c607 100644 --- a/biblio/bibliography.bib +++ b/biblio/bibliography.bib @@ -38,10 +38,36 @@ @article{Carriere16 year = {2017} } -@inproceedings{zigzag_reflection, - author = {Jean-Daniel Boissonnat and Cl\'ement Maria and Steve Oudot}, - title = {Zigzag Persistent Homology Algorithm via Reflections}, - year = {2014 $\ \ \ \ \ \ \ \ \ \ \ $ \emph{In Preparation}}, +@inproceedings{zigzag, + author = {Cl{\'{e}}ment Maria and + Steve Y. Oudot}, + editor = {Piotr Indyk}, + title = {Zigzag Persistence via Reflections and Transpositions}, + booktitle = {Proceedings of the Twenty-Sixth Annual {ACM-SIAM} Symposium on Discrete + Algorithms, {SODA} 2015, San Diego, CA, USA, January 4-6, 2015}, + pages = {181--199}, + publisher = {{SIAM}}, + year = {2015}, + url = {https://doi.org/10.1137/1.9781611973730.14}, + doi = {10.1137/1.9781611973730.14} +} + +@inproceedings{zigzag_morse, + author = {Cl{\'{e}}ment Maria and + Hannah Schreiber}, + editor = {Zachary Friggstad and + J{\"{o}}rg{-}R{\"{u}}diger Sack and + Mohammad R. Salavatipour}, + title = {Discrete Morse Theory for Computing Zigzag Persistence}, + booktitle = {Algorithms and Data Structures - 16th International Symposium, {WADS} + 2019, Edmonton, AB, Canada, August 5-7, 2019, Proceedings}, + series = {Lecture Notes in Computer Science}, + volume = {11646}, + pages = {538--552}, + publisher = {Springer}, + year = {2019}, + url = {https://doi.org/10.1007/978-3-030-24766-9\_39}, + doi = {10.1007/978-3-030-24766-9\_39} } @article{Cohen-Steiner2009, diff --git a/src/Zigzag_persistence/doc/COPYRIGHT b/src/Zigzag_persistence/doc/COPYRIGHT new file mode 100644 index 0000000000..61f17f6da1 --- /dev/null +++ b/src/Zigzag_persistence/doc/COPYRIGHT @@ -0,0 +1,12 @@ +The files of this directory are 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) 2015 Inria + +This gives everyone the freedoms to use openFrameworks in any context: +commercial or non-commercial, public or private, open or closed source. + +You should have received a copy of the MIT License along with this program. +If not, see https://opensource.org/licenses/MIT. \ No newline at end of file diff --git a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h new file mode 100644 index 0000000000..5fe392a659 --- /dev/null +++ b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h @@ -0,0 +1,52 @@ +/* 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): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#ifndef DOC_ZIGZAG_PERSISTENCE_INTRO_ZIGZAG_PERSISTENCE_H_ +#define DOC_ZIGZAG_PERSISTENCE_INTRO_ZIGZAG_PERSISTENCE_H_ + +// needs namespace for Doxygen to link on classes +namespace Gudhi { +namespace zigzag_persistence { + +/** \defgroup zigzag_persistence Zigzag Persistence + * @{ + * \author Clément Maria, Hannah Schreiber + * + * We refer to the introduction page \ref persistent_cohomology for persistent (co)homology for an introduction + * to the topic. + * Zigzag persistence is a generalization of the latter. While standard persistence only allows to grow the filtered + * complex by adding simplices, zigzag persistence also allows removals. Hence the name "zigzag", as the module + * diagram will have arrows alterning between forward and backward. + * + * The implementation is based on the algorithm introduced in \cite zigzag. + * + * \subsection zigzaginterface Stream-like interface + * + * As removals are possible in zigzag filtration, the maximal size of the complex does not depend on the length of the + * filtration anymore. This makes it possible to build very long fine tuned filtrations with relatively small complexes + * which can be processed without overreaching memory space. For this purpose, it is possible to feed the module with + * information about the filtration "on the fly" to avoid loading the whole filtration at once. Information about the + * current complex and current barcode can be retrieved between any steps. + * + * \subsection zigzagexamples Examples + * + * Here is a list of zigzag persistence examples : + * \li \gudhi_example_link{Zigzag_persistence,example_simple_zigzag_filtration.cpp} - A simple example to showcase how + * to use the \ref Zigzag_persistence class. + * + * \li \gudhi_example_link{Zigzag_persistence,example_zzfiltration_from_file.cpp} - An example of a "stream-like" usage + * by reading of the filtration from a file. + * + * @} + */ +} // namespace zigzag_persistence +} // namespace Gudhi + +#endif // DOC_ZIGZAG_PERSISTENCE_INTRO_ZIGZAG_PERSISTENCE_H_ diff --git a/src/Zigzag_persistence/doc/zigzag_ex.png b/src/Zigzag_persistence/doc/zigzag_ex.png new file mode 100644 index 0000000000000000000000000000000000000000..a58cc6ed5470ec17ce16cbf34cb96a15a4972c9e GIT binary patch literal 2547 zcmZ{k2UJt(62~vH8pU-jh`6AD(w=025Dg$GQF<2wEK&pmL>6Kwf`n#*0-}q+Mu<}6 z&}%3vWK~*LT}2TLH4&sJ6s?$fsZ0R979$`iq&?N2#QO9ARchVO+nCQ1O&~ThaiJY z2$BuC*Wh>pY;5weu`q>%Nonm2U~LPrbPEHkrRXw$jW+@}uy89oGuR|dX7|r}zrfK% z;M(PjwT}SeY6z0_0k##mu50neC=j&O!^-rubL7D6(1TQEmwhkicP5xVlt|Qlw@XGM zM>fsDFDLr&rox$wcFh}c$$hj)5%J(%nu4s^^vZ(d(?_+Y?~a8 zsq_>o?&-`f;C=mc^igbT&Yv3x{qA?`xyA72AI&SWiY+GGr&EgE;kdRXcVc`?#AjOT zv4plH2T3jCQg=-AYI(15%ER5BhRc1va>f%T<=A?od9CxO6o^I|OXW1jZ#I^-&gW;> zy)QRfHRO3VVs!G`)-2tk9$V~ZT;Kj&qPw)qx|YcgUd(4}(dBF>){Co7^eWprK2Q7z z^|PnC5RxMYnwd+D5fy!~GxyguIO>E6tO7P$PU}b5t0bw3=;{f3T8DE4$(mGT>yn1I zDGBWGAQ3B`)O1jpF8B+TO;f9(nQe^*8D4o7zg1(Ooh>B&Rw3WlfT^(#Rw9OfGDG^1N}s)(dyBkm8rVbjs4E(IG#1-E zLZELuF+?wuoxw|T?_W7yJIH_E(@Y;2qy|!RYcNxP;s$~k%ggL#PE6K580XG8cpjl9 z24CLf(7c#5-`k4SxLb_Dlwz{#SJw*Q;bd0l3ciQFmoZ8v3hcVOMU#ym*QDAj{KOX)nub+DY2H^#b8>f>G1{28TE2Z^kP5YdlCczR zSH@JfJtwJF_BAHGldx-wGnYQsu1{n-TecZAU)#I9cB+aR47*;I@^|c_>%^$7c}qXzmV=@F^Hm^8ZD^MLy~?Y_s3 zzqV~&QTXn8{npentBYDIv`!%&NjDX=Y@rA<$>j@|q&jSgq4B+Yr^wQ!Xz$hq{}egJ_26gR$xo3$ z>02aH=Cct1E$pqc2L5C6@S>$@&09LLN}EtM`*t#WL=C-l=XpLunV~`DU~HOhM$8~r}AO5(wt2hw64Cmv9)Itbpn$t zBPut5OHsNgpnV|DY9B@mB78f@*D?hWB_yeXy2ANd$JSio_^n7@vdZip?Qp@EcTuXL z@a9K)u(!^FqEHTX5VV@za^=PAJdXM7?*cs^-lB{;xka817tf%I-^wtzix(YNMtcM3 zlrNg)3()SFBjR>H28}wID2_1%?QRvfdlWn zY{I;mpyl?s;^O4h$gxSv*TZD*@txm(BR8z3IoZ*R8Tekp zfdxV$kViEUdYVWbXB|BQ9esmi$KVKr0RmxBBklQ(fDr5#a3Sjd3v?H0AAsOr|H}{_ zK){8CV+kSOX^msmUvZbh0)m4e2UBxR9W9iTuKQE44OyAlo7NbgzxE&arSYo( literal 0 HcmV?d00001 diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h index 51fd7f1c54..a263aede5a 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h @@ -50,6 +50,8 @@ namespace zigzag_persistence { * \brief Class computating the zigzag persistent homology of a zigzag * filtration. Algorithm based on \cite zigzag_reflection. * + * \ingroup zigzag_persistence + * * \details The type ZigzagFilteredComplex::Simplex_key counts the number of * insertions and * deletions of simplices, which may be large in zigzag persistence and require diff --git a/src/common/doc/examples.h b/src/common/doc/examples.h index 1634b19e32..913d95b0f8 100644 --- a/src/common/doc/examples.h +++ b/src/common/doc/examples.h @@ -129,5 +129,8 @@ * @example example_one_skeleton_rips_from_distance_matrix.cpp * @example example_one_skeleton_rips_from_points.cpp * @example example_rips_complex_from_off_file.cpp + * \section Zigzag_persistence_example_section Zigzag_persistence + * @example example_simple_zigzag_filtration.cpp + * @example example_zzfiltration_from_file.cpp */ diff --git a/src/common/doc/main_page.md b/src/common/doc/main_page.md index cd173f6819..167c224aa0 100644 --- a/src/common/doc/main_page.md +++ b/src/common/doc/main_page.md @@ -383,6 +383,31 @@ +### Zigzag Persistent Homology + + + + + + + + + + +
+ \image html "zigzag_ex.png" + + Zigzag filtrations are a generalization of (standard) filtrations: the inclusion maps can not only go forward, + but also backward. In other words, simplices can also be removed from the complex which is build. + The implemented algorithm is from \cite zigzag. + + Author: Clément Maria, Hannah Schreiber
+ Introduced in: GUDHI 3.9.0
+ Copyright: MIT
+
+ User manual: \ref zigzag_persistence +
+ ## Topological descriptors tools {#TopologicalDescriptorsTools} ### Bottleneck distance From 688b3817e99052b9023bacae49db34ac6414b5cc Mon Sep 17 00:00:00 2001 From: hschreiber Date: Fri, 21 Jul 2023 19:55:36 +0200 Subject: [PATCH 05/51] restore cmake files --- CMakeLists.txt | 24 ++++++++++++------------ src/CMakeLists.txt | 2 +- src/cmake/modules/GUDHI_options.cmake | 1 - 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2113c71f42..4bb7e6cb03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,8 +16,8 @@ set(GUDHI_PYTHON_PATH "src/python") include(GUDHI_submodules) 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) + # 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) @@ -51,23 +51,23 @@ add_gudhi_module(Persistence_matrix) # Include module CMake subdirectories # GUDHI_SUB_DIRECTORIES is managed in CMAKE_MODULE_PATH/GUDHI_modules.cmake foreach(GUDHI_MODULE ${GUDHI_MODULES}) - foreach(GUDHI_SUB_DIRECTORY ${GUDHI_SUB_DIRECTORIES}) - if(EXISTS ${CMAKE_SOURCE_DIR}/src/${GUDHI_MODULE}/${GUDHI_SUB_DIRECTORY}/CMakeLists.txt) - add_subdirectory(src/${GUDHI_MODULE}/${GUDHI_SUB_DIRECTORY}/) - endif() - endforeach() + foreach(GUDHI_SUB_DIRECTORY ${GUDHI_SUB_DIRECTORIES}) + if(EXISTS ${CMAKE_SOURCE_DIR}/src/${GUDHI_MODULE}/${GUDHI_SUB_DIRECTORY}/CMakeLists.txt) + add_subdirectory(src/${GUDHI_MODULE}/${GUDHI_SUB_DIRECTORY}/) + endif() + endforeach() endforeach() if (WITH_GUDHI_UI) - add_subdirectory(src/GudhUI) + add_subdirectory(src/GudhUI) endif() if (WITH_GUDHI_PYTHON) - # specific for cython module - add_subdirectory(${GUDHI_PYTHON_PATH}) + # specific for cython module + add_subdirectory(${GUDHI_PYTHON_PATH}) else() - message("++ Python module will not be compiled because WITH_GUDHI_PYTHON is set to OFF") - set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python") + message("++ Python module will not be compiled because WITH_GUDHI_PYTHON is set to OFF") + set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python") endif() # For "make user_version" - Requires GUDHI_modules to be performed diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 608cdf663b..ba8f398bd9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -73,7 +73,7 @@ foreach(GUDHI_MODULE ${GUDHI_MODULES}) endforeach() endforeach() -if (WITH_GUDHI_UI) +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 11a1f2e1f8..8379e3c63f 100644 --- a/src/cmake/modules/GUDHI_options.cmake +++ b/src/cmake/modules/GUDHI_options.cmake @@ -5,7 +5,6 @@ option(WITH_GUDHI_PYTHON "Activate/deactivate python module compilation and inst 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_THIRD_PARTY "Activate/deactivate third party libraries cmake detection. When set to OFF, it is useful for doxygen or user_version i.e." ON) -option(WITH_GUDHI_UI "Activate/deactivate compilation of UI module." OFF) if (NOT WITH_GUDHI_THIRD_PARTY) set (WITH_GUDHI_BENCHMARK OFF) From f7087fc6a7748623dd5ce014141c57a8bad1bbde Mon Sep 17 00:00:00 2001 From: hschreiber Date: Fri, 21 Jul 2023 19:56:40 +0200 Subject: [PATCH 06/51] restore cmake files --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4bb7e6cb03..c122ed66d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,7 +58,7 @@ foreach(GUDHI_MODULE ${GUDHI_MODULES}) endforeach() endforeach() -if (WITH_GUDHI_UI) +if (WITH_GUDHI_THIRD_PARTY) add_subdirectory(src/GudhUI) endif() From 15fbc5302d9679758f20b7ab8829a0f30c1a82b3 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Fri, 21 Jul 2023 19:57:21 +0200 Subject: [PATCH 07/51] restore cmake files --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c122ed66d6..3021e9a14a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,7 +59,7 @@ foreach(GUDHI_MODULE ${GUDHI_MODULES}) endforeach() if (WITH_GUDHI_THIRD_PARTY) - add_subdirectory(src/GudhUI) + add_subdirectory(src/GudhUI) endif() if (WITH_GUDHI_PYTHON) From 4f06e5cf3367a79fbb14d145f0897e24eff82c65 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Fri, 28 Jul 2023 18:45:48 +0200 Subject: [PATCH 08/51] rips edge iterator --- src/Zigzag_persistence/example/CMakeLists.txt | 5 + .../example/comparison_for_tests.cpp | 109 +++ .../oscillating_rips_iterators.h | 718 ++++++++++++++++++ 3 files changed, 832 insertions(+) create mode 100644 src/Zigzag_persistence/example/comparison_for_tests.cpp create mode 100644 src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h diff --git a/src/Zigzag_persistence/example/CMakeLists.txt b/src/Zigzag_persistence/example/CMakeLists.txt index dd1f88e1d2..f0b424423d 100644 --- a/src/Zigzag_persistence/example/CMakeLists.txt +++ b/src/Zigzag_persistence/example/CMakeLists.txt @@ -13,6 +13,11 @@ endif() file(COPY "zigzag_filtration_example.txt" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) add_test(NAME Zigzag_persistence_example_zzfiltration_from_file COMMAND $ "${CMAKE_CURRENT_BINARY_DIR}/zigzag_filtration_example.txt") +add_executable ( comp comparison_for_tests.cpp ) +if(TARGET TBB::tbb) + target_link_libraries(comp TBB::tbb) +endif() + diff --git a/src/Zigzag_persistence/example/comparison_for_tests.cpp b/src/Zigzag_persistence/example/comparison_for_tests.cpp new file mode 100644 index 0000000000..13db7163fe --- /dev/null +++ b/src/Zigzag_persistence/example/comparison_for_tests.cpp @@ -0,0 +1,109 @@ +/* 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): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#include +#include +#include +#include +#include // for pair +#include +#include + +#include + +#include +#include +#include + +using ST = Gudhi::Simplex_tree; +using Filtration_value = ST::Filtration_value; +using OR = Gudhi::zigzag_persistence::Oscillating_rips_edge_range; +using ZE = Gudhi::zigzag_persistence::Zigzag_edge; +using Point = std::vector; + +void print_points(const std::vector& points){ + std::cout << "Number of points: " << points.size() << "\n"; + for (const Point& p : points){ + std::cout << "(" << p[0] << ", " << p[1] << ")\n"; + } + std::cout << "\n"; +} + +std::vector build_point_cloud(unsigned int numberOfPoints, int seed){ + std::vector finalPoints; + std::set points; + std::random_device dev; + std::mt19937 rng(dev()); + if (seed > -1) rng.seed(seed); + std::uniform_real_distribution dist(0,10); + + for (unsigned int i = 0; i < numberOfPoints; ++i){ + auto res = points.insert({dist(rng), dist(rng)}); + while(!res.second){ + res = points.insert({dist(rng), dist(rng)}); + } + finalPoints.push_back(*res.first); + } + + // print_points(finalPoints); + + return finalPoints; +} + +int main(int argc, char* const argv[]) { + if (argc != 4 && argc != 5) { + std::cout << "Usage: ./comp nu mu nomberOfPoints [seed]\n"; + return 0; + } + + double nu = std::stod(argv[1]); + double mu = std::stod(argv[2]); + unsigned int numberOfPoints = std::stoi(argv[3]); + int seed = -1; + + if (argc == 5) + seed = std::stoi(argv[4]); + + std::cout << "nu, mu: " << nu << ", " << mu << "\n"; + std::cout << "number of points: " << numberOfPoints << "\n"; + std::cout << "seed: " << seed << "\n"; + + std::vector points = build_point_cloud(numberOfPoints, seed); + + std::vector edges_v1 = OR::compute_oscillating_rips_edges(nu, mu, points, Gudhi::Euclidean_distance(), OR::Order_policy::FARTHEST_POINT_ORDERING); + std::cout << edges_v1.size() << "\n"; + + // unsigned int i = 0; + // for (const auto& e : OR::compute_oscillating_rips_edges_as_iterator(nu, mu, points, Gudhi::Euclidean_distance(), OR::Order_policy::FARTHEST_POINT_ORDERING)){ + // ++i; + // } + // std::cout << i << "\n"; + + // unsigned int i = 0; + // for (const auto& e : OR::compute_oscillating_rips_edges_as_iterator(nu, mu, points, Gudhi::Euclidean_distance(), OR::Order_policy::FARTHEST_POINT_ORDERING)){ + // if (i < edges_v1.size()){ + // if (!(edges_v1[i] == e)){ + // std::cout << "[" << i << "] different:\n"; + // std::cout << edges_v1[i].get_smallest_vertex() << ", " << edges_v1[i].get_biggest_vertex() << ", " << edges_v1[i].get_filtration_value() << ", " << edges_v1[i].get_direction() << "\n"; + // std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() << ", " << e.get_direction() << "\n"; + // }/* else { + // std::cout << "[" << i << "] same:\n"; + // std::cout << edges_v1[i].get_smallest_vertex() << ", " << edges_v1[i].get_biggest_vertex() << ", " << edges_v1[i].get_filtration_value() << ", " << edges_v1[i].get_direction() << "\n"; + // std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() << ", " << e.get_direction() << "\n"; + // } */ + // } else { + // std::cout << "[" << i << "] too long:\n"; + // std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() << ", " << e.get_direction() << "\n"; + // } + // ++i; + // } + + return 0; +} diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h new file mode 100644 index 0000000000..a84b7497dc --- /dev/null +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h @@ -0,0 +1,718 @@ +/* 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): Clément Maria and Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#ifndef ZIGZAG_OSCILLATING_RIPS_ITERATORS_H_ +#define ZIGZAG_OSCILLATING_RIPS_ITERATORS_H_ + +#include +#include +#include + +#include + +#ifdef GUDHI_USE_TBB +#include +#endif + +#include +#include +#include + +namespace Gudhi { +namespace zigzag_persistence { + +template +class Zigzag_edge { + public: + Zigzag_edge(size_t u, size_t v, Filtration_value fil, bool direction) + : u_(u), v_(v), fil_(fil), direction_(direction) { + if (u > v) std::swap(u_, v_); + } + + Zigzag_edge() : u_(0), v_(0), fil_(0), direction_(true) {} + + /* Returns vertex with smaller label. */ + size_t get_smallest_vertex() const { return u_; } + /* Returns vertex with bigger label. */ + size_t get_biggest_vertex() const { return v_; } + /* Returns the filtration value of the edge. */ + Filtration_value get_filtration_value() const { return fil_; } + /* Returns true if insertion of the edge, false if removal. */ + bool get_direction() const { return direction_; } + + void set(size_t u, size_t v, Filtration_value fil, bool direction){ + u_ = u; + v_ = v; + fil_ = fil; + direction_ = direction; + } + + bool operator==(const Zigzag_edge& e) const { + return ((e.u_ == u_) && (e.v_ == v_) && (e.fil_ == fil_) && (e.direction_ == direction_)); + } + + void assign_filtration(Filtration_value fil) { fil_ = fil; } + + private: + size_t u_; + size_t v_; + Filtration_value fil_; + bool direction_; +}; + +template +class Identity_edge_modifier { + public: + static constexpr bool isActive_ = false; + + private: + Identity_edge_modifier() {} +}; + +template +class Square_root_edge_modifier { + public: + static Filtration_value apply_modifier(Filtration_value f) { return std::sqrt(f); } + static Filtration_value apply_inverse_modifier(Filtration_value f) { return f * f; } + + static constexpr bool isActive_ = true; + + private: + Square_root_edge_modifier() {} +}; + +template > +class Oscillating_rips_edge_range { + private: + class Oscillating_rips_edge_iterator; + + public: + enum Order_policy { ALREADY_ORDERED, FARTHEST_POINT_ORDERING, RANDOM_POINT_ORDERING }; + + template + static std::vector > compute_oscillating_rips_edges(Filtration_value nu, + Filtration_value mu, + const PointRange& points, + DistanceFunction&& distance, + Order_policy orderPolicy) + { + std::vector > edgeFiltration; + std::vector epsilonValues; + std::vector > > distanceMatrix; + auto n = points.size(); + + initialize_(nu, mu, epsilonValues, distanceMatrix, points, distance, orderPolicy); + + // edgesAdded[i] (resp. edgesRemoved[i]) == list of edges (i,j), with j > > edgesAdded, edgesRemoved; + + // auto it = std::upper_bound(distanceMatrix[1].begin(), distanceMatrix[1].end(), + // std::pair(distanceMatrix.size(), mu * epsilonValues[0]), + // Point_distance_comp()); + // std::cout << "start vect colind: " << (it - distanceMatrix[1].begin()) << ", (" << it->first << ", " << it->second << "), (" << distanceMatrix[1].begin()->first << ", " << distanceMatrix[1].begin()->second << ")\n"; + size_t number_of_arrows = compute_edges_(nu, mu, epsilonValues, distanceMatrix, edgesAdded, edgesRemoved); + + // Now, sort edges according to lengths, and put everything in edgeFiltration + edgeFiltration.clear(); + edgeFiltration.reserve(number_of_arrows + n); // count edges + vertices additions + + // initialise R({p_0}, +infinity) + edgeFiltration.emplace_back(0, 0, // add vertex p_0,+infty + std::numeric_limits::infinity(), true); + // epsilonValues[0], true); + + if constexpr (EdgeModifier::isActive_) { + for (size_t i = 0; i < n - 1; ++i) { // all ascending arrows eps_i + edgeFiltration.emplace_back(i + 1, i + 1, EdgeModifier::apply_modifier(epsilonValues[i]), true); // add p_{i+1},eps_i + for (auto edg_it = edgesAdded[i].begin(); edg_it != edgesAdded[i].end(); ++edg_it) { + edgeFiltration.push_back(*edg_it); + } + for (auto edg_it = edgesRemoved[i].rbegin(); // longest first + edg_it != edgesRemoved[i].rend(); ++edg_it) { + edgeFiltration.push_back(*edg_it); + } + } + } else { + for (size_t i = 0; i < n - 1; ++i) { // all ascending arrows eps_i + edgeFiltration.emplace_back(i + 1, i + 1, epsilonValues[i], true); // add p_{i+1},eps_i + for (auto edg_it = edgesAdded[i].begin(); edg_it != edgesAdded[i].end(); ++edg_it) { + edgeFiltration.push_back(*edg_it); + } + for (auto edg_it = edgesRemoved[i].rbegin(); // longest first + edg_it != edgesRemoved[i].rend(); ++edg_it) { + edgeFiltration.push_back(*edg_it); + } + } + } + + // what remains is removed in the zigzag iterator with -infinity values. If eps_n-1 + //== 0, which is the usual case, the remaining simplices in the filtration are + // the n vertices. + // cannot inforce this here. + + return edgeFiltration; + } + + template + static boost::iterator_range compute_oscillating_rips_edges_as_iterator( + Filtration_value nu, + Filtration_value mu, + const PointRange& points, + DistanceFunction&& distance, + Order_policy orderPolicy) + { + auto start = Oscillating_rips_edge_iterator(nu, mu, points, distance, orderPolicy); + auto end = Oscillating_rips_edge_iterator(); + return boost::iterator_range( + start, end); + } + + private: + /* The two input types std::pair encode pairs + * (j, d(p_i,p_j)) and (k, d(p_i,p_k)) for some fixed point p_i. + * The operator() orders edges by length. By convention, if lengths are equal, + * it orders pairs by taking the smaller vertex label between j and k. + */ + struct Point_distance_comp { + bool operator()(const std::pair& p1, const std::pair& p2) const { + { + if (p1.second != p2.second) { + return p1.second < p2.second; // shorter first + } + return p1.first < p2.first; + } + } + }; + + class Oscillating_rips_edge_iterator + : public boost::iterator_facade&, + boost::forward_traversal_tag> { + public: + template + Oscillating_rips_edge_iterator(Filtration_value nu, + Filtration_value mu, + const PointRange& points, + DistanceFunction&& distance, + Order_policy orderPolicy) + : nu_(nu), mu_(mu), currentEdge_(0, 0, std::numeric_limits::infinity(), true), isEnd_(false), epsValIndex_(0), rowIndex_(1), inPosEd_(true), insertVertex_(true) + { + initialize_(nu_, mu_, epsilonValues_, distanceMatrix_, points, distance, orderPolicy); + auto it = std::upper_bound(distanceMatrix_[1].begin(), distanceMatrix_[1].end(), + std::pair(distanceMatrix_.size(), mu_ * epsilonValues_[epsValIndex_]), + Point_distance_comp()); + columnIndex_ = it - distanceMatrix_[1].begin(); + // std::cout << "start colind: " << columnIndex_ << ", (" << it->first << ", " << it->second << "), (" << distanceMatrix_[1].begin()->first << ", " << distanceMatrix_[1].begin()->second << ")\n"; + } + + template + Oscillating_rips_edge_iterator(Filtration_value nu, Filtration_value mu, const PointRange& orderedPoints, + DistanceFunction&& distance, const std::vector& epsilonValues) + : epsilonValues_(epsilonValues), + nu_(nu), + mu_(mu), + currentEdge_(0, 0, std::numeric_limits::infinity(), true), + isEnd_(false), epsValIndex_(0), rowIndex_(1), inPosEd_(true), insertVertex_(true) { + GUDHI_CHECK(orderedPoints.size() == epsilonValues.size(), + "The number of points and the number of epsilon values should match."); + GUDHI_CHECK((nu <= mu) && (nu >= 0), "Invalid parameters mu and nu"); + + if constexpr (EdgeModifier::isActive_) { + nu_ = EdgeModifier::apply_inverse_modifier(nu); + mu_ = EdgeModifier::apply_inverse_modifier(mu); + } + + // compute the distance matrix + distanceMatrix_ = compute_distance_matrix_(orderedPoints, distance); + + auto it = std::upper_bound(distanceMatrix_[1].begin(), distanceMatrix_[1].end(), + std::pair(distanceMatrix_.size(), mu_ * epsilonValues_[epsValIndex_]), + Point_distance_comp()); + columnIndex_ = it - distanceMatrix_[1].begin(); + } + + Oscillating_rips_edge_iterator() : nu_(0), mu_(0), currentEdge_(0, 0, 0, true), isEnd_(true), epsValIndex_(0), rowIndex_(1), columnIndex_(0), inPosEd_(true), insertVertex_(true) {} + + private: + friend class boost::iterator_core_access; + + std::vector epsilonValues_; + std::vector > > distanceMatrix_; + Filtration_value nu_; + Filtration_value mu_; + Zigzag_edge currentEdge_; + bool isEnd_; //to replace + size_t epsValIndex_, rowIndex_, columnIndex_; + bool inPosEd_, insertVertex_; //to replace + + bool equal(Oscillating_rips_edge_iterator const& other) const { return isEnd_ == other.isEnd_ && currentEdge_ == other.currentEdge_; } + + const Zigzag_edge& dereference() const { return currentEdge_; } + + void increment() { + if (epsValIndex_ < distanceMatrix_.size() - 1) { + if (insertVertex_) { + currentEdge_.set(epsValIndex_ + 1, epsValIndex_ + 1, epsilonValues_[epsValIndex_], true); + insertVertex_ = false; + return; + } + + while ((epsValIndex_ < distanceMatrix_.size() - 1 && inPosEd_ && col_index_is_not_valid_pos()) || + (epsValIndex_ < distanceMatrix_.size() - 1 && !inPosEd_ && col_index_is_not_valid_neg())) { + if (inPosEd_) { + ini_col_index_pos(); + } else { + ini_col_index_neg(); + if (epsValIndex_ == distanceMatrix_.size() - 1) { + set_end_(); + return; + } + if (insertVertex_) { + currentEdge_.set(epsValIndex_ + 1, epsValIndex_ + 1, epsilonValues_[epsValIndex_], true); + insertVertex_ = false; + return; + } + } + } + + if (inPosEd_) { + up_edge_pos(epsValIndex_); + if (rowIndex_ == epsValIndex_ + 2) { + inPosEd_ = false; + --rowIndex_; + ini_col_index_low(); + } + return; + } + + up_edge_neg(epsValIndex_); + if (rowIndex_ == 0) { + ++epsValIndex_; + if (epsValIndex_ == distanceMatrix_.size() - 1) return; + insertVertex_ = true; + inPosEd_ = true; + ++rowIndex_; + ini_col_index_up(); + } + return; + } + + set_end_(); + } + + void set_end_(){ + isEnd_ = true; + currentEdge_ = Zigzag_edge(); + } + + void ini_col_index_up() { + auto it = + std::upper_bound(distanceMatrix_[rowIndex_].begin(), distanceMatrix_[rowIndex_].end(), + std::pair(distanceMatrix_.size(), mu_ * epsilonValues_[epsValIndex_]), + Point_distance_comp()); + columnIndex_ = it - distanceMatrix_[rowIndex_].begin(); + } + + void ini_col_index_low() { + auto it = + std::lower_bound(distanceMatrix_[rowIndex_].begin(), distanceMatrix_[rowIndex_].end(), + std::pair(0, nu_ * epsilonValues_[epsValIndex_ + 1]), + Point_distance_comp()); + while (it != distanceMatrix_[rowIndex_].end() && it->second == nu_ * epsilonValues_[epsValIndex_ + 1]) ++it; + columnIndex_ = it - distanceMatrix_[rowIndex_].begin(); + } + + void ini_col_index_pos(){ + while (col_index_is_not_valid_pos() && rowIndex_ <= epsValIndex_) { + ++rowIndex_; + ini_col_index_up(); + } + if (col_index_is_not_valid_pos()){ + ini_col_index_low(); + inPosEd_ = false; + } + } + + void ini_col_index_neg(){ + while (col_index_is_not_valid_neg() && rowIndex_ > 1) { + --rowIndex_; + ini_col_index_low(); + } + if (col_index_is_not_valid_neg()){ + ini_col_index_up(); + ++epsValIndex_; + insertVertex_ = true; + inPosEd_ = true; + } + } + + bool col_index_is_not_valid_pos() { + return columnIndex_ == 0 || + (rowIndex_ != (epsValIndex_ + 1) && + distanceMatrix_[rowIndex_][columnIndex_ - 1].second <= nu_ * epsilonValues_[epsValIndex_]); + } + + bool col_index_is_not_valid_neg() { + return columnIndex_ == distanceMatrix_[rowIndex_].size() || + distanceMatrix_[rowIndex_][columnIndex_].second > mu_ * epsilonValues_[epsValIndex_]; + } + + void up_edge_pos(size_t i) { + --columnIndex_; + if constexpr (EdgeModifier::isActive_) + currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, rowIndex_, EdgeModifier::apply_modifier(epsilonValues_[i]), true); + else + currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, rowIndex_, epsilonValues_[i], true); + // std::cout << "pos: " << rowIndex_ << ", " << columnIndex_ << ", " << distanceMatrix_[rowIndex_][columnIndex_].first << "\n"; + while (col_index_is_not_valid_pos() && rowIndex_ <= i) { + ++rowIndex_; + ini_col_index_up(); + } + if (col_index_is_not_valid_pos()) { + ++rowIndex_; + } + } + + void up_edge_neg(size_t i) { + if constexpr (EdgeModifier::isActive_) + currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, rowIndex_, EdgeModifier::apply_modifier(epsilonValues_[i]), false); + else + currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, rowIndex_, epsilonValues_[i], false); + // std::cout << "neg: " << rowIndex_ << ", " << columnIndex_ << ", " << distanceMatrix_[rowIndex_][columnIndex_].first << "\n"; + ++columnIndex_; + while (col_index_is_not_valid_neg() && rowIndex_ > 1) { + --rowIndex_; + ini_col_index_low(); + } + if (col_index_is_not_valid_neg()) { + --rowIndex_; + } + } + }; + + template + static void initialize_(Filtration_value& nu, Filtration_value& mu, std::vector& epsilonValues, + std::vector > >& distanceMatrix, + const PointRange& points, DistanceFunction&& distance, Order_policy orderPolicy) { + GUDHI_CHECK((nu <= mu) && (nu >= 0), "Invalid parameters mu and nu"); + + size_t n = points.size(); // number of points + PointRange sortedPoints; + sortedPoints.reserve(n); + + if constexpr (EdgeModifier::isActive_) { + nu = EdgeModifier::apply_inverse_modifier(nu); + mu = EdgeModifier::apply_inverse_modifier(mu); + } + + // compute epsilon values + if (orderPolicy == Order_policy::ALREADY_ORDERED) { + sortedPoints.assign(points.begin(), points.end()); + epsilonValues = compute_epsilon_values_(sortedPoints, distance); + } else if (orderPolicy == Order_policy::FARTHEST_POINT_ORDERING) { + epsilonValues.reserve(n); + Gudhi::subsampling::choose_n_farthest_points(distance, points, + n, // final size + 0, // starting point + std::back_inserter(sortedPoints), + std::back_inserter(epsilonValues)); + // need to shift values output by subsampling: + for (unsigned int i = 1; i < n; ++i) { + epsilonValues[i - 1] = epsilonValues[i]; + } + epsilonValues[n - 1] = 0; + } else { + Gudhi::subsampling::pick_n_random_points(points, n, std::back_inserter(sortedPoints)); + epsilonValues = compute_epsilon_values_(sortedPoints, distance); + } + + // compute the distance matrix + distanceMatrix = compute_distance_matrix_(sortedPoints, distance); + } + + /** \brief Compute the epsilon values for an ordered set of points, measuring the + * sparsity of the ordering. + * + * \details Let \f$P = \{p_0, \ldots, p_{n-1}\f$ be the ordered set of points. Then + * the method sets eps_range[i]<\CODE> with the value \$f\varepsilon_i\$f, + * defined as \f$\varpesilon_i = d_H(P_i,P)\f$, the Hausdorff between the points + * \f$P_i= \{p_0, \ldots, p_{i}\}\f$ and the entire point cloud + * \f$P = \{p_0, \ldots, p_{n-1}\}\f$. + * + * @param[in] points Range of points. + * @param[in] distance Distance function that can be called on two points. + * @param[in] eps_range Vector in which the epsilon values are written, + * eps_range[i]<\CODE> is \$f\varepsilon_i\$f. + * Satisfyies epsilonValues[i] >= + * epsilonValues[j] >= 0<\CODE> whenever i>=j<\CODE>. + * The range must be of same size as the number of points. + */ + template + static std::vector compute_epsilon_values_(const PointRange& points, DistanceFunction&& distance) { + size_t n = points.size(); + std::vector eps_range(n, std::numeric_limits::infinity()); + + // compute all \f$\varepsilon_i\f$ values, such that eps_range[i] == + // eps_i==d_H(P_i,P), for i=0 ... n-1: + for (size_t i = 0; i < n; ++i) { + // entering step i, maintain eps_range[j] = eps_j for j= i. +#ifdef GUDHI_USE_TBB + tbb::parallel_for(size_t(i + 1), n, [&](size_t k) { + // set eps_range[k] <- d(p_k, P_i) == + // min{ d(p_k, P_{i-1}), d(p_k, p_i) } for k >= i. + double dist_i_k = distance(points[i], points[k]); + if (dist_i_k < eps_range[k]) { + eps_range[k] = dist_i_k; + } + }); +#else + for (size_t k = i + 1; k < n; ++k) { + // set eps_range[k] <- d(p_k, P_i) == + // min{ d(p_k, P_{i-1}), d(p_k, p_i) } for k >= i. + double dist_i_k = distance(points[i], points[k]); + if (dist_i_k < eps_range[k]) { + eps_range[k] = dist_i_k; + } + } +#endif + // we have now eps_range[k] = d(p_k, P_i) for k > i. + // to do: implement parallel version by dividing the vector + // set eps_range[i] <- eps_i = d_H(P_i,P) = max_{k>i} d(p_k, P_i) + double eps_i = 0.; + for (size_t k = i + 1; k < n; ++k) { + if (eps_range[k] > eps_i) { + eps_i = eps_range[k]; + } + } + eps_range[i] = eps_i; + } + + return eps_range; + } + + template + static std::vector > > compute_distance_matrix_( + const PointRange& sortedPoints, DistanceFunction&& distance) { + std::vector > > distanceMatrix(sortedPoints.size()); +#ifdef GUDHI_USE_TBB + tbb::parallel_for(size_t(0), sortedPoints.size(), [&](size_t i) { + // distanceMatrix[i] = std::vector< std::pair >(); + distanceMatrix[i].resize(i); + for (size_t j = 0; j < i; ++j) { + distanceMatrix[i][j] = std::make_pair(j, distance(sortedPoints[i], sortedPoints[j])); + } + // distanceMatrix[i] is sorted by (j, d(p_i,p_j)) < (k, d(p_i,p_k)) iff + // d(p_i,p_j) < d(p_i,p_k) or (j >(); + distanceMatrix[i].resize(i); + for (size_t j = 0; j < i; ++j) { + distanceMatrix[i][j] = std::make_pair(j, distance(sortedPoints[i], sortedPoints[j])); + } + std::stable_sort(distanceMatrix[i].begin(), distanceMatrix[i].end(), Point_distance_comp()); + } +#endif + + return distanceMatrix; + } + + static size_t compute_edges_(Filtration_value nu, Filtration_value mu, + const std::vector& epsilonValues, + std::vector > >& distanceMatrix, + std::vector > >& edgesAdded, + std::vector > >& edgesRemoved) + { + size_t number_of_arrows = 0; + auto n = epsilonValues.size(); + edgesAdded.resize(n); + edgesRemoved.resize(n); + + // edgesAdded[i] must contain all new edges (k,j), 0 <= k < j <= i+1, + // inserted in inclusion: + // R({p_0, ... , p_i}, nu * eps_i) -> R({p_0, ... , p_i, p_i+1 }, mu * eps_i) + // and + // edgesRemoved[i] must contain all edges (k,j), 0 <= k < j <= i+1, + // removed in backward inclusion: + // R({p_0, ... , p_{i+1}}, mu * eps_i) <- R({p_0, ... , p_{i+1}}, nu * eps_{i+1}) +#ifdef GUDHI_USE_TBB + // no need to consider the case i=n-1 in an oRzz filtration + tbb::parallel_for(size_t(0), n - 1, [&](size_t i) { + typename std::vector >::iterator it; + //----edgesAdded[i]: + // consider first all edges added in inclusion: + // R({p_0, ... , p_i}, nu * eps_i) -> R({p_0, ... , p_i}, mu * eps_i), + // i.e., all (p_j,p_k) with 0 <= k < j <= i with + // nu eps_i < d(p_j,p_k) <= mu eps_i + // these new edges get filtration value epsilonValues[i] + for (size_t j = 1; j <= i; ++j) { + // get very first edge (k,j), over all k(n, mu * epsilonValues[i]), Point_distance_comp()); + + while (it != distanceMatrix[j].begin()) { + --it; + // if edge already in R({p_0, ... , p_i}, nu * eps_i), stop + if (it->second <= nu * epsilonValues[i]) { + break; + } + if constexpr (EdgeModifier::isActive_){ + edgesAdded[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), true); + } else { + edgesAdded[i].emplace_back(it->first, j, epsilonValues[i], true); + } + // std::cout << j << ", " << (it - distanceMatrix[j].begin()) << ", " << it->first << "\n"; + ++number_of_arrows; + } + } + // now consider all edges added in inclusion: + // R({p_0, ... , p_i}, mu * eps_i) -> R({p_0, ... , p_i, p_i+1}, mu * eps_i) + // i.e., all (p_j,p_i+1) with 0 <= j <= i with d(p_j,p_i+1) <= mu eps_i + // these new edges get filtration value epsilonValues[i] + // first striclty longer edge + it = std::upper_bound(distanceMatrix[i + 1].begin(), distanceMatrix[i + 1].end(), + std::pair(n, mu * epsilonValues[i]), Point_distance_comp()); + + while (it != distanceMatrix[i + 1].begin()) { + --it; + if constexpr (EdgeModifier::isActive_) { + edgesAdded[i].emplace_back(it->first, i + 1, EdgeModifier::apply_modifier(epsilonValues[i]), true); + } else { + edgesAdded[i].emplace_back(it->first, i + 1, epsilonValues[i], true); + } + // std::cout << (i + 1) << ", " << (it - distanceMatrix[i+1].begin()) << ", " << it->first << "\n"; + ++number_of_arrows; + } + + //----edgesRemoved[i]: + // consider all edges removed in + // R({p_0, ... , p_{i+1}}, mu * eps_i) <- R({p_0, ... , p_{i+1}}, nu * eps_{i+1}) + // i.e., all edges (p_k,p_j), 0<=k(n, mu * epsilonValues[i]), Point_distance_comp()); + + while (it != distanceMatrix[j].begin()) { + --it; + // when reading an edge in R({p_0, ... , p_{i+1}}, nu * eps_{i+1}), stop + if (it->second <= nu * epsilonValues[i + 1]) { + break; + } + // edgesRemoved[i].emplace_back(it->first, j, epsilonValues[i+1], false); + if constexpr (EdgeModifier::isActive_){ + edgesRemoved[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), false); + } else { + edgesRemoved[i].emplace_back(it->first, j, epsilonValues[i], false); + } + // std::cout << j << ", " << (it - distanceMatrix[j].begin()) << ", " << it->first << "\n"; + ++number_of_arrows; + } + } + }); +#else // GUDHI_USE_TBB not defined + + typename std::vector >::iterator it; + + for (size_t i = 0; i < n - 1; ++i) { + //----edgesAdded[i]: + // consider first all edges added in inclusion: + // R({p_0, ... , p_i}, nu * eps_i) -> R({p_0, ... , p_i}, mu * eps_i), + // i.e., all (p_j,p_k) with 0 <= k < j <= i with + // nu eps_i < d(p_j,p_k) <= mu eps_i + // these new edges get filtration value epsilonValues[i] + for (size_t j = 1; j <= i; ++j) { + // get very first edge (k,j), over all k(n, mu * epsilonValues[i]), Point_distance_comp()); + + while (it != distanceMatrix[j].begin()) { + --it; + // if edge already in R({p_0, ... , p_i}, nu * eps_i), stop + if (it->second <= nu * epsilonValues[i]) { + break; + } + if constexpr (EdgeModifier::isActive_){ + edgesAdded[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), true); + } else { + edgesAdded[i].emplace_back(it->first, j, epsilonValues[i], true); + } + ++number_of_arrows; + } + } + // now consider all edges added in inclusion: + // R({p_0, ... , p_i}, mu * eps_i) -> R({p_0, ... , p_i, p_i+1}, mu * eps_i) + // i.e., all (p_j,p_i+1) with 0 <= j <= i with d(p_j,p_i+1) <= mu eps_i + // these new edges get filtration value epsilonValues[i] + // first striclty longer edge + it = std::upper_bound(distanceMatrix[i + 1].begin(), distanceMatrix[i + 1].end(), + std::pair(n, mu * epsilonValues[i]), Point_distance_comp()); +// if (i == 0){ +// std::cout << "start vect colind2: " << (it - distanceMatrix[1].begin()) << ", (" << it->first << ", " << it->second << "), (" << distanceMatrix[1].begin()->first << ", " << distanceMatrix[1].begin()->second << ")\n"; +// } + while (it != distanceMatrix[i + 1].begin()) { + --it; + if constexpr (EdgeModifier::isActive_) { + edgesAdded[i].emplace_back(it->first, i + 1, EdgeModifier::apply_modifier(epsilonValues[i]), true); + } else { + edgesAdded[i].emplace_back(it->first, i + 1, epsilonValues[i], true); + // if (i==0) std::cout << "added: " << it->first << ", " << (i + 1) << ", " << epsilonValues[i] << "\n"; + } + ++number_of_arrows; + } + + //----edgesRemoved[i]: + // consider all edges removed in + // R({p_0, ... , p_{i+1}}, mu * eps_i) <- R({p_0, ... , p_{i+1}}, nu * eps_{i+1}) + // i.e., all edges (p_k,p_j), 0<=k(n, mu * epsilonValues[i]), Point_distance_comp()); + + while (it != distanceMatrix[j].begin()) { + --it; + // when reading an edge in R({p_0, ... , p_{i+1}}, nu * eps_{i+1}), stop + if (it->second <= nu * epsilonValues[i + 1]) { + break; + } + // edgesRemoved[i].emplace_back(it->first, j, epsilonValues[i+1], false); + if constexpr (EdgeModifier::isActive_){ + edgesRemoved[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), false); + } else { + edgesRemoved[i].emplace_back(it->first, j, epsilonValues[i], false); + } + ++number_of_arrows; + } + } + } +#endif + + return number_of_arrows; + } +}; + +} // namespace zigzag_persistence +} // namespace Gudhi + +#endif // ZIGZAG_OSCILLATING_RIPS_ITERATORS_H_ From 5e530f782502ecac1e677c0eeafe12e7adc3613e Mon Sep 17 00:00:00 2001 From: hschreiber Date: Fri, 4 Aug 2023 18:59:37 +0200 Subject: [PATCH 09/51] oscillating rips range --- src/Simplex_tree/include/gudhi/Simplex_tree.h | 32 - .../gudhi/Simplex_tree/hooks_simplex_base.h | 1 + .../example/comparison_for_tests.cpp | 341 ++++++-- .../oscillating_rips_iterators.h | 744 ++++++++++++------ 4 files changed, 791 insertions(+), 327 deletions(-) diff --git a/src/Simplex_tree/include/gudhi/Simplex_tree.h b/src/Simplex_tree/include/gudhi/Simplex_tree.h index 0594f7f479..c03ba65ea9 100644 --- a/src/Simplex_tree/include/gudhi/Simplex_tree.h +++ b/src/Simplex_tree/include/gudhi/Simplex_tree.h @@ -2510,38 +2510,6 @@ struct Simplex_tree_options_negative_indexation { static const bool simplex_handle_strong_validity = false; }; -/** Model of SimplexTreeOptions, same as `Simplex_tree_options_full_featured` - * but the possibility of much bigger keys and in particular of negative keys. - * - * Maximum number of simplices to compute persistence is std::numeric_limits::max() - * (way enough simplices). */ -struct Simplex_tree_options_wide_indexation { - typedef linear_indexing_tag Indexing_tag; - typedef int Vertex_handle; - typedef double Filtration_value; - typedef std::int64_t Simplex_key; - static const bool store_key = true; - static const bool store_filtration = true; - static const bool contiguous_vertices = false; - static const bool link_nodes_by_label = false; -}; - -/** Model of SimplexTreeOptions, same as `Simplex_tree_options_full_featured` - * but with the possibility of negative keys. - * - * Maximum number of simplices to compute persistence is std::numeric_limits::max() - * (about 2 billions of simplices). */ -struct Simplex_tree_options_negative_indexation { - typedef linear_indexing_tag Indexing_tag; - typedef int Vertex_handle; - typedef double Filtration_value; - typedef std::int32_t Simplex_key; - static const bool store_key = true; - static const bool store_filtration = true; - static const bool contiguous_vertices = false; - static const bool link_nodes_by_label = false; -}; - /** Model of SimplexTreeOptions, faster than `Simplex_tree_options_full_featured` but note the unsafe * `contiguous_vertices` option. * diff --git a/src/Simplex_tree/include/gudhi/Simplex_tree/hooks_simplex_base.h b/src/Simplex_tree/include/gudhi/Simplex_tree/hooks_simplex_base.h index 25cb794dfa..d000bcfc6e 100644 --- a/src/Simplex_tree/include/gudhi/Simplex_tree/hooks_simplex_base.h +++ b/src/Simplex_tree/include/gudhi/Simplex_tree/hooks_simplex_base.h @@ -12,6 +12,7 @@ #define SIMPLEX_TREE_HOOKS_SIMPLEX_BASE_H_ #include +#include namespace Gudhi { /** \brief No hook when SimplexTreeOptions::link_nodes_by_label is false. diff --git a/src/Zigzag_persistence/example/comparison_for_tests.cpp b/src/Zigzag_persistence/example/comparison_for_tests.cpp index 13db7163fe..bca280febf 100644 --- a/src/Zigzag_persistence/example/comparison_for_tests.cpp +++ b/src/Zigzag_persistence/example/comparison_for_tests.cpp @@ -9,101 +9,290 @@ */ #include +#include #include #include #include #include // for pair #include -#include +#include #include #include #include #include +#include -using ST = Gudhi::Simplex_tree; +using ST = Gudhi::Simplex_tree; using Filtration_value = ST::Filtration_value; -using OR = Gudhi::zigzag_persistence::Oscillating_rips_edge_range; +using Square = Gudhi::zigzag_persistence::Square_root_edge_modifier; using ZE = Gudhi::zigzag_persistence::Zigzag_edge; +using ORE = Gudhi::zigzag_persistence::Oscillating_rips_edge_range; +using ORi = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range; +using ORv = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range::iterator>; using Point = std::vector; -void print_points(const std::vector& points){ - std::cout << "Number of points: " << points.size() << "\n"; - for (const Point& p : points){ - std::cout << "(" << p[0] << ", " << p[1] << ")\n"; - } - std::cout << "\n"; +void print_points(const std::vector& points) { + std::cout << "Number of points: " << points.size() << "\n"; + for (const Point& p : points) { + std::cout << "(" << p[0] << ", " << p[1] << ")\n"; + } + std::cout << "\n"; +} + +std::vector build_point_cloud(unsigned int numberOfPoints, int seed) { + std::vector finalPoints; + std::set points; + std::random_device dev; + std::mt19937 rng(dev()); + if (seed > -1) rng.seed(seed); + std::uniform_real_distribution dist(0, 10); + + for (unsigned int i = 0; i < numberOfPoints; ++i) { + auto res = points.insert({dist(rng), dist(rng)}); + while (!res.second) { + res = points.insert({dist(rng), dist(rng)}); + } + finalPoints.push_back(*res.first); + } + + // print_points(finalPoints); + + return finalPoints; +} + +void canonical_sort_edge(std::vector& edges) { + // canonical sort of the edges: as much as possible, edges should be removed in + // the reverse order of their insertion. We decide to insert shorted edges first, + // with increasing lexicographical order, and remove larger edges first, with + // decreasing lexicographic order. + + // filtration then dimension, then lex order for insertion + auto edge_cmp = [](const ZE& e1, const ZE& e2) { + if (e1.get_filtration_value() != e2.get_filtration_value()) { + return e1.get_filtration_value() < e2.get_filtration_value(); + } // lower fil first + + if (e1.get_smallest_vertex() == e1.get_biggest_vertex()) { // e1 is a vertex, -> put vertices first + if (e2.get_smallest_vertex() == e2.get_biggest_vertex()) { + return e1.get_smallest_vertex() < e2.get_smallest_vertex(); + } //-> vertex of lower label + else { + return true; + } //-> always vertices before edges + } else { // e1 is an edge + if (e2.get_smallest_vertex() == e2.get_biggest_vertex()) { + return false; + } // e2 vertex, -> put it first + else { // both are edges, lexigraphic compare + if (e1.get_smallest_vertex() != e2.get_smallest_vertex()) { + return e1.get_smallest_vertex() < e2.get_smallest_vertex(); + } // lex order + if (e1.get_biggest_vertex() != e2.get_biggest_vertex()) { + return e1.get_biggest_vertex() < e2.get_biggest_vertex(); + } + return false; // equality + } + } + }; + // the inverse ordering for deletions + auto inv_edge_cmp = [&](const ZE& e1, const ZE& e2) { + if (e1.get_smallest_vertex() == e2.get_smallest_vertex() && e1.get_biggest_vertex() == e2.get_biggest_vertex()) { + return false; + } //== => false + return !(edge_cmp(e1, e2)); // reverse order + }; + // sort sequences of inclusions of same filtration with edge_cmp + // sort sequences of removals of same filtration with inv_edge_cmp + auto beg = edges.begin(); + auto end = edges.begin(); + auto curr_fil = beg->get_filtration_value(); + auto curr_type = beg->get_direction(); + while (beg != edges.end()) { + while (end != edges.end() && end->get_filtration_value() == curr_fil && end->get_direction() == curr_type) { + ++end; + } + if (curr_type) { + sort(beg, end, edge_cmp); + } // sequence of insertions + else { + sort(beg, end, inv_edge_cmp); + } // sequence of removals + beg = end; + curr_fil = beg->get_filtration_value(); + curr_type = beg->get_direction(); + } +} + +void test_edges_comp(const std::vector& points, double nu, double mu, ORE::Order_policy p) +{ + std::vector edges_v1 = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); + + unsigned int i = 0; + for (const auto& e : ORE::get_iterator_range(nu, mu, points, Gudhi::Euclidean_distance(), p)) { + if (i < edges_v1.size()) { + if (!(edges_v1[i] == e)) { + std::cout << "[" << i << "] different:\n"; + std::cout << edges_v1[i].get_smallest_vertex() << ", " << edges_v1[i].get_biggest_vertex() << ", " + << edges_v1[i].get_filtration_value() << ", " << edges_v1[i].get_direction() << "\n"; + std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() + << ", " << e.get_direction() << "\n"; + }/* else { + std::cout << "[" << i << "] same:\n"; + std::cout << edges_v1[i].get_smallest_vertex() << ", " << edges_v1[i].get_biggest_vertex() << ", " + << edges_v1[i].get_filtration_value() << ", " << edges_v1[i].get_direction() << "\n"; + std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() + << ", " << e.get_direction() << "\n"; + } */ + } else { + std::cout << "[" << i << "] too long:\n"; + std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() << ", " + << e.get_direction() << "\n"; + } + ++i; + } +} + +void test_edges_canonical_sort(const std::vector& points, double nu, double mu, ORE::Order_policy p) { + std::vector edges_v1 = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); + std::vector ordered_edges_v1(edges_v1); + canonical_sort_edge(ordered_edges_v1); + + for (unsigned int i = 0; i < edges_v1.size(); ++i) { + if (!(edges_v1[i] == ordered_edges_v1[i])) { + std::cout << "[" << i << "] different:\n"; + std::cout << edges_v1[i].get_smallest_vertex() << ", " << edges_v1[i].get_biggest_vertex() << ", " + << edges_v1[i].get_filtration_value() << ", " << edges_v1[i].get_direction() << "\n"; + std::cout << ordered_edges_v1[i].get_smallest_vertex() << ", " << ordered_edges_v1[i].get_biggest_vertex() << ", " + << ordered_edges_v1[i].get_filtration_value() << ", " << ordered_edges_v1[i].get_direction() << "\n"; + } + } +} + +void test_edges_asymetry(const std::vector& points, double nu, double mu, ORE::Order_policy p) { + auto comp = [](const ZE& e1, const ZE& e2) { + if (e1.get_smallest_vertex() != e2.get_smallest_vertex()) + return e1.get_smallest_vertex() < e2.get_smallest_vertex(); + return e1.get_biggest_vertex() < e2.get_biggest_vertex(); + }; + std::set > curedges(comp); + for (const auto& e : ORE::get_iterator_range(nu, mu, points, Gudhi::Euclidean_distance(), p)) { + if (e.get_direction()) + curedges.insert(e); + else + curedges.erase(e); + } + std::cout << "final state: " << curedges.size() << "\n"; + for (const auto& e : curedges) { + std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() << ", " + << e.get_direction() << "\n"; + } +} + +void test_edges_timings(const std::vector& points, double nu, double mu, ORE::Order_policy p) { +// { +// Gudhi::Clock time1("Vector version"); +// std::vector edges_v1 = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); +// std::cout << edges_v1.size() << "\n"; +// time1.end(); +// std::cout << time1; +// } + + { + Gudhi::Clock time2("Iterator version"); + unsigned int i = 0; + for ([[maybe_unused]] const auto& e : ORE::get_iterator_range(nu, mu, points, Gudhi::Euclidean_distance(), p)) { + ++i; + } + std::cout << i << "\n"; + time2.end(); + std::cout << time2; + } + + { + Gudhi::Clock time1("Vector version"); + std::vector edges_v1 = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); + std::cout << edges_v1.size() << "\n"; + time1.end(); + std::cout << time1; + } +} + +void test_edges(const std::vector& points, double nu, double mu) { + // ORE::Order_policy p = ORE::Order_policy::FARTHEST_POINT_ORDERING; + // ORE::Order_policy p = ORE::Order_policy::ALREADY_ORDERED; + ORE::Order_policy p = ORE::Order_policy::RANDOM_POINT_ORDERING; + + // test_edges_comp(points, nu, mu, p); + // test_edges_canonical_sort(points, nu, mu, p); + // test_edges_asymetry(points, nu, mu, p); + test_edges_timings(points, nu, mu, p); } -std::vector build_point_cloud(unsigned int numberOfPoints, int seed){ - std::vector finalPoints; - std::set points; - std::random_device dev; - std::mt19937 rng(dev()); - if (seed > -1) rng.seed(seed); - std::uniform_real_distribution dist(0,10); - - for (unsigned int i = 0; i < numberOfPoints; ++i){ - auto res = points.insert({dist(rng), dist(rng)}); - while(!res.second){ - res = points.insert({dist(rng), dist(rng)}); - } - finalPoints.push_back(*res.first); - } - - // print_points(finalPoints); - - return finalPoints; +void test_simplices_print(const std::vector& points, double nu, double mu, int maxDim, ORE::Order_policy p) { + ST st; + + auto start = ORE::begin(nu, mu, points, Gudhi::Euclidean_distance(), p); + auto end = ORE::end(); + for (auto& t : ORi::get_iterator_range(start, end, st, maxDim)) { + for (auto v : st.simplex_vertex_range(std::get<0>(t))) std::cout << v << " "; + std::cout << " -- " << std::get<1>(t) << ", " << std::get<2>(t) << "\n"; + } +} + +void test_simplices_comp(const std::vector& points, double nu, double mu, int maxDim, ORE::Order_policy p){ + ST st; + + auto startEIt = ORE::begin(nu, mu, points, Gudhi::Euclidean_distance(), p); + auto endEIt = ORE::end(); + auto vec = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); + auto startEVec = vec.begin(); + auto endEVec = vec.end(); + + auto startIt = ORi::begin(startEIt, endEIt, st, maxDim); + auto endIt = ORi::end(); + auto rangeVec = ORv::get_iterator_range(startEVec, endEVec, st, maxDim); + auto startVec = rangeVec.begin(); + auto endVec = rangeVec.end(); + for (; startIt != endIt && startVec != endVec; ++startIt,++startVec) { + if () + for (auto v : st.simplex_vertex_range(std::get<0>(t))) std::cout << v << " "; + std::cout << " -- " << std::get<1>(t) << ", " << std::get<2>(t) << "\n"; + } +} + +void test_simplices(const std::vector& points, double nu, double mu, int maxDim) { + ORE::Order_policy p = ORE::Order_policy::FARTHEST_POINT_ORDERING; + // ORE::Order_policy p = ORE::Order_policy::ALREADY_ORDERED; + // ORE::Order_policy p = ORE::Order_policy::RANDOM_POINT_ORDERING; + +// test_simplices_print(points, nu, mu, maxDim, p); + test_simplices_comp(points, nu, mu, maxDim, p); } int main(int argc, char* const argv[]) { - if (argc != 4 && argc != 5) { - std::cout << "Usage: ./comp nu mu nomberOfPoints [seed]\n"; - return 0; - } - - double nu = std::stod(argv[1]); - double mu = std::stod(argv[2]); - unsigned int numberOfPoints = std::stoi(argv[3]); - int seed = -1; - - if (argc == 5) - seed = std::stoi(argv[4]); - - std::cout << "nu, mu: " << nu << ", " << mu << "\n"; - std::cout << "number of points: " << numberOfPoints << "\n"; - std::cout << "seed: " << seed << "\n"; - - std::vector points = build_point_cloud(numberOfPoints, seed); - - std::vector edges_v1 = OR::compute_oscillating_rips_edges(nu, mu, points, Gudhi::Euclidean_distance(), OR::Order_policy::FARTHEST_POINT_ORDERING); - std::cout << edges_v1.size() << "\n"; - - // unsigned int i = 0; - // for (const auto& e : OR::compute_oscillating_rips_edges_as_iterator(nu, mu, points, Gudhi::Euclidean_distance(), OR::Order_policy::FARTHEST_POINT_ORDERING)){ - // ++i; - // } - // std::cout << i << "\n"; - - // unsigned int i = 0; - // for (const auto& e : OR::compute_oscillating_rips_edges_as_iterator(nu, mu, points, Gudhi::Euclidean_distance(), OR::Order_policy::FARTHEST_POINT_ORDERING)){ - // if (i < edges_v1.size()){ - // if (!(edges_v1[i] == e)){ - // std::cout << "[" << i << "] different:\n"; - // std::cout << edges_v1[i].get_smallest_vertex() << ", " << edges_v1[i].get_biggest_vertex() << ", " << edges_v1[i].get_filtration_value() << ", " << edges_v1[i].get_direction() << "\n"; - // std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() << ", " << e.get_direction() << "\n"; - // }/* else { - // std::cout << "[" << i << "] same:\n"; - // std::cout << edges_v1[i].get_smallest_vertex() << ", " << edges_v1[i].get_biggest_vertex() << ", " << edges_v1[i].get_filtration_value() << ", " << edges_v1[i].get_direction() << "\n"; - // std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() << ", " << e.get_direction() << "\n"; - // } */ - // } else { - // std::cout << "[" << i << "] too long:\n"; - // std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() << ", " << e.get_direction() << "\n"; - // } - // ++i; - // } - - return 0; + if (argc != 5 && argc != 6) { + std::cout << "Usage: ./comp nu mu max_dim nomberOfPoints [seed]\n"; + return 0; + } + + double nu = std::stod(argv[1]); + double mu = std::stod(argv[2]); + int maxDim = std::stoi(argv[3]); + unsigned int numberOfPoints = std::stoi(argv[4]); + int seed = -1; + + if (argc == 6) seed = std::stoi(argv[5]); + + std::cout << "nu, mu: " << nu << ", " << mu << "\n"; + std::cout << "number of points: " << numberOfPoints << "\n"; + std::cout << "seed: " << seed << "\n"; + + std::vector points = build_point_cloud(numberOfPoints, seed); + +// test_edges(points, nu, mu); + test_simplices(points, nu, mu, maxDim); + + return 0; } diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h index a84b7497dc..446aeb54ca 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include @@ -31,7 +33,7 @@ namespace zigzag_persistence { template class Zigzag_edge { public: - Zigzag_edge(size_t u, size_t v, Filtration_value fil, bool direction) + Zigzag_edge(int u, int v, Filtration_value fil, bool direction) : u_(u), v_(v), fil_(fil), direction_(direction) { if (u > v) std::swap(u_, v_); } @@ -39,15 +41,15 @@ class Zigzag_edge { Zigzag_edge() : u_(0), v_(0), fil_(0), direction_(true) {} /* Returns vertex with smaller label. */ - size_t get_smallest_vertex() const { return u_; } + int get_smallest_vertex() const { return u_; } /* Returns vertex with bigger label. */ - size_t get_biggest_vertex() const { return v_; } + int get_biggest_vertex() const { return v_; } /* Returns the filtration value of the edge. */ Filtration_value get_filtration_value() const { return fil_; } /* Returns true if insertion of the edge, false if removal. */ bool get_direction() const { return direction_; } - void set(size_t u, size_t v, Filtration_value fil, bool direction){ + void set(int u, int v, Filtration_value fil, bool direction){ u_ = u; v_ = v; fil_ = fil; @@ -58,11 +60,16 @@ class Zigzag_edge { return ((e.u_ == u_) && (e.v_ == v_) && (e.fil_ == fil_) && (e.direction_ == direction_)); } - void assign_filtration(Filtration_value fil) { fil_ = fil; } +// bool operator<(const Zigzag_edge& e) const { +// if (e.fil_ != fil_) return fil_ < e.fil_; +// if (e.direction_ != direction_) return direction_; +// if (e.u_ != u_) return u_ < e.u_; +// return v_ < e.v_; +// } private: - size_t u_; - size_t v_; + int u_; + int v_; Filtration_value fil_; bool direction_; }; @@ -88,111 +95,12 @@ class Square_root_edge_modifier { Square_root_edge_modifier() {} }; +//assumes that eps_n-1 == 0 template > class Oscillating_rips_edge_range { - private: - class Oscillating_rips_edge_iterator; - public: enum Order_policy { ALREADY_ORDERED, FARTHEST_POINT_ORDERING, RANDOM_POINT_ORDERING }; - template - static std::vector > compute_oscillating_rips_edges(Filtration_value nu, - Filtration_value mu, - const PointRange& points, - DistanceFunction&& distance, - Order_policy orderPolicy) - { - std::vector > edgeFiltration; - std::vector epsilonValues; - std::vector > > distanceMatrix; - auto n = points.size(); - - initialize_(nu, mu, epsilonValues, distanceMatrix, points, distance, orderPolicy); - - // edgesAdded[i] (resp. edgesRemoved[i]) == list of edges (i,j), with j > > edgesAdded, edgesRemoved; - - // auto it = std::upper_bound(distanceMatrix[1].begin(), distanceMatrix[1].end(), - // std::pair(distanceMatrix.size(), mu * epsilonValues[0]), - // Point_distance_comp()); - // std::cout << "start vect colind: " << (it - distanceMatrix[1].begin()) << ", (" << it->first << ", " << it->second << "), (" << distanceMatrix[1].begin()->first << ", " << distanceMatrix[1].begin()->second << ")\n"; - size_t number_of_arrows = compute_edges_(nu, mu, epsilonValues, distanceMatrix, edgesAdded, edgesRemoved); - - // Now, sort edges according to lengths, and put everything in edgeFiltration - edgeFiltration.clear(); - edgeFiltration.reserve(number_of_arrows + n); // count edges + vertices additions - - // initialise R({p_0}, +infinity) - edgeFiltration.emplace_back(0, 0, // add vertex p_0,+infty - std::numeric_limits::infinity(), true); - // epsilonValues[0], true); - - if constexpr (EdgeModifier::isActive_) { - for (size_t i = 0; i < n - 1; ++i) { // all ascending arrows eps_i - edgeFiltration.emplace_back(i + 1, i + 1, EdgeModifier::apply_modifier(epsilonValues[i]), true); // add p_{i+1},eps_i - for (auto edg_it = edgesAdded[i].begin(); edg_it != edgesAdded[i].end(); ++edg_it) { - edgeFiltration.push_back(*edg_it); - } - for (auto edg_it = edgesRemoved[i].rbegin(); // longest first - edg_it != edgesRemoved[i].rend(); ++edg_it) { - edgeFiltration.push_back(*edg_it); - } - } - } else { - for (size_t i = 0; i < n - 1; ++i) { // all ascending arrows eps_i - edgeFiltration.emplace_back(i + 1, i + 1, epsilonValues[i], true); // add p_{i+1},eps_i - for (auto edg_it = edgesAdded[i].begin(); edg_it != edgesAdded[i].end(); ++edg_it) { - edgeFiltration.push_back(*edg_it); - } - for (auto edg_it = edgesRemoved[i].rbegin(); // longest first - edg_it != edgesRemoved[i].rend(); ++edg_it) { - edgeFiltration.push_back(*edg_it); - } - } - } - - // what remains is removed in the zigzag iterator with -infinity values. If eps_n-1 - //== 0, which is the usual case, the remaining simplices in the filtration are - // the n vertices. - // cannot inforce this here. - - return edgeFiltration; - } - - template - static boost::iterator_range compute_oscillating_rips_edges_as_iterator( - Filtration_value nu, - Filtration_value mu, - const PointRange& points, - DistanceFunction&& distance, - Order_policy orderPolicy) - { - auto start = Oscillating_rips_edge_iterator(nu, mu, points, distance, orderPolicy); - auto end = Oscillating_rips_edge_iterator(); - return boost::iterator_range( - start, end); - } - - private: - /* The two input types std::pair encode pairs - * (j, d(p_i,p_j)) and (k, d(p_i,p_k)) for some fixed point p_i. - * The operator() orders edges by length. By convention, if lengths are equal, - * it orders pairs by taking the smaller vertex label between j and k. - */ - struct Point_distance_comp { - bool operator()(const std::pair& p1, const std::pair& p2) const { - { - if (p1.second != p2.second) { - return p1.second < p2.second; // shorter first - } - return p1.first < p2.first; - } - } - }; - class Oscillating_rips_edge_iterator : public boost::iterator_facade&, @@ -204,24 +112,37 @@ class Oscillating_rips_edge_range { const PointRange& points, DistanceFunction&& distance, Order_policy orderPolicy) - : nu_(nu), mu_(mu), currentEdge_(0, 0, std::numeric_limits::infinity(), true), isEnd_(false), epsValIndex_(0), rowIndex_(1), inPosEd_(true), insertVertex_(true) + : nu_(nu), + mu_(mu), + currentEdge_(0, 0, std::numeric_limits::infinity(), true), + epsilonIndex_(0), + rowIndex_(1), + inPositiveDirection_(true), + insertVertex_(true) { - initialize_(nu_, mu_, epsilonValues_, distanceMatrix_, points, distance, orderPolicy); - auto it = std::upper_bound(distanceMatrix_[1].begin(), distanceMatrix_[1].end(), - std::pair(distanceMatrix_.size(), mu_ * epsilonValues_[epsValIndex_]), - Point_distance_comp()); + _initialize(nu_, mu_, epsilonValues_, distanceMatrix_, points, distance, orderPolicy); + auto it = + std::upper_bound(distanceMatrix_[1].begin(), distanceMatrix_[1].end(), + std::pair(distanceMatrix_.size(), mu_ * epsilonValues_[epsilonIndex_]), + Point_distance_comp()); columnIndex_ = it - distanceMatrix_[1].begin(); - // std::cout << "start colind: " << columnIndex_ << ", (" << it->first << ", " << it->second << "), (" << distanceMatrix_[1].begin()->first << ", " << distanceMatrix_[1].begin()->second << ")\n"; } template - Oscillating_rips_edge_iterator(Filtration_value nu, Filtration_value mu, const PointRange& orderedPoints, - DistanceFunction&& distance, const std::vector& epsilonValues) + Oscillating_rips_edge_iterator(Filtration_value nu, + Filtration_value mu, + const PointRange& orderedPoints, + DistanceFunction&& distance, + const std::vector& epsilonValues) : epsilonValues_(epsilonValues), nu_(nu), mu_(mu), currentEdge_(0, 0, std::numeric_limits::infinity(), true), - isEnd_(false), epsValIndex_(0), rowIndex_(1), inPosEd_(true), insertVertex_(true) { + epsilonIndex_(0), + rowIndex_(1), + inPositiveDirection_(true), + insertVertex_(true) + { GUDHI_CHECK(orderedPoints.size() == epsilonValues.size(), "The number of points and the number of epsilon values should match."); GUDHI_CHECK((nu <= mu) && (nu >= 0), "Invalid parameters mu and nu"); @@ -232,15 +153,24 @@ class Oscillating_rips_edge_range { } // compute the distance matrix - distanceMatrix_ = compute_distance_matrix_(orderedPoints, distance); + distanceMatrix_ = _compute_distance_matrix(orderedPoints, distance); - auto it = std::upper_bound(distanceMatrix_[1].begin(), distanceMatrix_[1].end(), - std::pair(distanceMatrix_.size(), mu_ * epsilonValues_[epsValIndex_]), - Point_distance_comp()); + auto it = + std::upper_bound(distanceMatrix_[1].begin(), distanceMatrix_[1].end(), + std::pair(distanceMatrix_.size(), mu_ * epsilonValues_[epsilonIndex_]), + Point_distance_comp()); columnIndex_ = it - distanceMatrix_[1].begin(); } - Oscillating_rips_edge_iterator() : nu_(0), mu_(0), currentEdge_(0, 0, 0, true), isEnd_(true), epsValIndex_(0), rowIndex_(1), columnIndex_(0), inPosEd_(true), insertVertex_(true) {} + Oscillating_rips_edge_iterator() + : nu_(0), + mu_(0), + currentEdge_(0, 0, 0, true), + epsilonIndex_(0), + rowIndex_(0), + columnIndex_(0), + inPositiveDirection_(true), + insertVertex_(true) {} private: friend class boost::iterator_core_access; @@ -250,157 +180,265 @@ class Oscillating_rips_edge_range { Filtration_value nu_; Filtration_value mu_; Zigzag_edge currentEdge_; - bool isEnd_; //to replace - size_t epsValIndex_, rowIndex_, columnIndex_; - bool inPosEd_, insertVertex_; //to replace + size_t epsilonIndex_, rowIndex_, columnIndex_; + bool inPositiveDirection_, insertVertex_; - bool equal(Oscillating_rips_edge_iterator const& other) const { return isEnd_ == other.isEnd_ && currentEdge_ == other.currentEdge_; } + bool equal(Oscillating_rips_edge_iterator const& other) const { + return rowIndex_ == other.rowIndex_ && currentEdge_ == other.currentEdge_; + } const Zigzag_edge& dereference() const { return currentEdge_; } void increment() { - if (epsValIndex_ < distanceMatrix_.size() - 1) { + if (epsilonIndex_ < distanceMatrix_.size() - 1) { if (insertVertex_) { - currentEdge_.set(epsValIndex_ + 1, epsValIndex_ + 1, epsilonValues_[epsValIndex_], true); + _update_edge_as_positive_vertex(); insertVertex_ = false; return; } - while ((epsValIndex_ < distanceMatrix_.size() - 1 && inPosEd_ && col_index_is_not_valid_pos()) || - (epsValIndex_ < distanceMatrix_.size() - 1 && !inPosEd_ && col_index_is_not_valid_neg())) { - if (inPosEd_) { - ini_col_index_pos(); - } else { - ini_col_index_neg(); - if (epsValIndex_ == distanceMatrix_.size() - 1) { - set_end_(); - return; - } - if (insertVertex_) { - currentEdge_.set(epsValIndex_ + 1, epsValIndex_ + 1, epsilonValues_[epsValIndex_], true); - insertVertex_ = false; + if (inPositiveDirection_ && _positive_col_index_is_not_valid()) { + while (_positive_col_index_is_not_valid() && rowIndex_ <= epsilonIndex_) { + ++rowIndex_; + _initialize_positive_col_index(); + } + if (_positive_col_index_is_not_valid()) { + _initialize_negative_col_index(); + inPositiveDirection_ = false; + } + } + + if (!inPositiveDirection_ && _negative_col_index_is_not_valid()) { + while (_negative_col_index_is_not_valid() && rowIndex_ > 1) { + --rowIndex_; + _initialize_negative_col_index(); + } + if (_negative_col_index_is_not_valid()) { + _initialize_positive_col_index(); + ++epsilonIndex_; + inPositiveDirection_ = true; + if (epsilonIndex_ == distanceMatrix_.size() - 1) { + // _set_end(); + rowIndex_ = distanceMatrix_.size(); + _update_edge_as_negative_vertex(); return; } + _update_edge_as_positive_vertex(); + return; } } - if (inPosEd_) { - up_edge_pos(epsValIndex_); - if (rowIndex_ == epsValIndex_ + 2) { - inPosEd_ = false; + if (inPositiveDirection_) { + --columnIndex_; + _update_edge(epsilonIndex_, true); + while (_positive_col_index_is_not_valid() && rowIndex_ <= epsilonIndex_) { + ++rowIndex_; + _initialize_positive_col_index(); + } + if (_positive_col_index_is_not_valid()) { + ++rowIndex_; + } + if (rowIndex_ == epsilonIndex_ + 2) { + inPositiveDirection_ = false; --rowIndex_; - ini_col_index_low(); + _initialize_negative_col_index(); } return; } - up_edge_neg(epsValIndex_); + _update_edge(epsilonIndex_, false); + ++columnIndex_; + while (_negative_col_index_is_not_valid() && rowIndex_ > 1) { + --rowIndex_; + _initialize_negative_col_index(); + } + if (_negative_col_index_is_not_valid()) { + --rowIndex_; + } if (rowIndex_ == 0) { - ++epsValIndex_; - if (epsValIndex_ == distanceMatrix_.size() - 1) return; + ++epsilonIndex_; + if (epsilonIndex_ == distanceMatrix_.size() - 1) { + rowIndex_ = distanceMatrix_.size() + 1; + return; + } insertVertex_ = true; - inPosEd_ = true; + inPositiveDirection_ = true; ++rowIndex_; - ini_col_index_up(); + _initialize_positive_col_index(); } return; + } if (rowIndex_ > 1){ + --rowIndex_; + _update_edge_as_negative_vertex(); + return; } - set_end_(); + _set_end(); } - void set_end_(){ - isEnd_ = true; + void _set_end(){ + rowIndex_ = 0; currentEdge_ = Zigzag_edge(); } - void ini_col_index_up() { + void _initialize_positive_col_index() { auto it = std::upper_bound(distanceMatrix_[rowIndex_].begin(), distanceMatrix_[rowIndex_].end(), - std::pair(distanceMatrix_.size(), mu_ * epsilonValues_[epsValIndex_]), + std::pair(distanceMatrix_.size(), mu_ * epsilonValues_[epsilonIndex_]), Point_distance_comp()); columnIndex_ = it - distanceMatrix_[rowIndex_].begin(); } - void ini_col_index_low() { + void _initialize_negative_col_index() { auto it = std::lower_bound(distanceMatrix_[rowIndex_].begin(), distanceMatrix_[rowIndex_].end(), - std::pair(0, nu_ * epsilonValues_[epsValIndex_ + 1]), + std::pair(0, nu_ * epsilonValues_[epsilonIndex_ + 1]), Point_distance_comp()); - while (it != distanceMatrix_[rowIndex_].end() && it->second == nu_ * epsilonValues_[epsValIndex_ + 1]) ++it; + while (it != distanceMatrix_[rowIndex_].end() && it->second == nu_ * epsilonValues_[epsilonIndex_ + 1]) ++it; columnIndex_ = it - distanceMatrix_[rowIndex_].begin(); } - void ini_col_index_pos(){ - while (col_index_is_not_valid_pos() && rowIndex_ <= epsValIndex_) { - ++rowIndex_; - ini_col_index_up(); - } - if (col_index_is_not_valid_pos()){ - ini_col_index_low(); - inPosEd_ = false; - } - } - - void ini_col_index_neg(){ - while (col_index_is_not_valid_neg() && rowIndex_ > 1) { - --rowIndex_; - ini_col_index_low(); - } - if (col_index_is_not_valid_neg()){ - ini_col_index_up(); - ++epsValIndex_; - insertVertex_ = true; - inPosEd_ = true; - } - } - - bool col_index_is_not_valid_pos() { + bool _positive_col_index_is_not_valid() { return columnIndex_ == 0 || - (rowIndex_ != (epsValIndex_ + 1) && - distanceMatrix_[rowIndex_][columnIndex_ - 1].second <= nu_ * epsilonValues_[epsValIndex_]); + (rowIndex_ != (epsilonIndex_ + 1) && + distanceMatrix_[rowIndex_][columnIndex_ - 1].second <= nu_ * epsilonValues_[epsilonIndex_]); } - bool col_index_is_not_valid_neg() { + bool _negative_col_index_is_not_valid() { return columnIndex_ == distanceMatrix_[rowIndex_].size() || - distanceMatrix_[rowIndex_][columnIndex_].second > mu_ * epsilonValues_[epsValIndex_]; + distanceMatrix_[rowIndex_][columnIndex_].second > mu_ * epsilonValues_[epsilonIndex_]; } - void up_edge_pos(size_t i) { - --columnIndex_; + void _update_edge(size_t i, bool direction) { if constexpr (EdgeModifier::isActive_) - currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, rowIndex_, EdgeModifier::apply_modifier(epsilonValues_[i]), true); - else - currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, rowIndex_, epsilonValues_[i], true); - // std::cout << "pos: " << rowIndex_ << ", " << columnIndex_ << ", " << distanceMatrix_[rowIndex_][columnIndex_].first << "\n"; - while (col_index_is_not_valid_pos() && rowIndex_ <= i) { - ++rowIndex_; - ini_col_index_up(); - } - if (col_index_is_not_valid_pos()) { - ++rowIndex_; - } + currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, rowIndex_, + EdgeModifier::apply_modifier(epsilonValues_[i]), direction); + else + currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, rowIndex_, epsilonValues_[i], direction); } - void up_edge_neg(size_t i) { + void _update_edge_as_positive_vertex() { if constexpr (EdgeModifier::isActive_) - currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, rowIndex_, EdgeModifier::apply_modifier(epsilonValues_[i]), false); - else - currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, rowIndex_, epsilonValues_[i], false); - // std::cout << "neg: " << rowIndex_ << ", " << columnIndex_ << ", " << distanceMatrix_[rowIndex_][columnIndex_].first << "\n"; - ++columnIndex_; - while (col_index_is_not_valid_neg() && rowIndex_ > 1) { - --rowIndex_; - ini_col_index_low(); - } - if (col_index_is_not_valid_neg()) { - --rowIndex_; + currentEdge_.set(epsilonIndex_ + 1, epsilonIndex_ + 1, + EdgeModifier::apply_modifier(epsilonValues_[epsilonIndex_]), true); + else + currentEdge_.set(epsilonIndex_ + 1, epsilonIndex_ + 1, epsilonValues_[epsilonIndex_], true); + } + + void _update_edge_as_negative_vertex() { + currentEdge_.set(rowIndex_ - 1, rowIndex_ - 1, -std::numeric_limits::infinity(), false); + } + }; + + template + static std::vector > compute_vector_range(Filtration_value nu, + Filtration_value mu, + const PointRange& points, + DistanceFunction&& distance, + Order_policy orderPolicy) + { + std::vector > edgeFiltration; + std::vector epsilonValues; + std::vector > > distanceMatrix; + auto n = points.size(); + + _initialize(nu, mu, epsilonValues, distanceMatrix, points, distance, orderPolicy); + + // edgesAdded[i] (resp. edgesRemoved[i]) == list of edges (i,j), with j > > edgesAdded, edgesRemoved; + + // auto it = std::upper_bound(distanceMatrix[1].begin(), distanceMatrix[1].end(), + // std::pair(distanceMatrix.size(), mu * epsilonValues[0]), + // Point_distance_comp()); + // std::cout << "start vect colind: " << (it - distanceMatrix[1].begin()) << ", (" << it->first << ", " << it->second << "), (" << distanceMatrix[1].begin()->first << ", " << distanceMatrix[1].begin()->second << ")\n"; + size_t number_of_arrows = _compute_edges(nu, mu, epsilonValues, distanceMatrix, edgesAdded, edgesRemoved); + + // Now, sort edges according to lengths, and put everything in edgeFiltration + edgeFiltration.clear(); + edgeFiltration.reserve(number_of_arrows + n); // count edges + vertices additions + + // initialise R({p_0}, +infinity) + edgeFiltration.emplace_back(0, 0, // add vertex p_0,+infty + std::numeric_limits::infinity(), true); + // epsilonValues[0], true); + + for (size_t i = 0; i < n - 1; ++i) { // all ascending arrows eps_i + if constexpr (EdgeModifier::isActive_) { + edgeFiltration.emplace_back(i + 1, i + 1, + EdgeModifier::apply_modifier(epsilonValues[i]), + true); // add p_{i+1},eps_i + } else { + edgeFiltration.emplace_back(i + 1, i + 1, epsilonValues[i], true); // add p_{i+1},eps_i + } + for (auto edg_it = edgesAdded[i].begin(); edg_it != edgesAdded[i].end(); ++edg_it) { + edgeFiltration.push_back(*edg_it); + } + for (auto edg_it = edgesRemoved[i].rbegin(); // longest first + edg_it != edgesRemoved[i].rend(); ++edg_it) { + edgeFiltration.push_back(*edg_it); + } + } + for (int i = n - 1; i >= 0; --i) { + edgeFiltration.emplace_back(i, i, -std::numeric_limits::infinity(), false); + } + + _canonically_sort_edges(edgeFiltration); + + return edgeFiltration; + } + + template + static boost::iterator_range get_iterator_range( + Filtration_value nu, + Filtration_value mu, + const PointRange& points, + DistanceFunction&& distance, + Order_policy orderPolicy) + { + return boost::iterator_range( + Oscillating_rips_edge_iterator(nu, mu, points, distance, orderPolicy), Oscillating_rips_edge_iterator()); + } + + //as Oscillating_rips_edge_iterator is a heavy iterator to copy, it should not be used as a usual iterator + template + static Oscillating_rips_edge_iterator begin( + Filtration_value nu, + Filtration_value mu, + const PointRange& points, + DistanceFunction&& distance, + Order_policy orderPolicy) + { + return Oscillating_rips_edge_iterator(nu, mu, points, distance, orderPolicy); + } + + static Oscillating_rips_edge_iterator end() + { + return Oscillating_rips_edge_iterator(); + } + + private: + Oscillating_rips_edge_range(){}; + + /* The two input types std::pair encode pairs + * (j, d(p_i,p_j)) and (k, d(p_i,p_k)) for some fixed point p_i. + * The operator() orders edges by length. By convention, if lengths are equal, + * it orders pairs by taking the smaller vertex label between j and k. + */ + struct Point_distance_comp { + bool operator()(const std::pair& p1, const std::pair& p2) const { + { + if (p1.second != p2.second) { + return p1.second < p2.second; // shorter first } + return p1.first < p2.first; + } } }; template - static void initialize_(Filtration_value& nu, Filtration_value& mu, std::vector& epsilonValues, + static void _initialize(Filtration_value& nu, Filtration_value& mu, std::vector& epsilonValues, std::vector > >& distanceMatrix, const PointRange& points, DistanceFunction&& distance, Order_policy orderPolicy) { GUDHI_CHECK((nu <= mu) && (nu >= 0), "Invalid parameters mu and nu"); @@ -417,7 +455,7 @@ class Oscillating_rips_edge_range { // compute epsilon values if (orderPolicy == Order_policy::ALREADY_ORDERED) { sortedPoints.assign(points.begin(), points.end()); - epsilonValues = compute_epsilon_values_(sortedPoints, distance); + epsilonValues = _compute_epsilon_values(sortedPoints, distance); } else if (orderPolicy == Order_policy::FARTHEST_POINT_ORDERING) { epsilonValues.reserve(n); Gudhi::subsampling::choose_n_farthest_points(distance, points, @@ -432,11 +470,11 @@ class Oscillating_rips_edge_range { epsilonValues[n - 1] = 0; } else { Gudhi::subsampling::pick_n_random_points(points, n, std::back_inserter(sortedPoints)); - epsilonValues = compute_epsilon_values_(sortedPoints, distance); + epsilonValues = _compute_epsilon_values(sortedPoints, distance); } // compute the distance matrix - distanceMatrix = compute_distance_matrix_(sortedPoints, distance); + distanceMatrix = _compute_distance_matrix(sortedPoints, distance); } /** \brief Compute the epsilon values for an ordered set of points, measuring the @@ -457,7 +495,7 @@ class Oscillating_rips_edge_range { * The range must be of same size as the number of points. */ template - static std::vector compute_epsilon_values_(const PointRange& points, DistanceFunction&& distance) { + static std::vector _compute_epsilon_values(const PointRange& points, DistanceFunction&& distance) { size_t n = points.size(); std::vector eps_range(n, std::numeric_limits::infinity()); @@ -501,7 +539,7 @@ class Oscillating_rips_edge_range { } template - static std::vector > > compute_distance_matrix_( + static std::vector > > _compute_distance_matrix( const PointRange& sortedPoints, DistanceFunction&& distance) { std::vector > > distanceMatrix(sortedPoints.size()); #ifdef GUDHI_USE_TBB @@ -529,7 +567,7 @@ class Oscillating_rips_edge_range { return distanceMatrix; } - static size_t compute_edges_(Filtration_value nu, Filtration_value mu, + static size_t _compute_edges(Filtration_value nu, Filtration_value mu, const std::vector& epsilonValues, std::vector > >& distanceMatrix, std::vector > >& edgesAdded, @@ -710,6 +748,274 @@ class Oscillating_rips_edge_range { return number_of_arrows; } + + static void _canonically_sort_edges(std::vector >& edges) { + // canonical sort of the edges: as much as possible, edges should be removed in + // the reverse order of their insertion. We decide to insert shorted edges first, + // with increasing lexicographical order, and remove larger edges first, with + // decreasing lexicographic order. + + // filtration then dimension, then lex order for insertion + auto edge_cmp = [](const Zigzag_edge& e1, const Zigzag_edge& e2) { + if (e1.get_filtration_value() != e2.get_filtration_value()) { + return e1.get_filtration_value() < e2.get_filtration_value(); + } // lower fil first + + if (e1.get_smallest_vertex() == e1.get_biggest_vertex()) { // e1 is a vertex, -> put vertices first + if (e2.get_smallest_vertex() == e2.get_biggest_vertex()) { + return e1.get_smallest_vertex() < e2.get_smallest_vertex(); + } //-> vertex of lower label + else { + return true; + } //-> always vertices before edges + } + // e1 is an edge + if (e2.get_smallest_vertex() == e2.get_biggest_vertex()) { + return false; + } // e2 vertex, -> put it first + // both are edges, lexigraphic compare + if (e1.get_smallest_vertex() != e2.get_smallest_vertex()) { + return e1.get_smallest_vertex() < e2.get_smallest_vertex(); + } // lex order + if (e1.get_biggest_vertex() != e2.get_biggest_vertex()) { + return e1.get_biggest_vertex() < e2.get_biggest_vertex(); + } + return false; // equality + }; + // the inverse ordering for deletions + auto inv_edge_cmp = [&](const Zigzag_edge& e1, const Zigzag_edge& e2) { + if (e1.get_smallest_vertex() == e2.get_smallest_vertex() && e1.get_biggest_vertex() == e2.get_biggest_vertex()) { + return false; + } //== => false + return !(edge_cmp(e1, e2)); // reverse order + }; + // sort sequences of inclusions of same filtration with edge_cmp + // sort sequences of removals of same filtration with inv_edge_cmp + auto beg = edges.begin(); + auto end = edges.begin(); + auto curr_fil = beg->get_filtration_value(); + auto curr_type = beg->get_direction(); + while (beg != edges.end()) { + while (end != edges.end() && end->get_filtration_value() == curr_fil && end->get_direction() == curr_type) { + ++end; + } + if (curr_type) { +#ifdef GUDHI_USE_TBB + tbb::parallel_sort(beg, end, edge_cmp); +#else + std::sort(beg, end, edge_cmp); +#endif + } // sequence of insertions + else { +#ifdef GUDHI_USE_TBB + tbb::parallel_sort(beg, end, inv_edge_cmp); +#else + std::sort(beg, end, inv_edge_cmp); +#endif + } // sequence of removals + beg = end; + curr_fil = beg->get_filtration_value(); + curr_type = beg->get_direction(); + } + } +}; + +//needs Filtered_complex to have stable simplex handles +template +class Oscillating_rips_simplex_range { + public: + class Oscillating_rips_iterator + : public boost::iterator_facade&, + boost::forward_traversal_tag> { + public: + using Filtration_value = typename Filtered_complex::Filtration_value; + using Simplex_handle = typename Filtered_complex::Simplex_handle; + using Simplex_key = typename Filtered_complex::Simplex_key; + + // edges and complex are not copied, so do not modifiy outside as long as iterator != end. + // TODO: move constructor for iterator? + Oscillating_rips_iterator(EdgeRangeIterator& edgeStartIterator, EdgeRangeIterator& edgeEndIterator, + Filtered_complex& complex, int maxDimension = -1) + : complex_(&complex), + currentSimplexIndex_(0), + currentEdgeIt_(std::move(edgeStartIterator)), + endEdgeIt_(std::move(edgeEndIterator)), + currentDirection_(true), + maxDimension_(maxDimension), + currentArrowNumber_(0) + { + if (currentEdgeIt_ == endEdgeIt_) { + _set_end(); + return; + } + + // first simplex is the vertex (0,0) which is the only one with its filtration value. + complex_->insert_edge_as_flag(currentEdgeIt_->get_smallest_vertex(), currentEdgeIt_->get_biggest_vertex(), + currentEdgeIt_->get_filtration_value(), maxDimension_, currentSimplices_); + ++currentEdgeIt_; + + std::get<0>(currentArrow_) = currentSimplices_[currentSimplexIndex_]; + std::get<1>(currentArrow_) = complex_->filtration(currentSimplices_[currentSimplexIndex_]); + std::get<2>(currentArrow_) = currentDirection_; + } + + Oscillating_rips_iterator() : complex_(nullptr), currentSimplexIndex_(0), maxDimension_(0) {} + + private: + friend class boost::iterator_core_access; + + struct reverse_lexicographic_order { + explicit reverse_lexicographic_order(Filtered_complex* st) : st_(st) {} + + bool operator()(const Simplex_handle sh1, const Simplex_handle sh2) const { + auto rg1 = st_->simplex_vertex_range(sh1); + auto rg2 = st_->simplex_vertex_range(sh2); + auto it1 = rg1.begin(); + auto it2 = rg2.begin(); + while (it1 != rg1.end() && it2 != rg2.end()) { + if (*it1 == *it2) { + ++it1; + ++it2; + } else { + return *it1 < *it2; + } + } + return ((it1 == rg1.end()) && (it2 != rg2.end())); + } + Filtered_complex* st_; + }; + + std::vector currentSimplices_; + Filtered_complex* complex_; + size_t currentSimplexIndex_; + EdgeRangeIterator currentEdgeIt_; + EdgeRangeIterator endEdgeIt_; + bool currentDirection_; + const int maxDimension_; + // bool isEnd_; + std::tuple currentArrow_; + Simplex_key currentArrowNumber_; + + bool equal(Oscillating_rips_iterator const& other) const { + if (complex_ == nullptr) return other.complex_ == nullptr; + + return complex_ == other.complex_ && currentEdgeIt_ == other.currentEdgeIt_ && + currentSimplexIndex_ == other.currentSimplexIndex_; + } + + const std::tuple& dereference() const { + return currentArrow_; + } + + void increment() { + ++currentSimplexIndex_; + ++currentArrowNumber_; + + if (currentSimplexIndex_ == currentSimplices_.size()) { + if (currentEdgeIt_ == endEdgeIt_) { + _set_end(); + return; + } + + if (!currentDirection_) { + for (auto& sh : currentSimplices_) complex_->remove_maximal_simplex(sh); + } + currentSimplices_.clear(); + + auto fil = currentEdgeIt_->get_filtration_value(); + currentDirection_ = currentEdgeIt_->get_direction(); + + if (currentDirection_) { + _update_positive_current_simplices(fil); + } else { + _update_negative_current_simplices(fil); + } + currentSimplexIndex_ = 0; + } + + std::get<0>(currentArrow_) = currentSimplices_[currentSimplexIndex_]; + std::get<1>(currentArrow_) = complex_->filtration(currentSimplices_[currentSimplexIndex_]); + std::get<2>(currentArrow_) = currentDirection_; + if (currentDirection_) complex_->assign_key(currentSimplices_[currentSimplexIndex_], currentArrowNumber_); + } + + void _set_end() { complex_ = nullptr; } + + void _update_positive_current_simplices(Filtration_value fil) { + while (currentEdgeIt_ != endEdgeIt_ && currentEdgeIt_->get_direction() && + currentEdgeIt_->get_filtration_value() == fil) + { + complex_->insert_edge_as_flag(currentEdgeIt_->get_smallest_vertex(), currentEdgeIt_->get_biggest_vertex(), + currentEdgeIt_->get_filtration_value(), maxDimension_, currentSimplices_); + // std::cout << "add edge: " << currentEdgeIt_->get_smallest_vertex() << " " << currentEdgeIt_->get_biggest_vertex() << " - " << currentEdgeIt_->get_filtration_value() << "\n"; + // std::cout << "current:\n"; + // for (auto sh : currentSimplices_){ + // for (auto v : complex_->simplex_vertex_range(sh)) std::cout << v << " "; + // std::cout << "\n"; + // } + ++currentEdgeIt_; + } +#ifdef GUDHI_USE_TBB + tbb::parallel_sort(currentSimplices_.begin(), currentSimplices_.end(), + reverse_lexicographic_order(complex_)); +#else + std::sort(currentSimplices_.begin(), currentSimplices_.end(), + reverse_lexicographic_order(complex_)); +#endif + } + + void _update_negative_current_simplices(Filtration_value fil) { + unsigned int count = 0; + while (currentEdgeIt_ != endEdgeIt_ && !currentEdgeIt_->get_direction() && + currentEdgeIt_->get_filtration_value() == fil) + { + Simplex_handle sh = complex_->find({currentEdgeIt_->get_smallest_vertex()}); + if (currentEdgeIt_->get_smallest_vertex() != currentEdgeIt_->get_biggest_vertex()) { + sh = sh->second.children()->members().find(currentEdgeIt_->get_biggest_vertex()); + } + auto toRemove = complex_->star_simplex_range(sh); + currentSimplices_.insert(currentSimplices_.end(), toRemove.begin(), toRemove.end()); + ++currentEdgeIt_; + ++count; + } +#ifdef GUDHI_USE_TBB + tbb::parallel_sort( + currentSimplices_.begin(), currentSimplices_.end(), + [&](Simplex_handle sh1, Simplex_handle sh2) -> bool { return complex_->key(sh1) > complex_->key(sh2); }); +#else + std::sort( + currentSimplices_.begin(), currentSimplices_.end(), + [&](Simplex_handle sh1, Simplex_handle sh2) -> bool { return complex_->key(sh1) > complex_->key(sh2); }); +#endif + if (count > 1) { // more than 1 edge inserted: cofaces can be duplicated + auto last = std::unique(currentSimplices_.begin(), currentSimplices_.end(), + [&](Simplex_handle sh1, Simplex_handle sh2) -> bool { + return complex_->key(sh1) == complex_->key(sh2); + }); // equal simplex handles means equal key + currentSimplices_.erase(last, currentSimplices_.end()); // remove duplicated cofaces + } + } + }; + + static boost::iterator_range get_iterator_range(EdgeRangeIterator& edgeStartIterator, + EdgeRangeIterator& edgeEndIterator, + Filtered_complex& complex, + int maxDimension = -1) { + return boost::iterator_range( + Oscillating_rips_iterator(edgeStartIterator, edgeEndIterator, complex, maxDimension), + Oscillating_rips_iterator()); + } + + static Oscillating_rips_iterator begin(EdgeRangeIterator& edgeStartIterator, EdgeRangeIterator& edgeEndIterator, + Filtered_complex& complex, int maxDimension = -1) { + return Oscillating_rips_iterator(edgeStartIterator, edgeEndIterator, complex, maxDimension); + } + + static Oscillating_rips_iterator end() { return Oscillating_rips_iterator(); } + + private: + Oscillating_rips_simplex_range(){}; }; } // namespace zigzag_persistence From 9b0a7ccf96184b8baf45d1cf0daefa088fc98d56 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Tue, 8 Aug 2023 17:26:07 +0200 Subject: [PATCH 10/51] oscillating rips persistence, example and benchmark --- .../benchmark/CMakeLists.txt | 19 + .../benchmark/Oscillating_rips_benchmark.cpp | 213 ++ .../Oscillating_rips_comp_benchmark.cpp | 250 ++ .../benchmark/ext_zz/dionysus/chain.h | 153 + .../benchmark/ext_zz/dionysus/chain.hpp | 188 ++ .../ext_zz/dionysus/clearing-reduction.h | 45 + .../ext_zz/dionysus/clearing-reduction.hpp | 60 + .../benchmark/ext_zz/dionysus/cnpy.h | 241 ++ .../ext_zz/dionysus/cohomology-persistence.h | 116 + .../dionysus/cohomology-persistence.hpp | 61 + .../benchmark/ext_zz/dionysus/common.h | 25 + .../benchmark/ext_zz/dionysus/diagram.h | 114 + .../benchmark/ext_zz/dionysus/distances.h | 93 + .../benchmark/ext_zz/dionysus/distances.hpp | 30 + .../benchmark/ext_zz/dionysus/dlog/progress.h | 57 + .../benchmark/ext_zz/dionysus/fields/q.h | 63 + .../benchmark/ext_zz/dionysus/fields/z2.h | 31 + .../benchmark/ext_zz/dionysus/fields/zp.h | 55 + .../benchmark/ext_zz/dionysus/filtration.h | 124 + .../benchmark/ext_zz/dionysus/format.h | 8 + .../ext_zz/dionysus/format/format.cc | 1156 ++++++++ .../benchmark/ext_zz/dionysus/format/format.h | 2546 +++++++++++++++++ .../benchmark/ext_zz/dionysus/grid/box.h | 136 + .../benchmark/ext_zz/dionysus/grid/box.hpp | 141 + .../benchmark/ext_zz/dionysus/grid/grid.h | 143 + .../benchmark/ext_zz/dionysus/grid/point.h | 132 + .../benchmark/ext_zz/dionysus/grid/vertices.h | 86 + .../ext_zz/dionysus/matrix-filtration.h | 120 + .../ext_zz/dionysus/omni-field-persistence.h | 145 + .../dionysus/omni-field-persistence.hpp | 250 ++ .../benchmark/ext_zz/dionysus/opts/opts.h | 499 ++++ .../ext_zz/dionysus/ordinary-persistence.h | 64 + .../benchmark/ext_zz/dionysus/pair-recorder.h | 78 + .../ext_zz/dionysus/reduced-matrix.h | 170 ++ .../ext_zz/dionysus/reduced-matrix.hpp | 78 + .../benchmark/ext_zz/dionysus/reduction.h | 109 + .../dionysus/relative-homology-zigzag.h | 84 + .../dionysus/relative-homology-zigzag.hpp | 122 + .../benchmark/ext_zz/dionysus/rips.h | 147 + .../benchmark/ext_zz/dionysus/rips.hpp | 162 ++ .../benchmark/ext_zz/dionysus/row-reduction.h | 54 + .../ext_zz/dionysus/row-reduction.hpp | 103 + .../benchmark/ext_zz/dionysus/simplex.h | 280 ++ .../ext_zz/dionysus/sparse-row-matrix.h | 184 ++ .../ext_zz/dionysus/sparse-row-matrix.hpp | 103 + .../ext_zz/dionysus/standard-reduction.h | 44 + .../ext_zz/dionysus/standard-reduction.hpp | 47 + .../benchmark/ext_zz/dionysus/trails-chains.h | 17 + .../ext_zz/dionysus/zigzag-persistence.h | 142 + .../ext_zz/dionysus/zigzag-persistence.hpp | 541 ++++ .../benchmark/ext_zz/fzz/fzz.cpp | 206 ++ .../benchmark/ext_zz/fzz/fzz.h | 87 + .../ext_zz/phat/algorithms/chunk_reduction.h | 223 ++ .../ext_zz/phat/algorithms/row_reduction.h | 56 + .../algorithms/spectral_sequence_reduction.h | 80 + .../phat/algorithms/standard_reduction.h | 47 + .../ext_zz/phat/algorithms/twist_reduction.h | 51 + .../benchmark/ext_zz/phat/boundary_matrix.h | 343 +++ .../ext_zz/phat/compute_persistence_pairs.h | 128 + .../benchmark/ext_zz/phat/helpers/dualize.h | 74 + .../benchmark/ext_zz/phat/helpers/misc.h | 75 + .../phat/helpers/thread_local_storage.h | 52 + .../benchmark/ext_zz/phat/persistence_pairs.h | 155 + .../representations/abstract_pivot_column.h | 102 + .../representations/bit_tree_pivot_column.h | 165 ++ .../phat/representations/full_pivot_column.h | 100 + .../phat/representations/heap_pivot_column.h | 126 + .../representations/sparse_pivot_column.h | 79 + .../ext_zz/phat/representations/vector_heap.h | 170 ++ .../ext_zz/phat/representations/vector_list.h | 101 + .../ext_zz/phat/representations/vector_set.h | 99 + .../phat/representations/vector_vector.h | 107 + src/Zigzag_persistence/example/CMakeLists.txt | 6 + .../example/comparison_for_tests.cpp | 130 +- .../example_oscillating_rips_persistence.cpp | 95 + .../gudhi/Oscillating_rips_persistence.h | 93 + .../include/gudhi/Zigzag_persistence.h | 69 +- .../oscillating_rips_iterators.h | 48 +- 78 files changed, 12854 insertions(+), 42 deletions(-) create mode 100644 src/Zigzag_persistence/benchmark/CMakeLists.txt create mode 100644 src/Zigzag_persistence/benchmark/Oscillating_rips_benchmark.cpp create mode 100644 src/Zigzag_persistence/benchmark/Oscillating_rips_comp_benchmark.cpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.hpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.hpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/cnpy.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.hpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/common.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/diagram.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.hpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/dlog/progress.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/q.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/z2.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/zp.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/filtration.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/format.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.cc create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.hpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/grid.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/point.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/vertices.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/matrix-filtration.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.hpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/opts/opts.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/ordinary-persistence.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/pair-recorder.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.hpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduction.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.hpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.hpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.hpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/simplex.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.hpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.hpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/trails-chains.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.hpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.cpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/chunk_reduction.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/row_reduction.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/spectral_sequence_reduction.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/standard_reduction.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/twist_reduction.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/boundary_matrix.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/compute_persistence_pairs.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/dualize.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/misc.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/thread_local_storage.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/persistence_pairs.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/abstract_pivot_column.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/bit_tree_pivot_column.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/full_pivot_column.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/heap_pivot_column.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/sparse_pivot_column.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_heap.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_list.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_set.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_vector.h create mode 100644 src/Zigzag_persistence/example/example_oscillating_rips_persistence.cpp create mode 100644 src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h diff --git a/src/Zigzag_persistence/benchmark/CMakeLists.txt b/src/Zigzag_persistence/benchmark/CMakeLists.txt new file mode 100644 index 0000000000..3d21cbe72a --- /dev/null +++ b/src/Zigzag_persistence/benchmark/CMakeLists.txt @@ -0,0 +1,19 @@ +project(Zigzag_benchmark) + +find_package(benchmark REQUIRED) + +add_executable(Oscillating_rips_benchmark Oscillating_rips_benchmark.cpp) +target_link_libraries(Oscillating_rips_benchmark benchmark::benchmark) +if(TARGET TBB::tbb) + target_link_libraries(Oscillating_rips_benchmark TBB::tbb) +endif() + +add_executable(Comp_benchmark Oscillating_rips_comp_benchmark.cpp ./ext_zz/fzz/fzz.cpp) +target_link_libraries(Comp_benchmark benchmark::benchmark) +target_include_directories(Comp_benchmark PUBLIC . ./ext_zz) +if(TARGET TBB::tbb) + target_link_libraries(Comp_benchmark TBB::tbb) +endif() +target_compile_options(Comp_benchmark PUBLIC "-fopenmp") +target_link_options(Comp_benchmark PUBLIC "-fopenmp") + diff --git a/src/Zigzag_persistence/benchmark/Oscillating_rips_benchmark.cpp b/src/Zigzag_persistence/benchmark/Oscillating_rips_benchmark.cpp new file mode 100644 index 0000000000..179eecaebb --- /dev/null +++ b/src/Zigzag_persistence/benchmark/Oscillating_rips_benchmark.cpp @@ -0,0 +1,213 @@ +/* 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): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#include +#include +#include +#include // for pair +#include + +#include +#include +#include +#include +#include +#include + +using ST = Gudhi::Simplex_tree; +using Filtration_value = ST::Filtration_value; +using Simplex_handle = ST::Simplex_handle; +using Square = Gudhi::zigzag_persistence::Square_root_edge_modifier; +using ZE = Gudhi::zigzag_persistence::Zigzag_edge; +using ORE = Gudhi::zigzag_persistence::Oscillating_rips_edge_range; +using OR = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range; +using ORv = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range::iterator>; +using Point = std::vector; + +std::vector build_point_cloud(unsigned int numberOfPoints, int seed) { + std::vector finalPoints; + std::set points; + std::random_device dev; + std::mt19937 rng(dev()); + if (seed > -1) rng.seed(seed); + std::uniform_real_distribution dist(0, 10); + + for (unsigned int i = 0; i < numberOfPoints; ++i) { + auto res = points.insert({dist(rng), dist(rng)}); + while (!res.second) { + res = points.insert({dist(rng), dist(rng)}); + } + finalPoints.push_back(*res.first); + } + + return finalPoints; +} + +// static void ORP_with_custom_iterator(benchmark::State& state) { +// double nu = state.range(0); +// double mu = state.range(1); +// int maxDim = state.range(2); +// unsigned int numberOfPoints = state.range(3); +// int seed = 0; + +// std::vector points = build_point_cloud(numberOfPoints, seed); + +// for (auto _ : state) { +// auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence(points, nu, mu, maxDim, ORE::Order_policy::FARTHEST_POINT_ORDERING); +// } +// } +// BENCHMARK(ORP_with_custom_iterator) +// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), +// benchmark::CreateDenseRange(3, 5, 1), +// benchmark::CreateRange(2, 5, 4), +// benchmark::CreateRange(5, 5, 4)}) +// ->Unit(benchmark::kMicrosecond); +// BENCHMARK(ORP_with_custom_iterator) +// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), +// benchmark::CreateDenseRange(3, 5, 1), +// benchmark::CreateRange(4, 50, 4), +// benchmark::CreateRange(20, 64, 4)}) +// ->Unit(benchmark::kMillisecond); +// BENCHMARK(ORP_with_custom_iterator) +// ->ArgsProduct({benchmark::CreateDenseRange(1, 2, 1), +// benchmark::CreateDenseRange(3, 4, 1), +// benchmark::CreateRange(4, 50, 4), +// benchmark::CreateRange(256, 500, 4)}) +// ->Unit(benchmark::kMillisecond); + +// static void ORP_with_vector_iterator(benchmark::State& state) { +// double nu = state.range(0); +// double mu = state.range(1); +// int maxDim = state.range(2); +// unsigned int numberOfPoints = state.range(3); +// int seed = 0; + +// std::vector points = build_point_cloud(numberOfPoints, seed); + +// for (auto _ : state) { +// auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence< +// std::vector, Gudhi::zigzag_persistence::Edge_range_type::VECTOR>(points, nu, mu, maxDim, ORE::Order_policy::FARTHEST_POINT_ORDERING); +// } +// } +// BENCHMARK(ORP_with_vector_iterator) +// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), +// benchmark::CreateDenseRange(3, 5, 1), +// benchmark::CreateRange(2, 5, 4), +// benchmark::CreateRange(5, 5, 4)}) +// ->Unit(benchmark::kMicrosecond); +// BENCHMARK(ORP_with_vector_iterator) +// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), +// benchmark::CreateDenseRange(3, 5, 1), +// benchmark::CreateRange(4, 50, 4), +// benchmark::CreateRange(20, 64, 4)}) +// ->Unit(benchmark::kMillisecond); +// BENCHMARK(ORP_with_vector_iterator) +// ->ArgsProduct({benchmark::CreateDenseRange(1, 2, 1), +// benchmark::CreateDenseRange(3, 4, 1), +// benchmark::CreateRange(4, 50, 4), +// benchmark::CreateRange(256, 500, 4)}) +// ->Unit(benchmark::kMillisecond); + +// static void ORP_with_custom_iterator_random(benchmark::State& state) { +// double nu = state.range(0); +// double mu = state.range(1); +// int maxDim = state.range(2); +// unsigned int numberOfPoints = state.range(3); +// int seed = 0; + +// std::vector points = build_point_cloud(numberOfPoints, seed); + +// for (auto _ : state) { +// auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence(points, nu, mu, maxDim, ORE::Order_policy::RANDOM_POINT_ORDERING); +// } +// } +// BENCHMARK(ORP_with_custom_iterator_random) +// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), +// benchmark::CreateDenseRange(3, 5, 1), +// benchmark::CreateRange(4, 50, 4), +// benchmark::CreateDenseRange(5, 15, 5)}) +// ->Unit(benchmark::kMillisecond); +// BENCHMARK(ORP_with_custom_iterator_random) +// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), +// benchmark::CreateDenseRange(3, 4, 1), +// benchmark::CreateRange(4, 50, 4), +// benchmark::CreateDenseRange(20, 50, 30)}) +// ->Unit(benchmark::kMillisecond); + +static void ORP_with_vector_iterator_random(benchmark::State& state) { + double nu = state.range(0); + double mu = state.range(1); + int maxDim = state.range(2); + unsigned int numberOfPoints = state.range(3); + int seed = 0; + + std::vector points = build_point_cloud(numberOfPoints, seed); + + for (auto _ : state) { + auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence< + std::vector, Gudhi::zigzag_persistence::Edge_range_type::VECTOR>(points, nu, mu, maxDim, ORE::Order_policy::RANDOM_POINT_ORDERING); + } +} +BENCHMARK(ORP_with_vector_iterator_random) + ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), + benchmark::CreateDenseRange(3, 5, 1), + benchmark::CreateRange(4, 50, 4), + benchmark::CreateDenseRange(5, 15, 5)}) + ->Unit(benchmark::kMillisecond); +BENCHMARK(ORP_with_vector_iterator_random) + ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), + benchmark::CreateDenseRange(3, 4, 1), + benchmark::CreateRange(4, 50, 4), + benchmark::CreateDenseRange(20, 50, 30)}) + ->Unit(benchmark::kMillisecond); + +// static void ORP_with_custom_iterator_ordered(benchmark::State& state) { +// double nu = state.range(0); +// double mu = state.range(1); +// int maxDim = state.range(2); +// unsigned int numberOfPoints = state.range(3); +// int seed = 0; + +// std::vector points = build_point_cloud(numberOfPoints, seed); + +// for (auto _ : state) { +// auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence(points, nu, mu, maxDim, ORE::Order_policy::ALREADY_ORDERED); +// } +// } +// BENCHMARK(ORP_with_custom_iterator_ordered) +// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), +// benchmark::CreateDenseRange(3, 5, 1), +// benchmark::CreateRange(4, 50, 4), +// benchmark::CreateRange(5, 50, 4)}) +// ->Unit(benchmark::kMillisecond); + +// static void ORP_with_vector_iterator_ordered(benchmark::State& state) { +// double nu = state.range(0); +// double mu = state.range(1); +// int maxDim = state.range(2); +// unsigned int numberOfPoints = state.range(3); +// int seed = 0; + +// std::vector points = build_point_cloud(numberOfPoints, seed); + +// for (auto _ : state) { +// auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence< +// std::vector, Gudhi::zigzag_persistence::Edge_range_type::VECTOR>(points, nu, mu, maxDim, ORE::Order_policy::ALREADY_ORDERED); +// } +// } +// BENCHMARK(ORP_with_vector_iterator_ordered) +// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), +// benchmark::CreateDenseRange(3, 5, 1), +// benchmark::CreateRange(4, 50, 4), +// benchmark::CreateRange(5, 50, 4)}) +// ->Unit(benchmark::kMillisecond); + +BENCHMARK_MAIN(); + diff --git a/src/Zigzag_persistence/benchmark/Oscillating_rips_comp_benchmark.cpp b/src/Zigzag_persistence/benchmark/Oscillating_rips_comp_benchmark.cpp new file mode 100644 index 0000000000..5e8f512f61 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/Oscillating_rips_comp_benchmark.cpp @@ -0,0 +1,250 @@ +/* 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): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#include +#include +#include +#include // for pair +#include + +#include +#include +#include +#include +#include +#include + +#include "ext_zz/fzz/fzz.h" +#include "ext_zz/dionysus/simplex.h" +#include "ext_zz/dionysus/filtration.h" +#include "ext_zz/dionysus/zigzag-persistence.h" + +using ST = Gudhi::Simplex_tree; +using Vertex_handle = ST::Vertex_handle; +using Filtration_value = ST::Filtration_value; +using Simplex_handle = ST::Simplex_handle; +using Square = Gudhi::zigzag_persistence::Square_root_edge_modifier; +using ZE = Gudhi::zigzag_persistence::Zigzag_edge; +using ORE = Gudhi::zigzag_persistence::Oscillating_rips_edge_range; +using OR = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range; +using ORv = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range::const_iterator>; +using Point = std::vector; + +using DField = dionysus::Z2Field; +using Simplex = dionysus::Simplex<>; +using DFiltration = dionysus::Filtration; +using DZZ = dionysus::ZigzagPersistence; +using DIndex = typename DZZ::Index; +using DChain = dionysus::ChainEntry; +using DIChain = dionysus::ChainEntry; + +std::vector build_point_cloud(unsigned int numberOfPoints, int seed) { + std::vector finalPoints; + std::set points; + std::random_device dev; + std::mt19937 rng(dev()); + if (seed > -1) rng.seed(seed); + std::uniform_real_distribution dist(0, 10); + + for (unsigned int i = 0; i < numberOfPoints; ++i) { + auto res = points.insert({dist(rng), dist(rng)}); + while (!res.second) { + res = points.insert({dist(rng), dist(rng)}); + } + finalPoints.push_back(*res.first); + } + + return finalPoints; +} + +std::vector > compute_with_dionysus(const std::vector& edges, int maxDim) { + ST st; + DField k; + std::unordered_map indices; + DZZ persistence(k); + std::vector > res; + + std::set essentials; + + unsigned int op = 0; + unsigned int idx = 0; + + auto start = edges.begin(); + auto end = edges.end(); + for (const auto& t : ORv::get_iterator_range(start, end, st, maxDim)) { + auto r = st.simplex_vertex_range(std::get<0>(t)); + std::vector simplex(r.begin(), r.end()); + Simplex c(simplex); + DIndex pair; + if (std::get<2>(t)) { + indices.try_emplace(c, idx++); + // int dim = boost::distance(c.boundary(persistence.field())); + // dim = dim == 0 ? 0 : dim -1; + // fmt::print("[{}] Adding: {} : {}\n", op, c, dim); + pair = + persistence.add(c.boundary(persistence.field()) | boost::adaptors::transformed([&indices](const DChain& e) { + return DIChain(e.element(), indices.find(e.index())->second); + })); + } else { + // fmt::print("[{}] Removing: {} : {}\n", op, c, boost::distance(c.boundary(persistence.field())) - 1); + auto idxIt = indices.find(c); + pair = persistence.remove(idxIt->second); + indices.erase(idxIt); + } + + if (pair != DZZ::unpaired()) { + // fmt::print("{} - {}\n", pair, op); + res.emplace_back(pair, op); + essentials.erase(pair); + } else { + essentials.insert(essentials.end(), op); + } + op++; + } + + for (unsigned int v : essentials) { + // fmt::print("{} - inf\n", v); + res.emplace_back(v, op); + } + + return res; +} + +std::vector > compute_with_fzz(const std::vector& edges, + int maxDim) { + std::vector > persistence; + FZZ::FastZigzag fzz; + std::vector > simplices; + std::vector dirs; + ST st; + + auto start = edges.begin(); + auto end = edges.end(); + for (const auto& t : ORv::get_iterator_range(start, end, st, maxDim)){ + auto r = st.simplex_vertex_range(std::get<0>(t)); + simplices.emplace_back(r.begin(), r.end()); + dirs.push_back(std::get<2>(t)); + } + + fzz.compute(simplices, dirs, &persistence); + + std::sort(persistence.begin(), persistence.end(), + [](const std::tuple& p1, + const std::tuple& p2) { + if (std::get<1>(p1) == std::get<1>(p2)) { + return std::get<0>(p1) < std::get<0>(p2); + } + + return std::get<1>(p1) < std::get<1>(p2); + }); + + return persistence; +} + +static void ORP_with_gudhi(benchmark::State& state) { + double nu = state.range(0); + double mu = state.range(1); + int maxDim = state.range(2); + unsigned int numberOfPoints = state.range(3); + int seed = 0; + + std::vector points = build_point_cloud(numberOfPoints, seed); + + for (auto _ : state) { + auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence(points, nu, mu, maxDim); + } +} +BENCHMARK(ORP_with_gudhi) + ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), + benchmark::CreateDenseRange(3, 5, 1), + benchmark::CreateRange(2, 5, 4), + benchmark::CreateRange(5, 5, 4)}) + ->Unit(benchmark::kMicrosecond); +BENCHMARK(ORP_with_gudhi) + ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), + benchmark::CreateDenseRange(3, 5, 1), + benchmark::CreateRange(4, 50, 4), + benchmark::CreateRange(20, 64, 4)}) + ->Unit(benchmark::kMillisecond); +// BENCHMARK(ORP_with_gudhi) +// ->ArgsProduct({benchmark::CreateDenseRange(1, 2, 1), +// benchmark::CreateDenseRange(3, 4, 1), +// benchmark::CreateRange(4, 50, 4), +// benchmark::CreateRange(256, 500, 4)}) +// ->Unit(benchmark::kMillisecond); + +static void ORP_with_dionysus(benchmark::State& state) { + double nu = state.range(0); + double mu = state.range(1); + int maxDim = state.range(2); + unsigned int numberOfPoints = state.range(3); + int seed = 0; + + std::vector points = build_point_cloud(numberOfPoints, seed); + + for (auto _ : state) { + auto vec = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance()); + auto res = compute_with_dionysus(vec, maxDim); + } +} +BENCHMARK(ORP_with_dionysus) + ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), + benchmark::CreateDenseRange(3, 5, 1), + benchmark::CreateRange(2, 5, 4), + benchmark::CreateRange(5, 5, 4)}) + ->Unit(benchmark::kMicrosecond); +BENCHMARK(ORP_with_dionysus) + ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), + benchmark::CreateDenseRange(3, 5, 1), + benchmark::CreateRange(4, 50, 4), + benchmark::CreateRange(20, 64, 4)}) + ->Unit(benchmark::kMillisecond); +// BENCHMARK(ORP_with_dionysus) +// ->ArgsProduct({benchmark::CreateDenseRange(1, 2, 1), +// benchmark::CreateDenseRange(3, 4, 1), +// benchmark::CreateRange(4, 50, 4), +// benchmark::CreateRange(256, 500, 4)}) +// ->Unit(benchmark::kMillisecond); + +static void ORP_with_fzz(benchmark::State& state) { + double nu = state.range(0); + double mu = state.range(1); + int maxDim = state.range(2); + unsigned int numberOfPoints = state.range(3); + int seed = 0; + + std::vector points = build_point_cloud(numberOfPoints, seed); + + for (auto _ : state) { + auto vec = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance()); + auto res = compute_with_fzz(vec, maxDim); + } +} +BENCHMARK(ORP_with_fzz) + ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), + benchmark::CreateDenseRange(3, 5, 1), + benchmark::CreateRange(2, 5, 4), + benchmark::CreateRange(5, 5, 4)}) + ->Unit(benchmark::kMicrosecond); +BENCHMARK(ORP_with_fzz) + ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), + benchmark::CreateDenseRange(3, 5, 1), + benchmark::CreateRange(4, 50, 4), + benchmark::CreateRange(20, 64, 4)}) + ->Unit(benchmark::kMillisecond); +// BENCHMARK(ORP_with_fzz) +// ->ArgsProduct({benchmark::CreateDenseRange(1, 2, 1), +// benchmark::CreateDenseRange(3, 4, 1), +// benchmark::CreateRange(4, 50, 4), +// benchmark::CreateRange(256, 500, 4)}) +// ->Unit(benchmark::kMillisecond); + +BENCHMARK_MAIN(); + diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.h new file mode 100644 index 0000000000..00c983623a --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.h @@ -0,0 +1,153 @@ +#ifndef DIONYSUS_CHAIN_H +#define DIONYSUS_CHAIN_H + +#include +#include +#include + +#include "fields/z2.h" + +namespace dionysus +{ + +template +struct FieldElement +{ + typedef typename Field::Element Element; + FieldElement(Element e_): + e(e_) {} + Element element() const { return e; } + void set_element(Element e_) { e = e_; } + Element e; +}; + +template<> +struct FieldElement +{ + typedef Z2Field::Element Element; + FieldElement(Element) {} + Element element() const { return Z2Field::id(); } + void set_element(Element) {} +}; + +template +struct ChainEntry: public FieldElement, public Extra... +{ + typedef Field_ Field; + typedef Index_ Index; + + typedef FieldElement Parent; + typedef typename Parent::Element Element; + + ChainEntry(): Parent(Element()), i(Index()) {} // need for serialization + + ChainEntry(ChainEntry&& other) = default; + ChainEntry(const ChainEntry& other) = default; + ChainEntry& operator=(ChainEntry&& other) = default; + + ChainEntry(Element e_, const Index& i_): + Parent(e_), i(i_) {} + + ChainEntry(Element e_, Index&& i_): + Parent(e_), i(std::move(i_)) {} + + const Index& index() const { return i; } + Index& index() { return i; } + + // debug + bool operator==(const ChainEntry& other) const { return i == other.i; } + + Index i; +}; + +template +struct Chain +{ + struct Visitor + { + template + void first(Iter it) const {} + + template + void second(Iter it) const {} + + template + void equal_keep(Iter it) const {} + + template + void equal_drop(Iter it) const {} + }; + + // x += a*y + template + static void addto(C1& x, typename Field::Element a, const C2& y, const Field& field, const Cmp& cmp, const Visitor_& = Visitor_()); +}; + +template +struct Chain> +{ + struct Visitor + { + template + void first(Iter it) const {} + + template + void second(Iter it) const {} + + template + void equal_keep(Iter it) const {} + + template + void equal_drop(Iter it) const {} + }; + + // x += a*y + template + static void addto(std::list& x, typename Field::Element a, const C2& y, + const Field& field, const Cmp& cmp, const Visitor_& visitor = Visitor_()); +}; + + +template +struct Chain> +{ + struct Visitor + { + template + void first(Iter it) const {} + + template + void second(Iter it) const {} + + template + void equal_keep(Iter it) const {} + + template + void equal_drop(Iter it) const {} + }; + + // x += a*y + template + static void addto(std::set& x, typename Field::Element a, const C2& y, + const Field& field, const Cmp& cmp, const Visitor_& = Visitor_()); + + template + static void addto(std::set& x, typename Field::Element a, T&& y, + const Field& field, const Cmp& cmp, const Visitor_& = Visitor_()); +}; + +} + +//namespace std +//{ +// template +// void swap(::dionysus::ChainEntry& x, ::dionysus::ChainEntry& y) +// { +// std::swap(x.e, y.e); +// std::swap(x.i, y.i); +// } +//} + +#include "chain.hpp" + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.hpp new file mode 100644 index 0000000000..4da9f44615 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.hpp @@ -0,0 +1,188 @@ +template +template +void +dionysus::Chain>:: +addto(std::list& x, typename Field::Element a, const C2& y, const Field& field, const Cmp& cmp, const Visitor_& visitor) +{ + typedef typename Field::Element Element; + + auto cur_x = std::begin(x), + end_x = std::end(x); + auto cur_y = std::begin(y), + end_y = std::end(y); + + while (cur_x != end_x && cur_y != end_y) + { + if (cmp(cur_x->index(), cur_y->index())) + { + visitor.first(cur_x++); + } else if (cmp(cur_y->index(), cur_x->index())) + { + // multiply and add + Element ay = field.mul(a, cur_y->element()); + auto nw_x = x.insert(cur_x, *cur_y); + nw_x->set_element(ay); + ++cur_y; + visitor.second(nw_x); + } else + { + Element ay = field.mul(a, cur_y->element()); + Element r = field.add(cur_x->element(), ay); + if (field.is_zero(r)) + { + visitor.equal_drop(cur_x); + x.erase(cur_x++); + } + else + { + cur_x->set_element(r); + visitor.equal_keep(cur_x); + ++cur_x; + } + ++cur_y; + } + } + + for (auto it = cur_y; it != end_y; ++it) + { + Element ay = field.mul(a, it->element()); + x.push_back(*it); + x.back().set_element(ay); + visitor.second(--x.end()); + } +} + +template +template +void +dionysus::Chain>:: +addto(std::set& x, typename Field::Element a, const C2& y, const Field& field, const Cmp&, const Visitor_& visitor) +{ + typedef typename Field::Element Element; + + auto cur_y = std::begin(y), + end_y = std::end(y); + + while (cur_y != end_y) + { + auto cur_x = x.find(*cur_y); + if (cur_x == x.end()) + { + auto nw = x.insert(*cur_y).first; + Element ay = field.mul(a, nw->element()); + const_cast(*nw).set_element(ay); + visitor.second(nw); + } else + { + Element ay = field.mul(a, cur_y->element()); + Element r = field.add(cur_x->element(), ay); + if (field.is_zero(r)) + { + visitor.equal_drop(cur_x); + x.erase(cur_x); + } + else + { + const_cast(*cur_x).set_element(r); + visitor.equal_keep(cur_x); + } + } + ++cur_y; + } +} + +template +template +void +dionysus::Chain>:: +addto(std::set& x, typename Field::Element a, T&& y, const Field& field, const Cmp&, const Visitor_& visitor) +{ + typedef typename Field::Element Element; + + auto cur_x = x.find(y); + if (cur_x == x.end()) + { + auto nw = x.insert(std::move(y)).first; + Element ay = field.mul(a, nw->element()); + const_cast(*nw).set_element(ay); + visitor.second(nw); + } else + { + Element ay = field.mul(a, y.element()); + Element r = field.add(cur_x->element(), ay); + if (field.is_zero(r)) + { + visitor.equal_drop(cur_x); + x.erase(cur_x); + } + else + { + const_cast(*cur_x).set_element(r); + visitor.equal_keep(cur_x); + } + } +} + +template +template +void +dionysus::Chain:: +addto(C1& x, typename Field::Element a, const C2& y, const Field& field, const Cmp& cmp, const Visitor_& visitor) +{ + typedef typename Field::Element Element; + + C1 res; + + auto cur_x = std::begin(x), + end_x = std::end(x); + auto cur_y = std::begin(y), + end_y = std::end(y); + + while (cur_x != end_x && cur_y != end_y) + { + if (cmp(*cur_x, *cur_y)) + { + res.emplace_back(std::move(*cur_x)); + visitor.first(--res.end()); + ++cur_x; + } else if (cmp(*cur_y, *cur_x)) + { + // multiply and add + Element ay = field.mul(a, cur_y->element()); + res.emplace_back(ay, cur_y->index()); + visitor.second(--res.end()); + ++cur_y; + } else + { + Element ay = field.mul(a, cur_y->element()); + Element r = field.add(cur_x->element(), ay); + if (field.is_zero(r)) + visitor.equal_drop(cur_x); + else + { + res.emplace_back(std::move(*cur_x)); + res.back().set_element(r); + visitor.equal_keep(--res.end()); + } + ++cur_x; + ++cur_y; + } + } + + while (cur_y != end_y) + { + Element ay = field.mul(a, cur_y->element()); + res.emplace_back(ay, cur_y->index()); + visitor.second(--res.end()); + ++cur_y; + } + + while (cur_x != end_x) + { + res.emplace_back(std::move(*cur_x)); + visitor.first(--res.end()); + ++cur_x; + } + + x.swap(res); +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.h new file mode 100644 index 0000000000..8651e9a69a --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.h @@ -0,0 +1,45 @@ +#ifndef DIONYSUS_CLEARING_REDUCTION_H +#define DIONYSUS_CLEARING_REDUCTION_H + +namespace dionysus +{ + +// Mid-level interface +template +class ClearingReduction +{ + public: + using Persistence = Persistence_; + using Field = typename Persistence::Field; + using Index = typename Persistence::Index; + + public: + ClearingReduction(Persistence& persistence): + persistence_(persistence) {} + + template + void operator()(const Filtration& f, const Relative& relative, const ReportPair& report_pair, const Progress& progress); + + template + void operator()(const Filtration& f, const ReportPair& report_pair); + + template + void operator()(const Filtration& f) { return (*this)(f, &no_report_pair); } + + static void no_report_pair(int, Index, Index) {} + static void no_progress() {} + + const Persistence& + persistence() const { return persistence_; } + Persistence& persistence() { return persistence_; } + + private: + Persistence& persistence_; +}; + +} + +#include "clearing-reduction.hpp" + +#endif + diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.hpp new file mode 100644 index 0000000000..ceac11879d --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.hpp @@ -0,0 +1,60 @@ +#include +#include + +#include +namespace ba = boost::adaptors; + +template +template +void +dionysus::ClearingReduction

:: +operator()(const Filtration& filtration, const ReportPair& report_pair) +{ + using Cell = typename Filtration::Cell; + (*this)(filtration, [](const Cell&) { return false; }, report_pair, &no_progress); +} + +template +template +void +dionysus::ClearingReduction

:: +operator()(const Filtration& filtration, const Relative& relative, const ReportPair& report_pair, const Progress& progress) +{ + persistence_.resize(filtration.size()); + + // sort indices by decreasing dimension + std::vector indices(filtration.size()); + std::iota(indices.begin(), indices.end(), 0); + std::stable_sort(indices.begin(), indices.end(), + [&filtration](size_t x, size_t y) + { return filtration[x].dimension() > filtration[y].dimension(); }); + + typedef typename Filtration::Cell Cell; + typedef ChainEntry CellChainEntry; + typedef ChainEntry ChainEntry; + + for(size_t i : indices) + { + progress(); + const auto& c = filtration[i]; + + if (relative(c)) + { + persistence_.set_skip(i); + continue; + } + + if (persistence_.pair(i) != persistence_.unpaired()) + continue; + + persistence_.set(i, c.boundary(persistence_.field()) | + ba::filtered([relative](const CellChainEntry& e) { return !relative(e.index()); }) | + ba::transformed([this,&filtration](const CellChainEntry& e) + { return ChainEntry(e.element(), filtration.index(e.index())); })); + + Index pair = persistence_.reduce(i); + if (pair != persistence_.unpaired()) + report_pair(c.dimension(), pair, i); + } +} + diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cnpy.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cnpy.h new file mode 100644 index 0000000000..b11013b9d7 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cnpy.h @@ -0,0 +1,241 @@ +//Copyright (C) 2011 Carl Rogers +//Released under MIT License +//license available in LICENSE file, or at http://www.opensource.org/licenses/mit-license.php + +#ifndef LIBCNPY_H_ +#define LIBCNPY_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cnpy { + + struct NpyArray { + char* data; + std::vector shape; + unsigned int word_size; + bool fortran_order; + void destruct() {delete[] data;} + }; + + struct npz_t : public std::map + { + void destruct() + { + npz_t::iterator it = this->begin(); + for(; it != this->end(); ++it) (*it).second.destruct(); + } + }; + + char BigEndianTest(); + char map_type(const std::type_info& t); + template std::vector create_npy_header(const T* data, const unsigned int* shape, const unsigned int ndims); + void parse_npy_header(FILE* fp,unsigned int& word_size, unsigned int*& shape, unsigned int& ndims, bool& fortran_order); + void parse_zip_footer(FILE* fp, unsigned short& nrecs, unsigned int& global_header_size, unsigned int& global_header_offset); + npz_t npz_load(std::string fname); + NpyArray npz_load(std::string fname, std::string varname); + NpyArray npy_load(std::string fname); + + template std::vector& operator+=(std::vector& lhs, const T rhs) { + //write in little endian + for(char byte = 0; byte < sizeof(T); byte++) { + char val = *((char*)&rhs+byte); + lhs.push_back(val); + } + return lhs; + } + + template<> std::vector& operator+=(std::vector& lhs, const std::string rhs); + template<> std::vector& operator+=(std::vector& lhs, const char* rhs); + + + template std::string tostring(T i, int pad = 0, char padval = ' ') { + std::stringstream s; + s << i; + return s.str(); + } + + template void npy_save(std::string fname, const T* data, const unsigned int* shape, const unsigned int ndims, std::string mode = "w") { + FILE* fp = NULL; + + if(mode == "a") fp = fopen(fname.c_str(),"r+b"); + + if(fp) { + //file exists. we need to append to it. read the header, modify the array size + unsigned int word_size, tmp_dims; + unsigned int* tmp_shape = 0; + bool fortran_order; + parse_npy_header(fp,word_size,tmp_shape,tmp_dims,fortran_order); + assert(!fortran_order); + + if(word_size != sizeof(T)) { + std::cout<<"libnpy error: "< header = create_npy_header(data,tmp_shape,ndims); + fwrite(&header[0],sizeof(char),header.size(),fp); + fseek(fp,0,SEEK_END); + + delete[] tmp_shape; + } + else { + fp = fopen(fname.c_str(),"wb"); + std::vector header = create_npy_header(data,shape,ndims); + fwrite(&header[0],sizeof(char),header.size(),fp); + } + + unsigned int nels = 1; + for(int i = 0;i < ndims;i++) nels *= shape[i]; + + fwrite(data,sizeof(T),nels,fp); + fclose(fp); + } + + template void npz_save(std::string zipname, std::string fname, const T* data, const unsigned int* shape, const unsigned int ndims, std::string mode = "w") + { + //first, append a .npy to the fname + fname += ".npy"; + + //now, on with the show + FILE* fp = NULL; + unsigned short nrecs = 0; + unsigned int global_header_offset = 0; + std::vector global_header; + + if(mode == "a") fp = fopen(zipname.c_str(),"r+b"); + + if(fp) { + //zip file exists. we need to add a new npy file to it. + //first read the footer. this gives us the offset and size of the global header + //then read and store the global header. + //below, we will write the the new data at the start of the global header then append the global header and footer below it + unsigned int global_header_size; + parse_zip_footer(fp,nrecs,global_header_size,global_header_offset); + fseek(fp,global_header_offset,SEEK_SET); + global_header.resize(global_header_size); + size_t res = fread(&global_header[0],sizeof(char),global_header_size,fp); + if(res != global_header_size){ + throw std::runtime_error("npz_save: header read error while adding to existing zip"); + } + fseek(fp,global_header_offset,SEEK_SET); + } + else { + fp = fopen(zipname.c_str(),"wb"); + } + + std::vector npy_header = create_npy_header(data,shape,ndims); + + unsigned long nels = 1; + for (int m=0; m local_header; + local_header += "PK"; //first part of sig + local_header += (unsigned short) 0x0403; //second part of sig + local_header += (unsigned short) 20; //min version to extract + local_header += (unsigned short) 0; //general purpose bit flag + local_header += (unsigned short) 0; //compression method + local_header += (unsigned short) 0; //file last mod time + local_header += (unsigned short) 0; //file last mod date + local_header += (unsigned int) crc; //crc + local_header += (unsigned int) nbytes; //compressed size + local_header += (unsigned int) nbytes; //uncompressed size + local_header += (unsigned short) fname.size(); //fname length + local_header += (unsigned short) 0; //extra field length + local_header += fname; + + //build global header + global_header += "PK"; //first part of sig + global_header += (unsigned short) 0x0201; //second part of sig + global_header += (unsigned short) 20; //version made by + global_header.insert(global_header.end(),local_header.begin()+4,local_header.begin()+30); + global_header += (unsigned short) 0; //file comment length + global_header += (unsigned short) 0; //disk number where file starts + global_header += (unsigned short) 0; //internal file attributes + global_header += (unsigned int) 0; //external file attributes + global_header += (unsigned int) global_header_offset; //relative offset of local file header, since it begins where the global header used to begin + global_header += fname; + + //build footer + std::vector footer; + footer += "PK"; //first part of sig + footer += (unsigned short) 0x0605; //second part of sig + footer += (unsigned short) 0; //number of this disk + footer += (unsigned short) 0; //disk where footer starts + footer += (unsigned short) (nrecs+1); //number of records on this disk + footer += (unsigned short) (nrecs+1); //total number of records + footer += (unsigned int) global_header.size(); //nbytes of global headers + footer += (unsigned int) (global_header_offset + nbytes + local_header.size()); //offset of start of global headers, since global header now starts after newly written array + footer += (unsigned short) 0; //zip file comment length + + //write everything + fwrite(&local_header[0],sizeof(char),local_header.size(),fp); + fwrite(&npy_header[0],sizeof(char),npy_header.size(),fp); + fwrite(data,sizeof(T),nels,fp); + fwrite(&global_header[0],sizeof(char),global_header.size(),fp); + fwrite(&footer[0],sizeof(char),footer.size(),fp); + fclose(fp); + } + + template std::vector create_npy_header(const T* data, const unsigned int* shape, const unsigned int ndims) { + + std::vector dict; + dict += "{'descr': '"; + dict += BigEndianTest(); + dict += map_type(typeid(T)); + dict += tostring(sizeof(T)); + dict += "', 'fortran_order': False, 'shape': ("; + dict += tostring(shape[0]); + for(int i = 1;i < ndims;i++) { + dict += ", "; + dict += tostring(shape[i]); + } + if(ndims == 1) dict += ","; + dict += "), }"; + //pad with spaces so that preamble+dict is modulo 16 bytes. preamble is 10 bytes. dict needs to end with \n + int remainder = 16 - (10 + dict.size()) % 16; + dict.insert(dict.end(),remainder,' '); + dict.back() = '\n'; + + std::vector header; + header += (char) 0x93; + header += "NUMPY"; + header += (char) 0x01; //major version of numpy format + header += (char) 0x00; //minor version of numpy format + header += (unsigned short) dict.size(); + header.insert(header.end(),dict.begin(),dict.end()); + + return header; + } + + +} + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.h new file mode 100644 index 0000000000..8d2019e89d --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.h @@ -0,0 +1,116 @@ +#ifndef DIONYSUS_COHOMOLOGY_PERSISTENCE_H +#define DIONYSUS_COHOMOLOGY_PERSISTENCE_H + +#include +#include + +#include +namespace bi = boost::intrusive; + +#include "reduction.h" +#include "chain.h" + +namespace dionysus +{ + +template> +class CohomologyPersistence +{ + public: + typedef Field_ Field; + typedef Index_ Index; + typedef Comparison_ Comparison; + + typedef typename Field::Element FieldElement; + + typedef bi::list_base_hook> auto_unlink_hook; + struct Entry; + struct ColumnHead; + + typedef std::vector Column; + typedef bi::list> Row; + typedef std::list Columns; + typedef typename Columns::iterator ColumnsIterator; + typedef Column Chain; + + using IndexColumn = std::tuple; + + CohomologyPersistence(const Field& field, + const Comparison& cmp = Comparison()): + field_(field), cmp_(cmp) {} + + CohomologyPersistence(Field&& field, + const Comparison& cmp = Comparison()): + field_(std::move(field)), + cmp_(cmp) {} + + CohomologyPersistence(CohomologyPersistence&& other): + field_(std::move(other.field_)), + cmp_(std::move(other.cmp_)), + columns_(std::move(other.columns_)), + rows_(std::move(other.rows_)) {} + + template + Index add(const ChainRange& chain); + + template + IndexColumn add(const ChainRange& chain, bool keep_cocycle); + + // TODO: no skip support for now + bool skip(Index) const { return false; } + void add_skip() {} + void set_skip(Index, bool flag = true) {} + + const Field& field() const { return field_; } + const Columns& columns() const { return columns_; } + void reserve(size_t s) { rows_.reserve(s); } + + struct AddtoVisitor; + + static const Index unpaired() { return Reduction::unpaired; } + + private: + Field field_; + Comparison cmp_; + Columns columns_; + std::vector rows_; +}; + + +template +struct CohomologyPersistence::ColumnHead +{ + ColumnHead(Index i): index_(i) {} + + Index index() const { return index_; } + + Index index_; + Column chain; +}; + +template +struct CohomologyPersistence::Entry: + public ChainEntry +{ + typedef ChainEntry Parent; + + Entry(FieldElement e, const Index& i): // slightly dangerous + Parent(e,i) {} + + Entry(FieldElement e, const Index& i, ColumnsIterator it): + Parent(e,i), column(it) {} + + Entry(const Entry& other) = default; + Entry(Entry&& other) = default; + + void unlink() { auto_unlink_hook::unlink(); } + bool is_linked() const { return auto_unlink_hook::is_linked(); } + + ColumnsIterator column; // TODO: I really don't like this overhead +}; + +} + +#include "cohomology-persistence.hpp" + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.hpp new file mode 100644 index 0000000000..b2334f99e1 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.hpp @@ -0,0 +1,61 @@ +template +template +typename dionysus::CohomologyPersistence::Index +dionysus::CohomologyPersistence:: +add(const ChainRange& chain) +{ + return std::get<0>(add(chain, false)); // return just the index +} + + +template +template +typename dionysus::CohomologyPersistence::IndexColumn +dionysus::CohomologyPersistence:: +add(const ChainRange& chain, bool keep_cocycle) +{ + auto entry_cmp = [this](const Entry& e1, const Entry& e2) { return this->cmp_(e1.index(), e2.index()); }; + std::set row_sum(entry_cmp); + for (auto it = std::begin(chain); it != std::end(chain); ++it) + for (auto& re : rows_[it->index()]) + dionysus::Chain::addto(row_sum, it->element(), Entry(re.element(), re.column->index(), re.column), field_, cmp_); + + if (row_sum.empty()) // Birth + { + columns_.emplace_back(rows_.size()); + auto before_end = columns_.end(); + --before_end; + columns_.back().chain.push_back(Entry(field_.id(), rows_.size(), before_end)); + rows_.emplace_back(); + rows_.back().push_back(columns_.back().chain.front()); + return std::make_tuple(unpaired(), Column()); + } else // Death + { + // Select front element in terms of comparison (rows are unsorted) + auto it = std::max_element(std::begin(row_sum), std::end(row_sum), entry_cmp); + + Entry first = std::move(*it); + row_sum.erase(it); + + for (auto& ce : row_sum) + { + FieldElement ay = field_.neg(field_.div(ce.element(), first.element())); + dionysus::Chain::addto(ce.column->chain, ay, first.column->chain, field_, + [this](const Entry& e1, const Entry& e2) + { return this->cmp_(e1.index(), e2.index()); }); + + for (auto& x : ce.column->chain) + { + x.column = ce.column; + rows_[x.index()].push_back(x); + } + } + Index pair = first.column->index(); + Column cocycle; + if (keep_cocycle) + cocycle = std::move(first.column->chain); + columns_.erase(first.column); + rows_.emplace_back(); // useless row; only present to make indices match + return std::make_tuple(pair, cocycle); + } +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/common.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/common.h new file mode 100644 index 0000000000..e012b10539 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/common.h @@ -0,0 +1,25 @@ +#ifndef DIONYSUS_EXAMPLES_COMMON_H +#define DIONYSUS_EXAMPLES_COMMON_H + +#include +#include + +template +void read_points(const std::string& infilename, PointContainer& points) +{ + typedef typename PointContainer::value_type Point; + + std::ifstream in(infilename.c_str()); + std::string line; + while(std::getline(in, line)) + { + if (line[0] == '#') continue; // comment line in the file + std::stringstream linestream(line); + double x; + points.push_back(Point()); + while (linestream >> x) + points.back().push_back(x); + } +} + +#endif // DIONYSUS_EXAMPLES_COMMON_H diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/diagram.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/diagram.h new file mode 100644 index 0000000000..04eb29a927 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/diagram.h @@ -0,0 +1,114 @@ +#ifndef DIONYSUS_DIAGRAM_H +#define DIONYSUS_DIAGRAM_H + +#include +#include +#include + +namespace dionysus +{ + +template +class Diagram +{ + public: + using Value = Value_; + using Data = Data_; + struct Point: public std::pair + { + using Parent = std::pair; + + Point(Value b, Value d, Data dd): + Parent(b,d), data(dd) {} + + Value birth() const { return Parent::first; } + Value death() const { return Parent::second; } + + // FIXME: temporary hack + Value operator[](size_t i) const { if (i == 0) return birth(); return death(); } + + Data data; + }; + + using Points = std::vector; + using iterator = typename Points::iterator; + using const_iterator = typename Points::const_iterator; + using value_type = Point; + + public: + const_iterator begin() const { return points.begin(); } + const_iterator end() const { return points.end(); } + iterator begin() { return points.begin(); } + iterator end() { return points.end(); } + + const Point& operator[](size_t i) const { return points[i]; } + + size_t size() const { return points.size(); } + void push_back(const Point& p) { points.push_back(p); } + template + void emplace_back(Args&&... args) { points.emplace_back(std::forward(args)...); } + + private: + std::vector points; +}; + +namespace detail +{ + template + struct Diagrams + { + using Value = decltype(std::declval()(std::declval())); + using Data = decltype(std::declval()(std::declval())); + using type = std::vector>; + }; +} + +template +typename detail::Diagrams::type +init_diagrams(const ReducedMatrix& m, const Filtration& f, const GetValue& get_value, const GetData& get_data) +{ + using Result = typename detail::Diagrams::type; + + Result diagrams; + for (typename ReducedMatrix::Index i = 0; i < m.size(); ++i) + { + if (m.skip(i)) + continue; + + auto s = f[i]; + auto d = s.dimension(); + + while (d + 1 > diagrams.size()) + diagrams.emplace_back(); + + auto pair = m.pair(i); + if (pair == m.unpaired()) + { + auto birth = get_value(s); + using Value = decltype(birth); + Value death = std::numeric_limits::infinity(); + diagrams[d].emplace_back(birth, death, get_data(i)); + } else if (pair > i) // positive + { + auto birth = get_value(s); + auto death = get_value(f[pair]); + + // hack to work with coboundaries + auto pd = f[pair].dimension(); + if (pd < d) + { + d = pd; + std::swap(birth, death); + } + + if (birth != death) // skip diagonal + diagrams[d].emplace_back(birth, death, get_data(i)); + } // else negative: do nothing + } + + return diagrams; +} + +} + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.h new file mode 100644 index 0000000000..29cac601af --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.h @@ -0,0 +1,93 @@ +#ifndef DIONYSUS_DISTANCES_H +#define DIONYSUS_DISTANCES_H + +#include +#include + +namespace dionysus +{ + +/** + * Class: ExplicitDistances + * Stores the pairwise distances of Distances_ instance passed at construction. + * It's a protypical Distances template argument for the Rips complex. + */ +template +class ExplicitDistances +{ + public: + typedef Distances_ Distances; + typedef size_t IndexType; + typedef typename Distances::DistanceType DistanceType; + + ExplicitDistances(IndexType size): + size_(size), + distances_(size*(size + 1)/2 + size) {} + ExplicitDistances(const Distances& distances); + + DistanceType operator()(IndexType a, IndexType b) const; + DistanceType& operator()(IndexType a, IndexType b); + + size_t size() const { return size_; } + IndexType begin() const { return 0; } + IndexType end() const { return size(); } + + private: + std::vector distances_; + size_t size_; +}; + + +/** + * Class: PairwiseDistances + * Given a Container_ of points and a Distance_, it computes distances between elements + * in the container (given as instances of Index_ defaulted to unsigned) using the Distance_ functor. + * + * Container_ is assumed to be an std::vector. That simplifies a number of things. + */ +template +class PairwiseDistances +{ + public: + typedef Container_ Container; + typedef Distance_ Distance; + typedef Index_ IndexType; + typedef typename Distance::result_type DistanceType; + + + PairwiseDistances(const Container& container, + const Distance& distance = Distance()): + container_(container), distance_(distance) {} + + DistanceType operator()(IndexType a, IndexType b) const { return distance_(container_[a], container_[b]); } + + size_t size() const { return container_.size(); } + IndexType begin() const { return 0; } + IndexType end() const { return size(); } + + private: + const Container& container_; + Distance distance_; +}; + +template +struct L2Distance +{ + typedef Point_ Point; + typedef decltype(Point()[0] + 0) result_type; + + result_type operator()(const Point& p1, const Point& p2) const + { + result_type sum = 0; + for (size_t i = 0; i < p1.size(); ++i) + sum += (p1[i] - p2[i])*(p1[i] - p2[i]); + + return sqrt(sum); + } +}; + +} + +#include "distances.hpp" + +#endif // DIONYSUS_DISTANCES_H diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.hpp new file mode 100644 index 0000000000..9b1f20aa2b --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.hpp @@ -0,0 +1,30 @@ +template +dionysus::ExplicitDistances:: +ExplicitDistances(const Distances& distances): + size_(distances.size()), distances_((distances.size() * (distances.size() + 1))/2) +{ + IndexType i = 0; + for (typename Distances::IndexType a = distances.begin(); a != distances.end(); ++a) + for (typename Distances::IndexType b = a; b != distances.end(); ++b) + { + distances_[i++] = distances(a,b); + } +} + +template +typename dionysus::ExplicitDistances::DistanceType +dionysus::ExplicitDistances:: +operator()(IndexType a, IndexType b) const +{ + if (a > b) std::swap(a,b); + return distances_[a*size_ - ((a*(a-1))/2) + (b-a)]; +} + +template +typename dionysus::ExplicitDistances::DistanceType& +dionysus::ExplicitDistances:: +operator()(IndexType a, IndexType b) +{ + if (a > b) std::swap(a,b); + return distances_[a*size_ - ((a*(a-1))/2) + (b-a)]; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/dlog/progress.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/dlog/progress.h new file mode 100644 index 0000000000..12bf86a4a2 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/dlog/progress.h @@ -0,0 +1,57 @@ +#ifndef DLOG_PROGRESS_H +#define DLOG_PROGRESS_H + +#include +#include +#include +#include + +namespace dlog +{ + +struct progress +{ + progress(size_t total): + current_(0), total_(total) { show_progress(); } + + progress& operator++() { current_++; if (current_ * 100 / total_ > (current_ - 1) * 100 / total_) show_progress(); check_done(); return *this; } + progress& operator=(size_t cur) { current_ = cur; show_progress(); check_done(); return *this; } + progress& operator()(const std::string& s) { message_ = s; show_progress(); check_done(); return *this; } + template + progress& operator()(const T& x) { std::ostringstream oss; oss << x; return (*this)(oss.str()); } + + inline void show_progress() const; + void check_done() const { if (current_ >= total_) std::cout << "\n" << std::flush; } + + private: + size_t current_, total_; + std::string message_; +}; + +} + +void +dlog::progress:: +show_progress() const +{ + int barWidth = 70; + + std::cout << "["; + int pos = barWidth * current_ / total_; + for (int i = 0; i < barWidth; ++i) + { + if (i < pos) + std::cout << "="; + else if (i == pos) + std::cout << ">"; + else + std::cout << " "; + } + std::cout << "] " << std::setw(3) << current_ * 100 / total_ << "%"; + if (!message_.empty()) + std::cout << " (" << message_ << ")"; + std::cout << "\r"; + std::cout.flush(); +} + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/q.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/q.h new file mode 100644 index 0000000000..8972ae2b5a --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/q.h @@ -0,0 +1,63 @@ +#ifndef DIONYSUS_Q_H +#define DIONYSUS_Q_H + +#include + +// TODO: eventually need to be able to adaptively switch to arbitrary precision arithmetic + +namespace dionysus +{ + +template +class Q +{ + public: + using BaseElement = Element_; + struct Element + { + BaseElement numerator, denominator; + + bool operator==(Element o) const { return numerator == o.numerator && denominator == o.denominator; } + bool operator!=(Element o) const { return !((*this) == o); } + + friend + std::ostream& operator<<(std::ostream& out, Element e) { out << e.numerator << '/' << e.denominator; return out; } + }; + + Element id() const { return { 1,1 }; } + Element zero() const { return { 0,1 }; } + Element init(BaseElement a) const { return { a,1 }; } + + Element neg(Element a) const { return { -a.numerator, a.denominator }; } + Element add(Element a, Element b) const { Element x { a.numerator*b.denominator + b.numerator*a.denominator, a.denominator*b.denominator }; normalize(x); return x; } + + Element inv(Element a) const { return { a.denominator, a.numerator }; } + Element mul(Element a, Element b) const { Element x { a.numerator*b.numerator, a.denominator*b.denominator }; normalize(x); return x; } + Element div(Element a, Element b) const { return mul(a, inv(b)); } + + bool is_zero(Element a) const { return a.numerator == 0; } + + BaseElement numerator(const Element& x) const { return x.numerator; } + BaseElement denominator(const Element& x) const { return x.denominator; } + + static void normalize(Element& x) + { + BaseElement q = gcd(abs(x.numerator), abs(x.denominator)); + x.numerator /= q; + x.denominator /= q; + if (x.denominator < 0) + { + x.numerator = -x.numerator; + x.denominator = -x.denominator; + } + } + + static BaseElement abs(BaseElement x) { if (x < 0) return -x; return x; } + static BaseElement gcd(BaseElement a, BaseElement b) { if (b < a) return gcd(b,a); while (a != 0) { b %= a; std::swap(a,b); } return b; } + + static bool is_prime(BaseElement x) { return false; } // Ok, since is_prime is only used as a shortcut +}; + +} + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/z2.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/z2.h new file mode 100644 index 0000000000..6317ace6df --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/z2.h @@ -0,0 +1,31 @@ +#ifndef DIONYSUS_Z2_H +#define DIONYSUS_Z2_H + +namespace dionysus +{ + +class Z2Field +{ + public: + typedef short Element; + + Z2Field() {} + + static Element id() { return 1; } + static Element zero() { return 0; } + static Element init(int a) { return (a % 2 + 2) % 2; } + + Element neg(Element a) const { return 2 - a; } + Element add(Element a, Element b) const { return (a+b) % 2; } + + Element inv(Element a) const { return a; } + Element mul(Element a, Element b) const { return a*b; } + Element div(Element a, Element b) const { return a; } + + bool is_zero(Element a) const { return a == 0; } +}; + +} + +#endif + diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/zp.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/zp.h new file mode 100644 index 0000000000..c70c61cc87 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/zp.h @@ -0,0 +1,55 @@ +#ifndef DIONYSUS_ZP_H +#define DIONYSUS_ZP_H + +#include + +namespace dionysus +{ + +template +class ZpField +{ + public: + typedef Element_ Element; + + ZpField(Element p); + ZpField(const ZpField& other) = default; + ZpField(ZpField&& other) = default; + + Element id() const { return 1; } + Element zero() const { return 0; } + Element init(int a) const { return (a % p_ + p_) % p_; } + + Element neg(Element a) const { return p_ - a; } + Element add(Element a, Element b) const { return (a+b) % p_; } + + Element inv(Element a) const { while (a < 0) a += p_; return inverses_[a]; } + Element mul(Element a, Element b) const { return (a*b) % p_; } + Element div(Element a, Element b) const { return mul(a, inv(b)); } + + bool is_zero(Element a) const { return (a % p_) == 0; } + + Element prime() const { return p_; } + + private: + Element p_; + std::vector inverses_; +}; + +template +ZpField:: +ZpField(Element p): + p_(p), inverses_(p_) +{ + for (Element i = 1; i < p_; ++i) + for (Element j = 1; j < p_; ++j) + if (mul(i,j) == 1) + { + inverses_[i] = j; + break; + } +} + +} + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/filtration.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/filtration.h new file mode 100644 index 0000000000..caf871cdfb --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/filtration.h @@ -0,0 +1,124 @@ +#ifndef DIONYSUS_FILTRATION_H +#define DIONYSUS_FILTRATION_H + +#include +#include + +#include +#include +#include +#include + +namespace b = boost; +namespace bmi = boost::multi_index; + +namespace dionysus +{ + +// Filtration stores a filtered cell complex as boost::multi_index_container<...>. +// It allows for bidirectional translation between a cell and its index. +template>, + bool checked_index = false> +class Filtration +{ + public: + struct order {}; + + typedef Cell_ Cell; + typedef CellLookupIndex_ CellLookupIndex; + + typedef b::multi_index_container> + >> Container; + typedef typename Container::value_type value_type; + + typedef typename Container::template nth_index<0>::type Complex; + typedef typename Container::template nth_index<1>::type Order; + typedef typename Order::const_iterator OrderConstIterator; + typedef typename Order::iterator OrderIterator; + + + public: + Filtration() = default; + Filtration(Filtration&& other) = default; + Filtration& operator=(Filtration&& other) = default; + + Filtration(const std::initializer_list& cells): + Filtration(std::begin(cells), std::end(cells)) {} + + template + Filtration(Iterator bg, Iterator end): + cells_(bg, end) {} + + template + Filtration(const CellRange& cells): + Filtration(std::begin(cells), std::end(cells)) {} + + // Lookup + const Cell& operator[](size_t i) const { return cells_.template get()[i]; } + OrderConstIterator iterator(const Cell& s) const { return bmi::project(cells_, cells_.find(s)); } + size_t index(const Cell& s) const; + bool contains(const Cell& s) const { return cells_.find(s) != cells_.end(); } + + void push_back(const Cell& s) { cells_.template get().push_back(s); } + void push_back(Cell&& s) { cells_.template get().push_back(s); } + + void replace(size_t i, const Cell& s) { cells_.template get().replace(begin() + i, s); } + + // return index of the cell, adding it, if necessary + size_t add(const Cell& s) { size_t i = (iterator(s) - begin()); if (i == size()) emplace_back(s); return i; } + size_t add(Cell&& s) { size_t i = (iterator(s) - begin()); if (i == size()) emplace_back(std::move(s)); return i; } + + template + void emplace_back(Args&&... args) { cells_.template get().emplace_back(std::forward(args)...); } + + template> + void sort(const Cmp& cmp = Cmp()) { cells_.template get().sort(cmp); } + + void rearrange(const std::vector& indices); + + OrderConstIterator begin() const { return cells_.template get().begin(); } + OrderConstIterator end() const { return cells_.template get().end(); } + OrderIterator begin() { return cells_.template get().begin(); } + OrderIterator end() { return cells_.template get().end(); } + size_t size() const { return cells_.size(); } + void clear() { return Container().swap(cells_); } + + Cell& back() { return const_cast(cells_.template get().back()); } + const Cell& back() const { return cells_.template get().back(); } + + private: + Container cells_; +}; + +} + +template +size_t +dionysus::Filtration:: +index(const Cell& s) const +{ + auto it = iterator(s); + if (checked_index && it == end()) + { + std::ostringstream oss; + oss << "Trying to access non-existent cell: " << s; + throw std::runtime_error(oss.str()); + } + return it - begin(); +} + +template +void +dionysus::Filtration:: +rearrange(const std::vector& indices) +{ + std::vector> references; references.reserve(indices.size()); + for (size_t i : indices) + references.push_back(std::cref((*this)[i])); + cells_.template get().rearrange(references.begin()); +} + + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format.h new file mode 100644 index 0000000000..7f7ba83318 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format.h @@ -0,0 +1,8 @@ +#ifndef DIONYSUS_FORMAT_H +#define DIONYSUS_FORMAT_H + +#define FMT_HEADER_ONLY + +#include "format/format.h" + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.cc b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.cc new file mode 100644 index 0000000000..a01e272fd7 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.cc @@ -0,0 +1,1156 @@ +/* + Formatting library for C++ + + Copyright (c) 2012 - 2014, Victor Zverovich + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "format.h" + +#include + +#include +#include +#include +#include +#include + +#ifdef _WIN32 +# ifdef __MINGW32__ +# include +# endif +# include +#endif + +using fmt::internal::Arg; + +// Check if exceptions are disabled. +#if __GNUC__ && !__EXCEPTIONS +# define FMT_EXCEPTIONS 0 +#endif +#if _MSC_VER && !_HAS_EXCEPTIONS +# define FMT_EXCEPTIONS 0 +#endif +#ifndef FMT_EXCEPTIONS +# define FMT_EXCEPTIONS 1 +#endif + +#if FMT_EXCEPTIONS +# define FMT_TRY try +# define FMT_CATCH(x) catch (x) +#else +# define FMT_TRY if (true) +# define FMT_CATCH(x) if (false) +#endif + +#ifndef FMT_THROW +# if FMT_EXCEPTIONS +# define FMT_THROW(x) throw x +# define FMT_RETURN_AFTER_THROW(x) +# else +# define FMT_THROW(x) assert(false) +# define FMT_RETURN_AFTER_THROW(x) return x +# endif +#endif + +#ifdef FMT_HEADER_ONLY +# define FMT_FUNC inline +#else +# define FMT_FUNC +#endif + +#if _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4127) // conditional expression is constant +# pragma warning(disable: 4702) // unreachable code +#endif + +namespace { + +#ifndef _MSC_VER +# define FMT_SNPRINTF snprintf +#else // _MSC_VER +inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) { + va_list args; + va_start(args, format); + int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); + va_end(args); + return result; +} +# define FMT_SNPRINTF fmt_snprintf +#endif // _MSC_VER + +// Checks if a value fits in int - used to avoid warnings about comparing +// signed and unsigned integers. +template +struct IntChecker { + template + static bool fits_in_int(T value) { + unsigned max = INT_MAX; + return value <= max; + } +}; + +template <> +struct IntChecker { + template + static bool fits_in_int(T value) { + return value >= INT_MIN && value <= INT_MAX; + } +}; + +const char RESET_COLOR[] = "\x1b[0m"; + +typedef void (*FormatFunc)(fmt::Writer &, int, fmt::StringRef); + +// Portable thread-safe version of strerror. +// Sets buffer to point to a string describing the error code. +// This can be either a pointer to a string stored in buffer, +// or a pointer to some static immutable string. +// Returns one of the following values: +// 0 - success +// ERANGE - buffer is not large enough to store the error message +// other - failure +// Buffer should be at least of size 1. +int safe_strerror( + int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT { + assert(buffer != 0 && buffer_size != 0); + int result = 0; +#if ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !_GNU_SOURCE) || __ANDROID__ + // XSI-compliant version of strerror_r. + result = strerror_r(error_code, buffer, buffer_size); + if (result != 0) + result = errno; +#elif _GNU_SOURCE + // GNU-specific version of strerror_r. + char *message = strerror_r(error_code, buffer, buffer_size); + // If the buffer is full then the message is probably truncated. + if (message == buffer && strlen(buffer) == buffer_size - 1) + result = ERANGE; + buffer = message; +#elif __MINGW32__ + errno = 0; + (void)buffer_size; + buffer = strerror(error_code); + result = errno; +#elif _WIN32 + result = strerror_s(buffer, buffer_size, error_code); + // If the buffer is full then the message is probably truncated. + if (result == 0 && std::strlen(buffer) == buffer_size - 1) + result = ERANGE; +#else + result = strerror_r(error_code, buffer, buffer_size); + if (result == -1) + result = errno; // glibc versions before 2.13 return result in errno. +#endif + return result; +} + +void format_error_code(fmt::Writer &out, int error_code, + fmt::StringRef message) FMT_NOEXCEPT { + // Report error code making sure that the output fits into + // INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential + // bad_alloc. + out.clear(); + static const char SEP[] = ": "; + static const char ERR[] = "error "; + fmt::internal::IntTraits::MainType ec_value = error_code; + // Subtract 2 to account for terminating null characters in SEP and ERR. + std::size_t error_code_size = + sizeof(SEP) + sizeof(ERR) + fmt::internal::count_digits(ec_value) - 2; + if (message.size() <= fmt::internal::INLINE_BUFFER_SIZE - error_code_size) + out << message << SEP; + out << ERR << error_code; + assert(out.size() <= fmt::internal::INLINE_BUFFER_SIZE); +} + +void report_error(FormatFunc func, + int error_code, fmt::StringRef message) FMT_NOEXCEPT { + fmt::MemoryWriter full_message; + func(full_message, error_code, message); + // Use Writer::data instead of Writer::c_str to avoid potential memory + // allocation. + std::fwrite(full_message.data(), full_message.size(), 1, stderr); + std::fputc('\n', stderr); +} + +// IsZeroInt::visit(arg) returns true iff arg is a zero integer. +class IsZeroInt : public fmt::internal::ArgVisitor { + public: + template + bool visit_any_int(T value) { return value == 0; } +}; + +// Parses an unsigned integer advancing s to the end of the parsed input. +// This function assumes that the first character of s is a digit. +template +int parse_nonnegative_int(const Char *&s) { + assert('0' <= *s && *s <= '9'); + unsigned value = 0; + do { + unsigned new_value = value * 10 + (*s++ - '0'); + // Check if value wrapped around. + if (new_value < value) { + value = UINT_MAX; + break; + } + value = new_value; + } while ('0' <= *s && *s <= '9'); + if (value > INT_MAX) + FMT_THROW(fmt::FormatError("number is too big")); + return value; +} + +inline void require_numeric_argument(const Arg &arg, char spec) { + if (arg.type > Arg::LAST_NUMERIC_TYPE) { + std::string message = + fmt::format("format specifier '{}' requires numeric argument", spec); + FMT_THROW(fmt::FormatError(message)); + } +} + +template +void check_sign(const Char *&s, const Arg &arg) { + char sign = static_cast(*s); + require_numeric_argument(arg, sign); + if (arg.type == Arg::UINT || arg.type == Arg::ULONG_LONG) { + FMT_THROW(fmt::FormatError(fmt::format( + "format specifier '{}' requires signed argument", sign))); + } + ++s; +} + +// Checks if an argument is a valid printf width specifier and sets +// left alignment if it is negative. +class WidthHandler : public fmt::internal::ArgVisitor { + private: + fmt::FormatSpec &spec_; + + FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler); + + public: + explicit WidthHandler(fmt::FormatSpec &spec) : spec_(spec) {} + + unsigned visit_unhandled_arg() { + FMT_THROW(fmt::FormatError("width is not integer")); + FMT_RETURN_AFTER_THROW(0); + } + + template + unsigned visit_any_int(T value) { + typedef typename fmt::internal::IntTraits::MainType UnsignedType; + UnsignedType width = value; + if (fmt::internal::is_negative(value)) { + spec_.align_ = fmt::ALIGN_LEFT; + width = 0 - width; + } + if (width > INT_MAX) + FMT_THROW(fmt::FormatError("number is too big")); + return static_cast(width); + } +}; + +class PrecisionHandler : + public fmt::internal::ArgVisitor { + public: + unsigned visit_unhandled_arg() { + FMT_THROW(fmt::FormatError("precision is not integer")); + FMT_RETURN_AFTER_THROW(0); + } + + template + int visit_any_int(T value) { + if (!IntChecker::is_signed>::fits_in_int(value)) + FMT_THROW(fmt::FormatError("number is too big")); + return static_cast(value); + } +}; + +// Converts an integer argument to an integral type T for printf. +template +class ArgConverter : public fmt::internal::ArgVisitor, void> { + private: + fmt::internal::Arg &arg_; + wchar_t type_; + + FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter); + + public: + ArgConverter(fmt::internal::Arg &arg, wchar_t type) + : arg_(arg), type_(type) {} + + template + void visit_any_int(U value) { + bool is_signed = type_ == 'd' || type_ == 'i'; + using fmt::internal::Arg; + if (sizeof(T) <= sizeof(int)) { + // Extra casts are used to silence warnings. + if (is_signed) { + arg_.type = Arg::INT; + arg_.int_value = static_cast(static_cast(value)); + } else { + arg_.type = Arg::UINT; + arg_.uint_value = static_cast( + static_cast::Type>(value)); + } + } else { + if (is_signed) { + arg_.type = Arg::LONG_LONG; + arg_.long_long_value = + static_cast::Type>(value); + } else { + arg_.type = Arg::ULONG_LONG; + arg_.ulong_long_value = + static_cast::Type>(value); + } + } + } +}; + +// Converts an integer argument to char for printf. +class CharConverter : public fmt::internal::ArgVisitor { + private: + fmt::internal::Arg &arg_; + + FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter); + + public: + explicit CharConverter(fmt::internal::Arg &arg) : arg_(arg) {} + + template + void visit_any_int(T value) { + arg_.type = Arg::CHAR; + arg_.int_value = static_cast(value); + } +}; + +// This function template is used to prevent compile errors when handling +// incompatible string arguments, e.g. handling a wide string in a narrow +// string formatter. +template +Arg::StringValue ignore_incompatible_str(Arg::StringValue); + +template <> +inline Arg::StringValue ignore_incompatible_str( + Arg::StringValue) { return Arg::StringValue(); } + +template <> +inline Arg::StringValue ignore_incompatible_str( + Arg::StringValue s) { return s; } +} // namespace + +FMT_FUNC void fmt::SystemError::init( + int err_code, StringRef format_str, ArgList args) { + error_code_ = err_code; + MemoryWriter w; + internal::format_system_error(w, err_code, format(format_str, args)); + std::runtime_error &base = *this; + base = std::runtime_error(w.str()); +} + +template +int fmt::internal::CharTraits::format_float( + char *buffer, std::size_t size, const char *format, + unsigned width, int precision, T value) { + if (width == 0) { + return precision < 0 ? + FMT_SNPRINTF(buffer, size, format, value) : + FMT_SNPRINTF(buffer, size, format, precision, value); + } + return precision < 0 ? + FMT_SNPRINTF(buffer, size, format, width, value) : + FMT_SNPRINTF(buffer, size, format, width, precision, value); +} + +template +int fmt::internal::CharTraits::format_float( + wchar_t *buffer, std::size_t size, const wchar_t *format, + unsigned width, int precision, T value) { + if (width == 0) { + return precision < 0 ? + swprintf(buffer, size, format, value) : + swprintf(buffer, size, format, precision, value); + } + return precision < 0 ? + swprintf(buffer, size, format, width, value) : + swprintf(buffer, size, format, width, precision, value); +} + +template +const char fmt::internal::BasicData::DIGITS[] = + "0001020304050607080910111213141516171819" + "2021222324252627282930313233343536373839" + "4041424344454647484950515253545556575859" + "6061626364656667686970717273747576777879" + "8081828384858687888990919293949596979899"; + +#define FMT_POWERS_OF_10(factor) \ + factor * 10, \ + factor * 100, \ + factor * 1000, \ + factor * 10000, \ + factor * 100000, \ + factor * 1000000, \ + factor * 10000000, \ + factor * 100000000, \ + factor * 1000000000 + +template +const uint32_t fmt::internal::BasicData::POWERS_OF_10_32[] = { + 0, FMT_POWERS_OF_10(1) +}; + +template +const uint64_t fmt::internal::BasicData::POWERS_OF_10_64[] = { + 0, + FMT_POWERS_OF_10(1), + FMT_POWERS_OF_10(fmt::ULongLong(1000000000)), + // Multiply several constants instead of using a single long long constant + // to avoid warnings about C++98 not supporting long long. + fmt::ULongLong(1000000000) * fmt::ULongLong(1000000000) * 10 +}; + +FMT_FUNC void fmt::internal::report_unknown_type(char code, const char *type) { + if (std::isprint(static_cast(code))) { + FMT_THROW(fmt::FormatError( + fmt::format("unknown format code '{}' for {}", code, type))); + } + FMT_THROW(fmt::FormatError( + fmt::format("unknown format code '\\x{:02x}' for {}", + static_cast(code), type))); +} + +#ifdef _WIN32 + +FMT_FUNC fmt::internal::UTF8ToUTF16::UTF8ToUTF16(fmt::StringRef s) { + int length = MultiByteToWideChar( + CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, 0, 0); + static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16"; + if (length == 0) + FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); + buffer_.resize(length); + length = MultiByteToWideChar( + CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, &buffer_[0], length); + if (length == 0) + FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); +} + +FMT_FUNC fmt::internal::UTF16ToUTF8::UTF16ToUTF8(fmt::WStringRef s) { + if (int error_code = convert(s)) { + FMT_THROW(WindowsError(error_code, + "cannot convert string from UTF-16 to UTF-8")); + } +} + +FMT_FUNC int fmt::internal::UTF16ToUTF8::convert(fmt::WStringRef s) { + int length = WideCharToMultiByte(CP_UTF8, 0, s.c_str(), -1, 0, 0, 0, 0); + if (length == 0) + return GetLastError(); + buffer_.resize(length); + length = WideCharToMultiByte( + CP_UTF8, 0, s.c_str(), -1, &buffer_[0], length, 0, 0); + if (length == 0) + return GetLastError(); + return 0; +} + +FMT_FUNC void fmt::WindowsError::init( + int err_code, StringRef format_str, ArgList args) { + error_code_ = err_code; + MemoryWriter w; + internal::format_windows_error(w, err_code, format(format_str, args)); + std::runtime_error &base = *this; + base = std::runtime_error(w.str()); +} + +#endif + +FMT_FUNC void fmt::internal::format_system_error( + fmt::Writer &out, int error_code, + fmt::StringRef message) FMT_NOEXCEPT { + FMT_TRY { + MemoryBuffer buffer; + buffer.resize(INLINE_BUFFER_SIZE); + for (;;) { + char *system_message = &buffer[0]; + int result = safe_strerror(error_code, system_message, buffer.size()); + if (result == 0) { + out << message << ": " << system_message; + return; + } + if (result != ERANGE) + break; // Can't get error message, report error code instead. + buffer.resize(buffer.size() * 2); + } + } FMT_CATCH(...) {} + format_error_code(out, error_code, message); +} + +#ifdef _WIN32 +FMT_FUNC void fmt::internal::format_windows_error( + fmt::Writer &out, int error_code, + fmt::StringRef message) FMT_NOEXCEPT { + class String { + private: + LPWSTR str_; + + public: + String() : str_() {} + ~String() { LocalFree(str_); } + LPWSTR *ptr() { return &str_; } + LPCWSTR c_str() const { return str_; } + }; + FMT_TRY { + String system_message; + if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, + error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(system_message.ptr()), 0, 0)) { + UTF16ToUTF8 utf8_message; + if (utf8_message.convert(system_message.c_str()) == ERROR_SUCCESS) { + out << message << ": " << utf8_message; + return; + } + } + } FMT_CATCH(...) {} + format_error_code(out, error_code, message); +} +#endif + +// An argument formatter. +template +class fmt::internal::ArgFormatter : + public fmt::internal::ArgVisitor, void> { + private: + fmt::BasicFormatter &formatter_; + fmt::BasicWriter &writer_; + fmt::FormatSpec &spec_; + const Char *format_; + + FMT_DISALLOW_COPY_AND_ASSIGN(ArgFormatter); + + public: + ArgFormatter( + fmt::BasicFormatter &f,fmt::FormatSpec &s, const Char *fmt) + : formatter_(f), writer_(f.writer()), spec_(s), format_(fmt) {} + + template + void visit_any_int(T value) { writer_.write_int(value, spec_); } + + template + void visit_any_double(T value) { writer_.write_double(value, spec_); } + + void visit_char(int value) { + if (spec_.type_ && spec_.type_ != 'c') { + spec_.flags_ |= CHAR_FLAG; + writer_.write_int(value, spec_); + return; + } + if (spec_.align_ == ALIGN_NUMERIC || spec_.flags_ != 0) + FMT_THROW(FormatError("invalid format specifier for char")); + typedef typename fmt::BasicWriter::CharPtr CharPtr; + Char fill = static_cast(spec_.fill()); + if (spec_.precision_ == 0) { + std::fill_n(writer_.grow_buffer(spec_.width_), spec_.width_, fill); + return; + } + CharPtr out = CharPtr(); + if (spec_.width_ > 1) { + out = writer_.grow_buffer(spec_.width_); + if (spec_.align_ == fmt::ALIGN_RIGHT) { + std::fill_n(out, spec_.width_ - 1, fill); + out += spec_.width_ - 1; + } else if (spec_.align_ == fmt::ALIGN_CENTER) { + out = writer_.fill_padding(out, spec_.width_, 1, fill); + } else { + std::fill_n(out + 1, spec_.width_ - 1, fill); + } + } else { + out = writer_.grow_buffer(1); + } + *out = static_cast(value); + } + + void visit_string(Arg::StringValue value) { + writer_.write_str(value, spec_); + } + void visit_wstring(Arg::StringValue value) { + writer_.write_str(ignore_incompatible_str(value), spec_); + } + + void visit_pointer(const void *value) { + if (spec_.type_ && spec_.type_ != 'p') + fmt::internal::report_unknown_type(spec_.type_, "pointer"); + spec_.flags_ = fmt::HASH_FLAG; + spec_.type_ = 'x'; + writer_.write_int(reinterpret_cast(value), spec_); + } + + void visit_custom(Arg::CustomValue c) { + c.format(&formatter_, c.value, &format_); + } +}; + +template +template +void fmt::BasicWriter::write_str( + const Arg::StringValue &s, const FormatSpec &spec) { + // Check if StrChar is convertible to Char. + internal::CharTraits::convert(StrChar()); + if (spec.type_ && spec.type_ != 's') + internal::report_unknown_type(spec.type_, "string"); + const StrChar *str_value = s.value; + std::size_t str_size = s.size; + if (str_size == 0) { + if (!str_value) + FMT_THROW(FormatError("string pointer is null")); + if (*str_value) + str_size = std::char_traits::length(str_value); + } + std::size_t precision = spec.precision_; + if (spec.precision_ >= 0 && precision < str_size) + str_size = spec.precision_; + write_str(str_value, str_size, spec); +} + +template +inline Arg fmt::BasicFormatter::parse_arg_index(const Char *&s) { + const char *error = 0; + Arg arg = *s < '0' || *s > '9' ? + next_arg(error) : get_arg(parse_nonnegative_int(s), error); + if (error) { + FMT_THROW(FormatError( + *s != '}' && *s != ':' ? "invalid format string" : error)); + } + return arg; +} + +FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg( + unsigned arg_index, const char *&error) { + Arg arg = args_[arg_index]; + if (arg.type == Arg::NONE) + error = "argument index out of range"; + return arg; +} + +inline Arg fmt::internal::FormatterBase::next_arg(const char *&error) { + if (next_arg_index_ >= 0) + return do_get_arg(next_arg_index_++, error); + error = "cannot switch from manual to automatic argument indexing"; + return Arg(); +} + +inline Arg fmt::internal::FormatterBase::get_arg( + unsigned arg_index, const char *&error) { + if (next_arg_index_ <= 0) { + next_arg_index_ = -1; + return do_get_arg(arg_index, error); + } + error = "cannot switch from automatic to manual argument indexing"; + return Arg(); +} + +template +void fmt::internal::PrintfFormatter::parse_flags( + FormatSpec &spec, const Char *&s) { + for (;;) { + switch (*s++) { + case '-': + spec.align_ = ALIGN_LEFT; + break; + case '+': + spec.flags_ |= SIGN_FLAG | PLUS_FLAG; + break; + case '0': + spec.fill_ = '0'; + break; + case ' ': + spec.flags_ |= SIGN_FLAG; + break; + case '#': + spec.flags_ |= HASH_FLAG; + break; + default: + --s; + return; + } + } +} + +template +Arg fmt::internal::PrintfFormatter::get_arg( + const Char *s, unsigned arg_index) { + const char *error = 0; + Arg arg = arg_index == UINT_MAX ? + next_arg(error) : FormatterBase::get_arg(arg_index - 1, error); + if (error) + FMT_THROW(FormatError(!*s ? "invalid format string" : error)); + return arg; +} + +template +unsigned fmt::internal::PrintfFormatter::parse_header( + const Char *&s, FormatSpec &spec) { + unsigned arg_index = UINT_MAX; + Char c = *s; + if (c >= '0' && c <= '9') { + // Parse an argument index (if followed by '$') or a width possibly + // preceded with '0' flag(s). + unsigned value = parse_nonnegative_int(s); + if (*s == '$') { // value is an argument index + ++s; + arg_index = value; + } else { + if (c == '0') + spec.fill_ = '0'; + if (value != 0) { + // Nonzero value means that we parsed width and don't need to + // parse it or flags again, so return now. + spec.width_ = value; + return arg_index; + } + } + } + parse_flags(spec, s); + // Parse width. + if (*s >= '0' && *s <= '9') { + spec.width_ = parse_nonnegative_int(s); + } else if (*s == '*') { + ++s; + spec.width_ = WidthHandler(spec).visit(get_arg(s)); + } + return arg_index; +} + +template +void fmt::internal::PrintfFormatter::format( + BasicWriter &writer, BasicStringRef format_str, + const ArgList &args) { + const Char *start = format_str.c_str(); + set_args(args); + const Char *s = start; + while (*s) { + Char c = *s++; + if (c != '%') continue; + if (*s == c) { + write(writer, start, s); + start = ++s; + continue; + } + write(writer, start, s - 1); + + FormatSpec spec; + spec.align_ = ALIGN_RIGHT; + + // Parse argument index, flags and width. + unsigned arg_index = parse_header(s, spec); + + // Parse precision. + if (*s == '.') { + ++s; + if ('0' <= *s && *s <= '9') { + spec.precision_ = parse_nonnegative_int(s); + } else if (*s == '*') { + ++s; + spec.precision_ = PrecisionHandler().visit(get_arg(s)); + } + } + + Arg arg = get_arg(s, arg_index); + if (spec.flag(HASH_FLAG) && IsZeroInt().visit(arg)) + spec.flags_ &= ~HASH_FLAG; + if (spec.fill_ == '0') { + if (arg.type <= Arg::LAST_NUMERIC_TYPE) + spec.align_ = ALIGN_NUMERIC; + else + spec.fill_ = ' '; // Ignore '0' flag for non-numeric types. + } + + // Parse length and convert the argument to the required type. + switch (*s++) { + case 'h': + if (*s == 'h') + ArgConverter(arg, *++s).visit(arg); + else + ArgConverter(arg, *s).visit(arg); + break; + case 'l': + if (*s == 'l') + ArgConverter(arg, *++s).visit(arg); + else + ArgConverter(arg, *s).visit(arg); + break; + case 'j': + ArgConverter(arg, *s).visit(arg); + break; + case 'z': + ArgConverter(arg, *s).visit(arg); + break; + case 't': + ArgConverter(arg, *s).visit(arg); + break; + case 'L': + // printf produces garbage when 'L' is omitted for long double, no + // need to do the same. + break; + default: + --s; + ArgConverter(arg, *s).visit(arg); + } + + // Parse type. + if (!*s) + FMT_THROW(FormatError("invalid format string")); + spec.type_ = static_cast(*s++); + if (arg.type <= Arg::LAST_INTEGER_TYPE) { + // Normalize type. + switch (spec.type_) { + case 'i': case 'u': + spec.type_ = 'd'; + break; + case 'c': + // TODO: handle wchar_t + CharConverter(arg).visit(arg); + break; + } + } + + start = s; + + // Format argument. + switch (arg.type) { + case Arg::INT: + writer.write_int(arg.int_value, spec); + break; + case Arg::UINT: + writer.write_int(arg.uint_value, spec); + break; + case Arg::LONG_LONG: + writer.write_int(arg.long_long_value, spec); + break; + case Arg::ULONG_LONG: + writer.write_int(arg.ulong_long_value, spec); + break; + case Arg::CHAR: { + if (spec.type_ && spec.type_ != 'c') + writer.write_int(arg.int_value, spec); + typedef typename BasicWriter::CharPtr CharPtr; + CharPtr out = CharPtr(); + if (spec.width_ > 1) { + Char fill = ' '; + out = writer.grow_buffer(spec.width_); + if (spec.align_ != ALIGN_LEFT) { + std::fill_n(out, spec.width_ - 1, fill); + out += spec.width_ - 1; + } else { + std::fill_n(out + 1, spec.width_ - 1, fill); + } + } else { + out = writer.grow_buffer(1); + } + *out = static_cast(arg.int_value); + break; + } + case Arg::DOUBLE: + writer.write_double(arg.double_value, spec); + break; + case Arg::LONG_DOUBLE: + writer.write_double(arg.long_double_value, spec); + break; + case Arg::CSTRING: + arg.string.size = 0; + writer.write_str(arg.string, spec); + break; + case Arg::STRING: + writer.write_str(arg.string, spec); + break; + case Arg::WSTRING: + writer.write_str(ignore_incompatible_str(arg.wstring), spec); + break; + case Arg::POINTER: + if (spec.type_ && spec.type_ != 'p') + internal::report_unknown_type(spec.type_, "pointer"); + spec.flags_= HASH_FLAG; + spec.type_ = 'x'; + writer.write_int(reinterpret_cast(arg.pointer), spec); + break; + case Arg::CUSTOM: { + if (spec.type_) + internal::report_unknown_type(spec.type_, "object"); + const void *str_format = "s"; + arg.custom.format(&writer, arg.custom.value, &str_format); + break; + } + default: + assert(false); + break; + } + } + write(writer, start, s); +} + +template +const Char *fmt::BasicFormatter::format( + const Char *&format_str, const Arg &arg) { + const Char *s = format_str; + FormatSpec spec; + if (*s == ':') { + if (arg.type == Arg::CUSTOM) { + arg.custom.format(this, arg.custom.value, &s); + return s; + } + ++s; + // Parse fill and alignment. + if (Char c = *s) { + const Char *p = s + 1; + spec.align_ = ALIGN_DEFAULT; + do { + switch (*p) { + case '<': + spec.align_ = ALIGN_LEFT; + break; + case '>': + spec.align_ = ALIGN_RIGHT; + break; + case '=': + spec.align_ = ALIGN_NUMERIC; + break; + case '^': + spec.align_ = ALIGN_CENTER; + break; + } + if (spec.align_ != ALIGN_DEFAULT) { + if (p != s) { + if (c == '}') break; + if (c == '{') + FMT_THROW(FormatError("invalid fill character '{'")); + s += 2; + spec.fill_ = c; + } else ++s; + if (spec.align_ == ALIGN_NUMERIC) + require_numeric_argument(arg, '='); + break; + } + } while (--p >= s); + } + + // Parse sign. + switch (*s) { + case '+': + check_sign(s, arg); + spec.flags_ |= SIGN_FLAG | PLUS_FLAG; + break; + case '-': + check_sign(s, arg); + spec.flags_ |= MINUS_FLAG; + break; + case ' ': + check_sign(s, arg); + spec.flags_ |= SIGN_FLAG; + break; + } + + if (*s == '#') { + require_numeric_argument(arg, '#'); + spec.flags_ |= HASH_FLAG; + ++s; + } + + // Parse width and zero flag. + if ('0' <= *s && *s <= '9') { + if (*s == '0') { + require_numeric_argument(arg, '0'); + spec.align_ = ALIGN_NUMERIC; + spec.fill_ = '0'; + } + // Zero may be parsed again as a part of the width, but it is simpler + // and more efficient than checking if the next char is a digit. + spec.width_ = parse_nonnegative_int(s); + } + + // Parse precision. + if (*s == '.') { + ++s; + spec.precision_ = 0; + if ('0' <= *s && *s <= '9') { + spec.precision_ = parse_nonnegative_int(s); + } else if (*s == '{') { + ++s; + const Arg &precision_arg = parse_arg_index(s); + if (*s++ != '}') + FMT_THROW(FormatError("invalid format string")); + ULongLong value = 0; + switch (precision_arg.type) { + case Arg::INT: + if (precision_arg.int_value < 0) + FMT_THROW(FormatError("negative precision")); + value = precision_arg.int_value; + break; + case Arg::UINT: + value = precision_arg.uint_value; + break; + case Arg::LONG_LONG: + if (precision_arg.long_long_value < 0) + FMT_THROW(FormatError("negative precision")); + value = precision_arg.long_long_value; + break; + case Arg::ULONG_LONG: + value = precision_arg.ulong_long_value; + break; + default: + FMT_THROW(FormatError("precision is not integer")); + } + if (value > INT_MAX) + FMT_THROW(FormatError("number is too big")); + spec.precision_ = static_cast(value); + } else { + FMT_THROW(FormatError("missing precision specifier")); + } + if (arg.type < Arg::LAST_INTEGER_TYPE || arg.type == Arg::POINTER) { + FMT_THROW(FormatError( + fmt::format("precision not allowed in {} format specifier", + arg.type == Arg::POINTER ? "pointer" : "integer"))); + } + } + + // Parse type. + if (*s != '}' && *s) + spec.type_ = static_cast(*s++); + } + + if (*s++ != '}') + FMT_THROW(FormatError("missing '}' in format string")); + start_ = s; + + // Format argument. + internal::ArgFormatter(*this, spec, s - 1).visit(arg); + return s; +} + +template +void fmt::BasicFormatter::format( + BasicStringRef format_str, const ArgList &args) { + const Char *s = start_ = format_str.c_str(); + set_args(args); + while (*s) { + Char c = *s++; + if (c != '{' && c != '}') continue; + if (*s == c) { + write(writer_, start_, s); + start_ = ++s; + continue; + } + if (c == '}') + FMT_THROW(FormatError("unmatched '}' in format string")); + write(writer_, start_, s - 1); + Arg arg = parse_arg_index(s); + s = format(s, arg); + } + write(writer_, start_, s); +} + +FMT_FUNC void fmt::report_system_error( + int error_code, fmt::StringRef message) FMT_NOEXCEPT { + report_error(internal::format_system_error, error_code, message); +} + +#ifdef _WIN32 +FMT_FUNC void fmt::report_windows_error( + int error_code, fmt::StringRef message) FMT_NOEXCEPT { + report_error(internal::format_windows_error, error_code, message); +} +#endif + +FMT_FUNC void fmt::print(std::FILE *f, StringRef format_str, ArgList args) { + MemoryWriter w; + w.write(format_str, args); + std::fwrite(w.data(), 1, w.size(), f); +} + +FMT_FUNC void fmt::print(StringRef format_str, ArgList args) { + print(stdout, format_str, args); +} + +FMT_FUNC void fmt::print(std::ostream &os, StringRef format_str, ArgList args) { + MemoryWriter w; + w.write(format_str, args); + os.write(w.data(), w.size()); +} + +FMT_FUNC void fmt::print_colored(Color c, StringRef format, ArgList args) { + char escape[] = "\x1b[30m"; + escape[3] = '0' + static_cast(c); + std::fputs(escape, stdout); + print(format, args); + std::fputs(RESET_COLOR, stdout); +} + +FMT_FUNC int fmt::fprintf(std::FILE *f, StringRef format, ArgList args) { + MemoryWriter w; + printf(w, format, args); + std::size_t size = w.size(); + return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast(size); +} + +// Explicit instantiations for char. + +template const char *fmt::BasicFormatter::format( + const char *&format_str, const fmt::internal::Arg &arg); + +template void fmt::BasicFormatter::format( + BasicStringRef format, const ArgList &args); + +template void fmt::internal::PrintfFormatter::format( + BasicWriter &writer, BasicStringRef format, const ArgList &args); + +template int fmt::internal::CharTraits::format_float( + char *buffer, std::size_t size, const char *format, + unsigned width, int precision, double value); + +template int fmt::internal::CharTraits::format_float( + char *buffer, std::size_t size, const char *format, + unsigned width, int precision, long double value); + +// Explicit instantiations for wchar_t. + +template const wchar_t *fmt::BasicFormatter::format( + const wchar_t *&format_str, const fmt::internal::Arg &arg); + +template void fmt::BasicFormatter::format( + BasicStringRef format, const ArgList &args); + +template void fmt::internal::PrintfFormatter::format( + BasicWriter &writer, BasicStringRef format, + const ArgList &args); + +template int fmt::internal::CharTraits::format_float( + wchar_t *buffer, std::size_t size, const wchar_t *format, + unsigned width, int precision, double value); + +template int fmt::internal::CharTraits::format_float( + wchar_t *buffer, std::size_t size, const wchar_t *format, + unsigned width, int precision, long double value); + +#if _MSC_VER +# pragma warning(pop) +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.h new file mode 100644 index 0000000000..03ed685383 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.h @@ -0,0 +1,2546 @@ +/* + Formatting library for C++ + + Copyright (c) 2012 - 2014, Victor Zverovich + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FMT_FORMAT_H_ +#define FMT_FORMAT_H_ + +#include + +#include +#include +#include // for std::ptrdiff_t +#include +#include +#include +#include +#include +#include + +#if _SECURE_SCL +# include +#endif + +#ifdef _MSC_VER +# include // _BitScanReverse, _BitScanReverse64 + +namespace fmt { +namespace internal { +# pragma intrinsic(_BitScanReverse) +inline uint32_t clz(uint32_t x) { + unsigned long r = 0; + _BitScanReverse(&r, x); + return 31 - r; +} +# define FMT_BUILTIN_CLZ(n) fmt::internal::clz(n) +inline uint32_t clzll(uint64_t x) { + unsigned long r = 0; +# ifdef _WIN64 +# pragma intrinsic(_BitScanReverse64) + _BitScanReverse64(&r, x); +# else + // Scan the high 32 bits. + if (_BitScanReverse(&r, static_cast(x >> 32))) + return 63 - (r + 32); + + // Scan the low 32 bits. + _BitScanReverse(&r, static_cast(x)); +# endif + return 63 - r; +} +# define FMT_BUILTIN_CLZLL(n) fmt::internal::clzll(n) +} +} +#endif + +#ifdef __GNUC__ +# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +# define FMT_GCC_EXTENSION __extension__ +# if FMT_GCC_VERSION >= 406 +# pragma GCC diagnostic push +// Disable the warning about "long long" which is sometimes reported even +// when using __extension__. +# pragma GCC diagnostic ignored "-Wlong-long" +// Disable the warning about declaration shadowing because it affects too +// many valid cases. +# pragma GCC diagnostic ignored "-Wshadow" +# endif +# if __cplusplus >= 201103L || defined __GXX_EXPERIMENTAL_CXX0X__ +# define FMT_HAS_GXX_CXX11 1 +# endif +#else +# define FMT_GCC_EXTENSION +#endif + +#ifdef __clang__ +# pragma clang diagnostic ignored "-Wdocumentation-unknown-command" +#endif + +#ifdef __GNUC_LIBSTD__ +# define FMT_GNUC_LIBSTD_VERSION (__GNUC_LIBSTD__ * 100 + __GNUC_LIBSTD_MINOR__) +#endif + +#ifdef __has_feature +# define FMT_HAS_FEATURE(x) __has_feature(x) +#else +# define FMT_HAS_FEATURE(x) 0 +#endif + +#ifdef __has_builtin +# define FMT_HAS_BUILTIN(x) __has_builtin(x) +#else +# define FMT_HAS_BUILTIN(x) 0 +#endif + +#ifdef __has_cpp_attribute +# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +# define FMT_HAS_CPP_ATTRIBUTE(x) 0 +#endif + +#ifndef FMT_USE_VARIADIC_TEMPLATES +// Variadic templates are available in GCC since version 4.4 +// (http://gcc.gnu.org/projects/cxx0x.html) and in Visual C++ +// since version 2013. +# define FMT_USE_VARIADIC_TEMPLATES \ + (FMT_HAS_FEATURE(cxx_variadic_templates) || \ + (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1800) +#endif + +#ifndef FMT_USE_RVALUE_REFERENCES +// Don't use rvalue references when compiling with clang and an old libstdc++ +// as the latter doesn't provide std::move. +# if defined(FMT_GNUC_LIBSTD_VERSION) && FMT_GNUC_LIBSTD_VERSION <= 402 +# define FMT_USE_RVALUE_REFERENCES 0 +# else +# define FMT_USE_RVALUE_REFERENCES \ + (FMT_HAS_FEATURE(cxx_rvalue_references) || \ + (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1600) +# endif +#endif + +#if FMT_USE_RVALUE_REFERENCES +# include // for std::move +#endif + +// Define FMT_USE_NOEXCEPT to make C++ Format use noexcept (C++11 feature). +#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ + (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) +# define FMT_NOEXCEPT noexcept +#else +# define FMT_NOEXCEPT throw() +#endif + +// A macro to disallow the copy constructor and operator= functions +// This should be used in the private: declarations for a class +#if FMT_USE_DELETED_FUNCTIONS || FMT_HAS_FEATURE(cxx_deleted_functions) || \ + (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1800 +# define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&) = delete; \ + TypeName& operator=(const TypeName&) = delete +#else +# define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + TypeName& operator=(const TypeName&) +#endif + +namespace fmt { + +// Fix the warning about long long on older versions of GCC +// that don't support the diagnostic pragma. +FMT_GCC_EXTENSION typedef long long LongLong; +FMT_GCC_EXTENSION typedef unsigned long long ULongLong; + +#if FMT_USE_RVALUE_REFERENCES +using std::move; +#endif + +template +class BasicWriter; + +typedef BasicWriter Writer; +typedef BasicWriter WWriter; + +template +class BasicFormatter; + +template +void format(BasicFormatter &f, const Char *&format_str, const T &value); + +/** + \rst + A string reference. It can be constructed from a C string or + ``std::string``. + + You can use one of the following typedefs for common character types: + + +------------+-------------------------+ + | Type | Definition | + +============+=========================+ + | StringRef | BasicStringRef | + +------------+-------------------------+ + | WStringRef | BasicStringRef | + +------------+-------------------------+ + + This class is most useful as a parameter type to allow passing + different types of strings to a function, for example:: + + template + std::string format(StringRef format_str, const Args & ... args); + + format("{}", 42); + format(std::string("{}"), 42); + \endrst + */ +template +class BasicStringRef { + private: + const Char *data_; + std::size_t size_; + + public: + /** + Constructs a string reference object from a C string and a size. + */ + BasicStringRef(const Char *s, std::size_t size) : data_(s), size_(size) {} + + /** + Constructs a string reference object from a C string computing + the size with ``std::char_traits::length``. + */ + BasicStringRef(const Char *s) + : data_(s), size_(std::char_traits::length(s)) {} + + /** + Constructs a string reference from an `std::string` object. + */ + BasicStringRef(const std::basic_string &s) + : data_(s.c_str()), size_(s.size()) {} + + /** + Converts a string reference to an `std::string` object. + */ + operator std::basic_string() const { + return std::basic_string(data_, size()); + } + + /** + Returns the pointer to a C string. + */ + const Char *c_str() const { return data_; } + + /** + Returns the string size. + */ + std::size_t size() const { return size_; } + + friend bool operator==(BasicStringRef lhs, BasicStringRef rhs) { + return lhs.data_ == rhs.data_; + } + friend bool operator!=(BasicStringRef lhs, BasicStringRef rhs) { + return lhs.data_ != rhs.data_; + } +}; + +typedef BasicStringRef StringRef; +typedef BasicStringRef WStringRef; + +/** + A formatting error such as invalid format string. +*/ +class FormatError : public std::runtime_error { +public: + explicit FormatError(StringRef message) + : std::runtime_error(message.c_str()) {} +}; + +namespace internal { + +// The number of characters to store in the MemoryBuffer object itself +// to avoid dynamic memory allocation. +enum { INLINE_BUFFER_SIZE = 500 }; + +#if _SECURE_SCL +// Use checked iterator to avoid warnings on MSVC. +template +inline stdext::checked_array_iterator make_ptr(T *ptr, std::size_t size) { + return stdext::checked_array_iterator(ptr, size); +} +#else +template +inline T *make_ptr(T *ptr, std::size_t) { return ptr; } +#endif + +// A buffer for POD types. It supports a subset of std::vector's operations. +template +class Buffer { + private: + FMT_DISALLOW_COPY_AND_ASSIGN(Buffer); + + protected: + T *ptr_; + std::size_t size_; + std::size_t capacity_; + + Buffer(T *ptr = 0, std::size_t capacity = 0) + : ptr_(ptr), size_(0), capacity_(capacity) {} + + virtual void grow(std::size_t size) = 0; + + public: + virtual ~Buffer() {} + + // Returns the size of this buffer. + std::size_t size() const { return size_; } + + // Returns the capacity of this buffer. + std::size_t capacity() const { return capacity_; } + + // Resizes the buffer. If T is a POD type new elements are not initialized. + void resize(std::size_t new_size) { + if (new_size > capacity_) + grow(new_size); + size_ = new_size; + } + + // Reserves space to store at least capacity elements. + void reserve(std::size_t capacity) { + if (capacity > capacity_) + grow(capacity); + } + + void clear() FMT_NOEXCEPT { size_ = 0; } + + void push_back(const T &value) { + if (size_ == capacity_) + grow(size_ + 1); + ptr_[size_++] = value; + } + + // Appends data to the end of the buffer. + void append(const T *begin, const T *end); + + T &operator[](std::size_t index) { return ptr_[index]; } + const T &operator[](std::size_t index) const { return ptr_[index]; } +}; + +template +void Buffer::append(const T *begin, const T *end) { + std::ptrdiff_t num_elements = end - begin; + if (size_ + num_elements > capacity_) + grow(size_ + num_elements); + std::copy(begin, end, make_ptr(ptr_, capacity_) + size_); + size_ += num_elements; +} + +// A memory buffer for POD types with the first SIZE elements stored in +// the object itself. +template > +class MemoryBuffer : private Allocator, public Buffer { + private: + T data_[SIZE]; + + // Free memory allocated by the buffer. + void free() { + if (this->ptr_ != data_) this->deallocate(this->ptr_, this->capacity_); + } + + protected: + void grow(std::size_t size); + + public: + explicit MemoryBuffer(const Allocator &alloc = Allocator()) + : Allocator(alloc), Buffer(data_, SIZE) {} + ~MemoryBuffer() { free(); } + +#if FMT_USE_RVALUE_REFERENCES + private: + // Move data from other to this buffer. + void move(MemoryBuffer &other) { + Allocator &this_alloc = *this, &other_alloc = other; + this_alloc = std::move(other_alloc); + this->size_ = other.size_; + this->capacity_ = other.capacity_; + if (other.ptr_ == other.data_) { + this->ptr_ = data_; + std::copy(other.data_, + other.data_ + this->size_, make_ptr(data_, this->capacity_)); + } else { + this->ptr_ = other.ptr_; + // Set pointer to the inline array so that delete is not called + // when freeing. + other.ptr_ = other.data_; + } + } + + public: + MemoryBuffer(MemoryBuffer &&other) { + move(other); + } + + MemoryBuffer &operator=(MemoryBuffer &&other) { + assert(this != &other); + free(); + move(other); + return *this; + } +#endif + + // Returns a copy of the allocator associated with this buffer. + Allocator get_allocator() const { return *this; } +}; + +template +void MemoryBuffer::grow(std::size_t size) { + std::size_t new_capacity = + (std::max)(size, this->capacity_ + this->capacity_ / 2); + T *new_ptr = this->allocate(new_capacity); + // The following code doesn't throw, so the raw pointer above doesn't leak. + std::copy(this->ptr_, + this->ptr_ + this->size_, make_ptr(new_ptr, new_capacity)); + std::size_t old_capacity = this->capacity_; + T *old_ptr = this->ptr_; + this->capacity_ = new_capacity; + this->ptr_ = new_ptr; + // deallocate may throw (at least in principle), but it doesn't matter since + // the buffer already uses the new storage and will deallocate it in case + // of exception. + if (old_ptr != data_) + this->deallocate(old_ptr, old_capacity); +} + +#ifndef _MSC_VER +// Portable version of signbit. +inline int getsign(double x) { + // When compiled in C++11 mode signbit is no longer a macro but a function + // defined in namespace std and the macro is undefined. +# ifdef signbit + return signbit(x); +# else + return std::signbit(x); +# endif +} + +// Portable version of isinf. +# ifdef isinf +inline int isinfinity(double x) { return isinf(x); } +inline int isinfinity(long double x) { return isinf(x); } +# else +inline int isinfinity(double x) { return std::isinf(x); } +inline int isinfinity(long double x) { return std::isinf(x); } +# endif +#else +inline int getsign(double value) { + if (value < 0) return 1; + if (value == value) return 0; + int dec = 0, sign = 0; + char buffer[2]; // The buffer size must be >= 2 or _ecvt_s will fail. + _ecvt_s(buffer, sizeof(buffer), value, 0, &dec, &sign); + return sign; +} +inline int isinfinity(double x) { return !_finite(x); } +inline int isinfinity(long double x) { return !_finite(static_cast(x)); } +#endif + +template +class BasicCharTraits { + public: +#if _SECURE_SCL + typedef stdext::checked_array_iterator CharPtr; +#else + typedef Char *CharPtr; +#endif +}; + +template +class CharTraits; + +template <> +class CharTraits : public BasicCharTraits { + private: + // Conversion from wchar_t to char is not allowed. + static char convert(wchar_t); + +public: + typedef const wchar_t *UnsupportedStrType; + + static char convert(char value) { return value; } + + // Formats a floating-point number. + template + static int format_float(char *buffer, std::size_t size, + const char *format, unsigned width, int precision, T value); +}; + +template <> +class CharTraits : public BasicCharTraits { + public: + typedef const char *UnsupportedStrType; + + static wchar_t convert(char value) { return value; } + static wchar_t convert(wchar_t value) { return value; } + + template + static int format_float(wchar_t *buffer, std::size_t size, + const wchar_t *format, unsigned width, int precision, T value); +}; + +// Checks if a number is negative - used to avoid warnings. +template +struct SignChecker { + template + static bool is_negative(T value) { return value < 0; } +}; + +template <> +struct SignChecker { + template + static bool is_negative(T) { return false; } +}; + +// Returns true if value is negative, false otherwise. +// Same as (value < 0) but doesn't produce warnings if T is an unsigned type. +template +inline bool is_negative(T value) { + return SignChecker::is_signed>::is_negative(value); +} + +// Selects uint32_t if FitsIn32Bits is true, uint64_t otherwise. +template +struct TypeSelector { typedef uint32_t Type; }; + +template <> +struct TypeSelector { typedef uint64_t Type; }; + +template +struct IntTraits { + // Smallest of uint32_t and uint64_t that is large enough to represent + // all values of T. + typedef typename + TypeSelector::digits <= 32>::Type MainType; +}; + +// MakeUnsigned::Type gives an unsigned type corresponding to integer type T. +template +struct MakeUnsigned { typedef T Type; }; + +#define FMT_SPECIALIZE_MAKE_UNSIGNED(T, U) \ + template <> \ + struct MakeUnsigned { typedef U Type; } + +FMT_SPECIALIZE_MAKE_UNSIGNED(char, unsigned char); +FMT_SPECIALIZE_MAKE_UNSIGNED(signed char, unsigned char); +FMT_SPECIALIZE_MAKE_UNSIGNED(short, unsigned short); +FMT_SPECIALIZE_MAKE_UNSIGNED(int, unsigned); +FMT_SPECIALIZE_MAKE_UNSIGNED(long, unsigned long); +FMT_SPECIALIZE_MAKE_UNSIGNED(LongLong, ULongLong); + +void report_unknown_type(char code, const char *type); + +// Static data is placed in this class template to allow header-only +// configuration. +template +struct BasicData { + static const uint32_t POWERS_OF_10_32[]; + static const uint64_t POWERS_OF_10_64[]; + static const char DIGITS[]; +}; + +typedef BasicData<> Data; + +#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clz) +# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) +#endif + +#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clzll) +# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) +#endif + +#ifdef FMT_BUILTIN_CLZLL +// Returns the number of decimal digits in n. Leading zeros are not counted +// except for n == 0 in which case count_digits returns 1. +inline unsigned count_digits(uint64_t n) { + // Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 + // and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits. + unsigned t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12; + return t - (n < Data::POWERS_OF_10_64[t]) + 1; +} +#else +// Fallback version of count_digits used when __builtin_clz is not available. +inline unsigned count_digits(uint64_t n) { + unsigned count = 1; + for (;;) { + // Integer division is slow so do it for a group of four digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + if (n < 10) return count; + if (n < 100) return count + 1; + if (n < 1000) return count + 2; + if (n < 10000) return count + 3; + n /= 10000u; + count += 4; + } +} +#endif + +#ifdef FMT_BUILTIN_CLZ +// Optional version of count_digits for better performance on 32-bit platforms. +inline unsigned count_digits(uint32_t n) { + uint32_t t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12; + return t - (n < Data::POWERS_OF_10_32[t]) + 1; +} +#endif + +// Formats a decimal unsigned integer value writing into buffer. +template +inline void format_decimal(Char *buffer, UInt value, unsigned num_digits) { + --num_digits; + while (value >= 100) { + // Integer division is slow so do it for a group of two digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + unsigned index = (value % 100) * 2; + value /= 100; + buffer[num_digits] = Data::DIGITS[index + 1]; + buffer[num_digits - 1] = Data::DIGITS[index]; + num_digits -= 2; + } + if (value < 10) { + *buffer = static_cast('0' + value); + return; + } + unsigned index = static_cast(value * 2); + buffer[1] = Data::DIGITS[index + 1]; + buffer[0] = Data::DIGITS[index]; +} + +#ifdef _WIN32 +// A converter from UTF-8 to UTF-16. +// It is only provided for Windows since other systems support UTF-8 natively. +class UTF8ToUTF16 { + private: + MemoryBuffer buffer_; + + public: + explicit UTF8ToUTF16(StringRef s); + operator WStringRef() const { return WStringRef(&buffer_[0], size()); } + size_t size() const { return buffer_.size() - 1; } + const wchar_t *c_str() const { return &buffer_[0]; } + std::wstring str() const { return std::wstring(&buffer_[0], size()); } +}; + +// A converter from UTF-16 to UTF-8. +// It is only provided for Windows since other systems support UTF-8 natively. +class UTF16ToUTF8 { + private: + MemoryBuffer buffer_; + + public: + UTF16ToUTF8() {} + explicit UTF16ToUTF8(WStringRef s); + operator StringRef() const { return StringRef(&buffer_[0], size()); } + size_t size() const { return buffer_.size() - 1; } + const char *c_str() const { return &buffer_[0]; } + std::string str() const { return std::string(&buffer_[0], size()); } + + // Performs conversion returning a system error code instead of + // throwing exception on conversion error. This method may still throw + // in case of memory allocation error. + int convert(WStringRef s); +}; +#endif + +void format_system_error(fmt::Writer &out, int error_code, + fmt::StringRef message) FMT_NOEXCEPT; + +#ifdef _WIN32 +void format_windows_error(fmt::Writer &out, int error_code, + fmt::StringRef message) FMT_NOEXCEPT; +#endif + +// Computes max(Arg, 1) at compile time. It is used to avoid errors about +// allocating an array of 0 size. +template +struct NonZero { + enum { VALUE = Arg }; +}; + +template <> +struct NonZero<0> { + enum { VALUE = 1 }; +}; + +// The value of a formatting argument. It is a POD type to allow storage in +// internal::MemoryBuffer. +struct Value { + template + struct StringValue { + const Char *value; + std::size_t size; + }; + + typedef void (*FormatFunc)( + void *formatter, const void *arg, void *format_str_ptr); + + struct CustomValue { + const void *value; + FormatFunc format; + }; + + union { + int int_value; + unsigned uint_value; + LongLong long_long_value; + ULongLong ulong_long_value; + double double_value; + long double long_double_value; + const void *pointer; + StringValue string; + StringValue sstring; + StringValue ustring; + StringValue wstring; + CustomValue custom; + }; +}; + +struct Arg : Value { + enum Type { + NONE, + // Integer types should go first, + INT, UINT, LONG_LONG, ULONG_LONG, CHAR, LAST_INTEGER_TYPE = CHAR, + // followed by floating-point types. + DOUBLE, LONG_DOUBLE, LAST_NUMERIC_TYPE = LONG_DOUBLE, + CSTRING, STRING, WSTRING, POINTER, CUSTOM + }; + Type type; +}; + +// Makes a Value object from any type. +template +class MakeValue : public Value { + private: + // The following two methods are private to disallow formatting of + // arbitrary pointers. If you want to output a pointer cast it to + // "void *" or "const void *". In particular, this forbids formatting + // of "[const] volatile char *" which is printed as bool by iostreams. + // Do not implement! + template + MakeValue(const T *value); + template + MakeValue(T *value); + + void set_string(StringRef str) { + string.value = str.c_str(); + string.size = str.size(); + } + + void set_string(WStringRef str) { + CharTraits::convert(wchar_t()); + wstring.value = str.c_str(); + wstring.size = str.size(); + } + + // Formats an argument of a custom type, such as a user-defined class. + template + static void format_custom_arg( + void *formatter, const void *arg, void *format_str_ptr) { + format(*static_cast*>(formatter), + *static_cast(format_str_ptr), + *static_cast(arg)); + } + +public: + MakeValue() {} + +#define FMT_MAKE_VALUE(Type, field, TYPE) \ + MakeValue(Type value) { field = value; } \ + static uint64_t type(Type) { return Arg::TYPE; } + + FMT_MAKE_VALUE(bool, int_value, INT) + FMT_MAKE_VALUE(short, int_value, INT) + FMT_MAKE_VALUE(unsigned short, uint_value, UINT) + FMT_MAKE_VALUE(int, int_value, INT) + FMT_MAKE_VALUE(unsigned, uint_value, UINT) + + MakeValue(long value) { + // To minimize the number of types we need to deal with, long is + // translated either to int or to long long depending on its size. + if (sizeof(long) == sizeof(int)) + int_value = static_cast(value); + else + long_long_value = value; + } + static uint64_t type(long) { + return sizeof(long) == sizeof(int) ? Arg::INT : Arg::LONG_LONG; + } + + MakeValue(unsigned long value) { + if (sizeof(unsigned long) == sizeof(unsigned)) + uint_value = static_cast(value); + else + ulong_long_value = value; + } + static uint64_t type(unsigned long) { + return sizeof(unsigned long) == sizeof(unsigned) ? + Arg::UINT : Arg::ULONG_LONG; + } + + FMT_MAKE_VALUE(LongLong, long_long_value, LONG_LONG) + FMT_MAKE_VALUE(ULongLong, ulong_long_value, ULONG_LONG) + FMT_MAKE_VALUE(float, double_value, DOUBLE) + FMT_MAKE_VALUE(double, double_value, DOUBLE) + FMT_MAKE_VALUE(long double, long_double_value, LONG_DOUBLE) + FMT_MAKE_VALUE(signed char, int_value, CHAR) + FMT_MAKE_VALUE(unsigned char, int_value, CHAR) + FMT_MAKE_VALUE(char, int_value, CHAR) + + MakeValue(wchar_t value) { + int_value = internal::CharTraits::convert(value); + } + static uint64_t type(wchar_t) { return Arg::CHAR; } + +#define FMT_MAKE_STR_VALUE(Type, TYPE) \ + MakeValue(Type value) { set_string(value); } \ + static uint64_t type(Type) { return Arg::TYPE; } + + FMT_MAKE_VALUE(char *, string.value, CSTRING) + FMT_MAKE_VALUE(const char *, string.value, CSTRING) + FMT_MAKE_VALUE(const signed char *, sstring.value, CSTRING) + FMT_MAKE_VALUE(const unsigned char *, ustring.value, CSTRING) + FMT_MAKE_STR_VALUE(const std::string &, STRING) + FMT_MAKE_STR_VALUE(StringRef, STRING) + + FMT_MAKE_STR_VALUE(wchar_t *, WSTRING) + FMT_MAKE_STR_VALUE(const wchar_t *, WSTRING) + FMT_MAKE_STR_VALUE(const std::wstring &, WSTRING) + FMT_MAKE_STR_VALUE(WStringRef, WSTRING) + + FMT_MAKE_VALUE(void *, pointer, POINTER) + FMT_MAKE_VALUE(const void *, pointer, POINTER) + + template + MakeValue(const T &value) { + custom.value = &value; + custom.format = &format_custom_arg; + } + template + static uint64_t type(const T &) { return Arg::CUSTOM; } +}; + +#define FMT_DISPATCH(call) static_cast(this)->call + +// An argument visitor. +// To use ArgVisitor define a subclass that implements some or all of the +// visit methods with the same signatures as the methods in ArgVisitor, +// for example, visit_int(int). +// Specify the subclass name as the Impl template parameter. Then calling +// ArgVisitor::visit for some argument will dispatch to a visit method +// specific to the argument type. For example, if the argument type is +// double then visit_double(double) method of a subclass will be called. +// If the subclass doesn't contain a method with this signature, then +// a corresponding method of ArgVisitor will be called. +// +// Example: +// class MyArgVisitor : public ArgVisitor { +// public: +// void visit_int(int value) { print("{}", value); } +// void visit_double(double value) { print("{}", value ); } +// }; +// +// ArgVisitor uses the curiously recurring template pattern: +// http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern +template +class ArgVisitor { + public: + Result visit_unhandled_arg() { return Result(); } + + Result visit_int(int value) { + return FMT_DISPATCH(visit_any_int(value)); + } + Result visit_long_long(LongLong value) { + return FMT_DISPATCH(visit_any_int(value)); + } + Result visit_uint(unsigned value) { + return FMT_DISPATCH(visit_any_int(value)); + } + Result visit_ulong_long(ULongLong value) { + return FMT_DISPATCH(visit_any_int(value)); + } + Result visit_char(int value) { + return FMT_DISPATCH(visit_any_int(value)); + } + template + Result visit_any_int(T) { + return FMT_DISPATCH(visit_unhandled_arg()); + } + + Result visit_double(double value) { + return FMT_DISPATCH(visit_any_double(value)); + } + Result visit_long_double(long double value) { + return FMT_DISPATCH(visit_any_double(value)); + } + template + Result visit_any_double(T) { + return FMT_DISPATCH(visit_unhandled_arg()); + } + + Result visit_string(Arg::StringValue) { + return FMT_DISPATCH(visit_unhandled_arg()); + } + Result visit_wstring(Arg::StringValue) { + return FMT_DISPATCH(visit_unhandled_arg()); + } + Result visit_pointer(const void *) { + return FMT_DISPATCH(visit_unhandled_arg()); + } + Result visit_custom(Arg::CustomValue) { + return FMT_DISPATCH(visit_unhandled_arg()); + } + + Result visit(const Arg &arg) { + switch (arg.type) { + default: + assert(false); + return Result(); + case Arg::INT: + return FMT_DISPATCH(visit_int(arg.int_value)); + case Arg::UINT: + return FMT_DISPATCH(visit_uint(arg.uint_value)); + case Arg::LONG_LONG: + return FMT_DISPATCH(visit_long_long(arg.long_long_value)); + case Arg::ULONG_LONG: + return FMT_DISPATCH(visit_ulong_long(arg.ulong_long_value)); + case Arg::DOUBLE: + return FMT_DISPATCH(visit_double(arg.double_value)); + case Arg::LONG_DOUBLE: + return FMT_DISPATCH(visit_long_double(arg.long_double_value)); + case Arg::CHAR: + return FMT_DISPATCH(visit_char(arg.int_value)); + case Arg::CSTRING: { + Value::StringValue str = arg.string; + str.size = 0; + return FMT_DISPATCH(visit_string(str)); + } + case Arg::STRING: + return FMT_DISPATCH(visit_string(arg.string)); + case Arg::WSTRING: + return FMT_DISPATCH(visit_wstring(arg.wstring)); + case Arg::POINTER: + return FMT_DISPATCH(visit_pointer(arg.pointer)); + case Arg::CUSTOM: + return FMT_DISPATCH(visit_custom(arg.custom)); + } + } +}; + +class RuntimeError : public std::runtime_error { + protected: + RuntimeError() : std::runtime_error("") {} +}; + +template +class ArgFormatter; +} // namespace internal + +/** + An argument list. + */ +class ArgList { + private: + uint64_t types_; + const internal::Value *values_; + + public: + // Maximum number of arguments that can be passed in ArgList. + enum { MAX_ARGS = 16 }; + + ArgList() : types_(0) {} + ArgList(ULongLong types, const internal::Value *values) + : types_(types), values_(values) {} + + /** + Returns the argument at specified index. + */ + internal::Arg operator[](unsigned index) const { + using internal::Arg; + Arg arg; + if (index >= MAX_ARGS) { + arg.type = Arg::NONE; + return arg; + } + unsigned shift = index * 4; + uint64_t mask = 0xf; + Arg::Type type = + static_cast((types_ & (mask << shift)) >> shift); + arg.type = type; + if (type != Arg::NONE) { + internal::Value &value = arg; + value = values_[index]; + } + return arg; + } +}; + +struct FormatSpec; + +namespace internal { + +class FormatterBase { + private: + ArgList args_; + int next_arg_index_; + + // Returns the argument with specified index. + Arg do_get_arg(unsigned arg_index, const char *&error); + + protected: + void set_args(const ArgList &args) { + args_ = args; + next_arg_index_ = 0; + } + + // Returns the next argument. + Arg next_arg(const char *&error); + + // Checks if manual indexing is used and returns the argument with + // specified index. + Arg get_arg(unsigned arg_index, const char *&error); + + template + void write(BasicWriter &w, const Char *start, const Char *end) { + if (start != end) + w << BasicStringRef(start, end - start); + } +}; + +// A printf formatter. +template +class PrintfFormatter : private FormatterBase { + private: + void parse_flags(FormatSpec &spec, const Char *&s); + + // Returns the argument with specified index or, if arg_index is equal + // to the maximum unsigned value, the next argument. + Arg get_arg(const Char *s, + unsigned arg_index = (std::numeric_limits::max)()); + + // Parses argument index, flags and width and returns the argument index. + unsigned parse_header(const Char *&s, FormatSpec &spec); + + public: + void format(BasicWriter &writer, + BasicStringRef format_str, const ArgList &args); +}; +} // namespace internal + +// A formatter. +template +class BasicFormatter : private internal::FormatterBase { + private: + BasicWriter &writer_; + const Char *start_; + + FMT_DISALLOW_COPY_AND_ASSIGN(BasicFormatter); + + // Parses argument index and returns corresponding argument. + internal::Arg parse_arg_index(const Char *&s); + + public: + explicit BasicFormatter(BasicWriter &w) : writer_(w) {} + + BasicWriter &writer() { return writer_; } + + void format(BasicStringRef format_str, const ArgList &args); + + const Char *format(const Char *&format_str, const internal::Arg &arg); +}; + +enum Alignment { + ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC +}; + +// Flags. +enum { + SIGN_FLAG = 1, PLUS_FLAG = 2, MINUS_FLAG = 4, HASH_FLAG = 8, + CHAR_FLAG = 0x10 // Argument has char type - used in error reporting. +}; + +// An empty format specifier. +struct EmptySpec {}; + +// A type specifier. +template +struct TypeSpec : EmptySpec { + Alignment align() const { return ALIGN_DEFAULT; } + unsigned width() const { return 0; } + int precision() const { return -1; } + bool flag(unsigned) const { return false; } + char type() const { return TYPE; } + char fill() const { return ' '; } +}; + +// A width specifier. +struct WidthSpec { + unsigned width_; + // Fill is always wchar_t and cast to char if necessary to avoid having + // two specialization of WidthSpec and its subclasses. + wchar_t fill_; + + WidthSpec(unsigned width, wchar_t fill) : width_(width), fill_(fill) {} + + unsigned width() const { return width_; } + wchar_t fill() const { return fill_; } +}; + +// An alignment specifier. +struct AlignSpec : WidthSpec { + Alignment align_; + + AlignSpec(unsigned width, wchar_t fill, Alignment align = ALIGN_DEFAULT) + : WidthSpec(width, fill), align_(align) {} + + Alignment align() const { return align_; } + + int precision() const { return -1; } +}; + +// An alignment and type specifier. +template +struct AlignTypeSpec : AlignSpec { + AlignTypeSpec(unsigned width, wchar_t fill) : AlignSpec(width, fill) {} + + bool flag(unsigned) const { return false; } + char type() const { return TYPE; } +}; + +// A full format specifier. +struct FormatSpec : AlignSpec { + unsigned flags_; + int precision_; + char type_; + + FormatSpec( + unsigned width = 0, char type = 0, wchar_t fill = ' ') + : AlignSpec(width, fill), flags_(0), precision_(-1), type_(type) {} + + bool flag(unsigned f) const { return (flags_ & f) != 0; } + int precision() const { return precision_; } + char type() const { return type_; } +}; + +// An integer format specifier. +template , typename Char = char> +class IntFormatSpec : public SpecT { + private: + T value_; + + public: + IntFormatSpec(T val, const SpecT &spec = SpecT()) + : SpecT(spec), value_(val) {} + + T value() const { return value_; } +}; + +// A string format specifier. +template +class StrFormatSpec : public AlignSpec { + private: + const T *str_; + + public: + StrFormatSpec(const T *str, unsigned width, wchar_t fill) + : AlignSpec(width, fill), str_(str) {} + + const T *str() const { return str_; } +}; + +/** + Returns an integer format specifier to format the value in base 2. + */ +IntFormatSpec > bin(int value); + +/** + Returns an integer format specifier to format the value in base 8. + */ +IntFormatSpec > oct(int value); + +/** + Returns an integer format specifier to format the value in base 16 using + lower-case letters for the digits above 9. + */ +IntFormatSpec > hex(int value); + +/** + Returns an integer formatter format specifier to format in base 16 using + upper-case letters for the digits above 9. + */ +IntFormatSpec > hexu(int value); + +/** + \rst + Returns an integer format specifier to pad the formatted argument with the + fill character to the specified width using the default (right) numeric + alignment. + + **Example**:: + + MemoryWriter out; + out << pad(hex(0xcafe), 8, '0'); + // out.str() == "0000cafe" + + \endrst + */ +template +IntFormatSpec, Char> pad( + int value, unsigned width, Char fill = ' '); + +#define FMT_DEFINE_INT_FORMATTERS(TYPE) \ +inline IntFormatSpec > bin(TYPE value) { \ + return IntFormatSpec >(value, TypeSpec<'b'>()); \ +} \ + \ +inline IntFormatSpec > oct(TYPE value) { \ + return IntFormatSpec >(value, TypeSpec<'o'>()); \ +} \ + \ +inline IntFormatSpec > hex(TYPE value) { \ + return IntFormatSpec >(value, TypeSpec<'x'>()); \ +} \ + \ +inline IntFormatSpec > hexu(TYPE value) { \ + return IntFormatSpec >(value, TypeSpec<'X'>()); \ +} \ + \ +template \ +inline IntFormatSpec > pad( \ + IntFormatSpec > f, unsigned width) { \ + return IntFormatSpec >( \ + f.value(), AlignTypeSpec(width, ' ')); \ +} \ + \ +/* For compatibility with older compilers we provide two overloads for pad, */ \ +/* one that takes a fill character and one that doesn't. In the future this */ \ +/* can be replaced with one overload making the template argument Char */ \ +/* default to char (C++11). */ \ +template \ +inline IntFormatSpec, Char> pad( \ + IntFormatSpec, Char> f, \ + unsigned width, Char fill) { \ + return IntFormatSpec, Char>( \ + f.value(), AlignTypeSpec(width, fill)); \ +} \ + \ +inline IntFormatSpec > pad( \ + TYPE value, unsigned width) { \ + return IntFormatSpec >( \ + value, AlignTypeSpec<0>(width, ' ')); \ +} \ + \ +template \ +inline IntFormatSpec, Char> pad( \ + TYPE value, unsigned width, Char fill) { \ + return IntFormatSpec, Char>( \ + value, AlignTypeSpec<0>(width, fill)); \ +} + +FMT_DEFINE_INT_FORMATTERS(int) +FMT_DEFINE_INT_FORMATTERS(long) +FMT_DEFINE_INT_FORMATTERS(unsigned) +FMT_DEFINE_INT_FORMATTERS(unsigned long) +FMT_DEFINE_INT_FORMATTERS(LongLong) +FMT_DEFINE_INT_FORMATTERS(ULongLong) + +/** + \rst + Returns a string formatter that pads the formatted argument with the fill + character to the specified width using the default (left) string alignment. + + **Example**:: + + std::string s = str(MemoryWriter() << pad("abc", 8)); + // s == "abc " + + \endrst + */ +template +inline StrFormatSpec pad( + const Char *str, unsigned width, Char fill = ' ') { + return StrFormatSpec(str, width, fill); +} + +inline StrFormatSpec pad( + const wchar_t *str, unsigned width, char fill = ' ') { + return StrFormatSpec(str, width, fill); +} + +// Generates a comma-separated list with results of applying f to +// numbers 0..n-1. +# define FMT_GEN(n, f) FMT_GEN##n(f) +# define FMT_GEN1(f) f(0) +# define FMT_GEN2(f) FMT_GEN1(f), f(1) +# define FMT_GEN3(f) FMT_GEN2(f), f(2) +# define FMT_GEN4(f) FMT_GEN3(f), f(3) +# define FMT_GEN5(f) FMT_GEN4(f), f(4) +# define FMT_GEN6(f) FMT_GEN5(f), f(5) +# define FMT_GEN7(f) FMT_GEN6(f), f(6) +# define FMT_GEN8(f) FMT_GEN7(f), f(7) +# define FMT_GEN9(f) FMT_GEN8(f), f(8) +# define FMT_GEN10(f) FMT_GEN9(f), f(9) +# define FMT_GEN11(f) FMT_GEN10(f), f(10) +# define FMT_GEN12(f) FMT_GEN11(f), f(11) +# define FMT_GEN13(f) FMT_GEN12(f), f(12) +# define FMT_GEN14(f) FMT_GEN13(f), f(13) +# define FMT_GEN15(f) FMT_GEN14(f), f(14) + +namespace internal { +inline uint64_t make_type() { return 0; } + +template +inline uint64_t make_type(const T &arg) { return MakeValue::type(arg); } + +#if FMT_USE_VARIADIC_TEMPLATES +template +inline uint64_t make_type(const Arg &first, const Args & ... tail) { + return make_type(first) | (make_type(tail...) << 4); +} +#else + +struct ArgType { + uint64_t type; + + ArgType() : type(0) {} + + template + ArgType(const T &arg) : type(make_type(arg)) {} +}; + +# define FMT_ARG_TYPE_DEFAULT(n) ArgType t##n = ArgType() + +inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) { + return t0.type | (t1.type << 4) | (t2.type << 8) | (t3.type << 12) | + (t4.type << 16) | (t5.type << 20) | (t6.type << 24) | (t7.type << 28) | + (t8.type << 32) | (t9.type << 36) | (t10.type << 40) | (t11.type << 44) | + (t12.type << 48) | (t13.type << 52) | (t14.type << 56); +} +#endif +} // namespace internal + +# define FMT_MAKE_TEMPLATE_ARG(n) typename T##n +# define FMT_MAKE_ARG_TYPE(n) T##n +# define FMT_MAKE_ARG(n) const T##n &v##n +# define FMT_MAKE_REF_char(n) fmt::internal::MakeValue(v##n) +# define FMT_MAKE_REF_wchar_t(n) fmt::internal::MakeValue(v##n) + +#if FMT_USE_VARIADIC_TEMPLATES +// Defines a variadic function returning void. +# define FMT_VARIADIC_VOID(func, arg_type) \ + template \ + void func(arg_type arg1, const Args & ... args) { \ + const fmt::internal::Value values[ \ + fmt::internal::NonZero::VALUE] = { \ + fmt::internal::MakeValue(args)... \ + }; \ + func(arg1, ArgList(fmt::internal::make_type(args...), values)); \ + } + +// Defines a variadic constructor. +# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ + template \ + ctor(arg0_type arg0, arg1_type arg1, const Args & ... args) { \ + using fmt::internal::MakeValue; \ + const fmt::internal::Value values[ \ + fmt::internal::NonZero::VALUE] = { \ + MakeValue(args)... \ + }; \ + func(arg0, arg1, ArgList(fmt::internal::make_type(args...), values)); \ + } + +#else + +# define FMT_MAKE_REF(n) fmt::internal::MakeValue(v##n) +# define FMT_MAKE_REF2(n) v##n + +// Defines a wrapper for a function taking one argument of type arg_type +// and n additional arguments of arbitrary types. +# define FMT_WRAP1(func, arg_type, n) \ + template \ + inline void func(arg_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ + const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF)}; \ + func(arg1, fmt::ArgList( \ + fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), vals)); \ + } + +// Emulates a variadic function returning void on a pre-C++11 compiler. +# define FMT_VARIADIC_VOID(func, arg_type) \ + inline void func(arg_type arg) { func(arg, fmt::ArgList()); } \ + FMT_WRAP1(func, arg_type, 1) FMT_WRAP1(func, arg_type, 2) \ + FMT_WRAP1(func, arg_type, 3) FMT_WRAP1(func, arg_type, 4) \ + FMT_WRAP1(func, arg_type, 5) FMT_WRAP1(func, arg_type, 6) \ + FMT_WRAP1(func, arg_type, 7) FMT_WRAP1(func, arg_type, 8) \ + FMT_WRAP1(func, arg_type, 9) FMT_WRAP1(func, arg_type, 10) + +# define FMT_CTOR(ctor, func, arg0_type, arg1_type, n) \ + template \ + ctor(arg0_type arg0, arg1_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ + const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF)}; \ + func(arg0, arg1, fmt::ArgList( \ + fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), vals)); \ + } + +// Emulates a variadic constructor on a pre-C++11 compiler. +# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 1) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 2) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 3) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 4) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 5) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 6) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 7) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 8) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 9) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 10) +#endif + +// Generates a comma-separated list with results of applying f to pairs +// (argument, index). +#define FMT_FOR_EACH1(f, x0) f(x0, 0) +#define FMT_FOR_EACH2(f, x0, x1) \ + FMT_FOR_EACH1(f, x0), f(x1, 1) +#define FMT_FOR_EACH3(f, x0, x1, x2) \ + FMT_FOR_EACH2(f, x0 ,x1), f(x2, 2) +#define FMT_FOR_EACH4(f, x0, x1, x2, x3) \ + FMT_FOR_EACH3(f, x0, x1, x2), f(x3, 3) +#define FMT_FOR_EACH5(f, x0, x1, x2, x3, x4) \ + FMT_FOR_EACH4(f, x0, x1, x2, x3), f(x4, 4) +#define FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5) \ + FMT_FOR_EACH5(f, x0, x1, x2, x3, x4), f(x5, 5) +#define FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6) \ + FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5), f(x6, 6) +#define FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7) \ + FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6), f(x7, 7) +#define FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8) \ + FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7), f(x8, 8) +#define FMT_FOR_EACH10(f, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) \ + FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8), f(x9, 9) + +/** + An error returned by an operating system or a language runtime, + for example a file opening error. +*/ +class SystemError : public internal::RuntimeError { + private: + void init(int err_code, StringRef format_str, ArgList args); + + protected: + int error_code_; + + typedef char Char; // For FMT_VARIADIC_CTOR. + + SystemError() {} + + public: + /** + \rst + Constructs a :class:`fmt::SystemError` object with the description + of the form + + .. parsed-literal:: + **: ** + + where ** is the formatted message and ** is + the system message corresponding to the error code. + *error_code* is a system error code as given by ``errno``. + If *error_code* is not a valid error code such as -1, the system message + may look like "Unknown error -1" and is platform-dependent. + + **Example**:: + + // This throws a SystemError with the description + // cannot open file 'madeup': No such file or directory + // or similar (system message may vary). + const char *filename = "madeup"; + std::FILE *file = std::fopen(filename, "r"); + if (!file) + throw fmt::SystemError(errno, "cannot open file '{}'", filename); + \endrst + */ + SystemError(int error_code, StringRef message) { + init(error_code, message, ArgList()); + } + FMT_VARIADIC_CTOR(SystemError, init, int, StringRef) + + int error_code() const { return error_code_; } +}; + +/** + \rst + This template provides operations for formatting and writing data into + a character stream. The output is stored in a buffer provided by a subclass + such as :class:`fmt::BasicMemoryWriter`. + + You can use one of the following typedefs for common character types: + + +---------+----------------------+ + | Type | Definition | + +=========+======================+ + | Writer | BasicWriter | + +---------+----------------------+ + | WWriter | BasicWriter | + +---------+----------------------+ + + \endrst + */ +template +class BasicWriter { + private: + // Output buffer. + internal::Buffer &buffer_; + + FMT_DISALLOW_COPY_AND_ASSIGN(BasicWriter); + + typedef typename internal::CharTraits::CharPtr CharPtr; + +#if _SECURE_SCL + // Returns pointer value. + static Char *get(CharPtr p) { return p.base(); } +#else + static Char *get(Char *p) { return p; } +#endif + + // Fills the padding around the content and returns the pointer to the + // content area. + static CharPtr fill_padding(CharPtr buffer, + unsigned total_size, std::size_t content_size, wchar_t fill); + + // Grows the buffer by n characters and returns a pointer to the newly + // allocated area. + CharPtr grow_buffer(std::size_t n) { + std::size_t size = buffer_.size(); + buffer_.resize(size + n); + return internal::make_ptr(&buffer_[size], n); + } + + // Prepare a buffer for integer formatting. + CharPtr prepare_int_buffer(unsigned num_digits, + const EmptySpec &, const char *prefix, unsigned prefix_size) { + unsigned size = prefix_size + num_digits; + CharPtr p = grow_buffer(size); + std::copy(prefix, prefix + prefix_size, p); + return p + size - 1; + } + + template + CharPtr prepare_int_buffer(unsigned num_digits, + const Spec &spec, const char *prefix, unsigned prefix_size); + + // Formats an integer. + template + void write_int(T value, Spec spec); + + // Formats a floating-point number (double or long double). + template + void write_double(T value, const FormatSpec &spec); + + // Writes a formatted string. + template + CharPtr write_str( + const StrChar *s, std::size_t size, const AlignSpec &spec); + + template + void write_str( + const internal::Arg::StringValue &str, const FormatSpec &spec); + + // This method is private to disallow writing a wide string to a + // char stream and vice versa. If you want to print a wide string + // as a pointer as std::ostream does, cast it to const void*. + // Do not implement! + void operator<<(typename internal::CharTraits::UnsupportedStrType); + + // Appends floating-point length specifier to the format string. + // The second argument is only used for overload resolution. + void append_float_length(Char *&format_ptr, long double) { + *format_ptr++ = 'L'; + } + + template + void append_float_length(Char *&, T) {} + + friend class internal::ArgFormatter; + friend class internal::PrintfFormatter; + + protected: + /** + Constructs a ``BasicWriter`` object. + */ + explicit BasicWriter(internal::Buffer &b) : buffer_(b) {} + + public: + /** + Destroys a ``BasicWriter`` object. + */ + virtual ~BasicWriter() {} + + /** + Returns the total number of characters written. + */ + std::size_t size() const { return buffer_.size(); } + + /** + Returns a pointer to the output buffer content. No terminating null + character is appended. + */ + const Char *data() const FMT_NOEXCEPT { return &buffer_[0]; } + + /** + Returns a pointer to the output buffer content with terminating null + character appended. + */ + const Char *c_str() const { + std::size_t size = buffer_.size(); + buffer_.reserve(size + 1); + buffer_[size] = '\0'; + return &buffer_[0]; + } + + /** + Returns the content of the output buffer as an `std::string`. + */ + std::basic_string str() const { + return std::basic_string(&buffer_[0], buffer_.size()); + } + + /** + \rst + Writes formatted data. + + *args* is an argument list representing arbitrary arguments. + + **Example**:: + + MemoryWriter out; + out.write("Current point:\n"); + out.write("({:+f}, {:+f})", -3.14, 3.14); + + This will write the following output to the ``out`` object: + + .. code-block:: none + + Current point: + (-3.140000, +3.140000) + + The output can be accessed using :func:`data()`, :func:`c_str` or + :func:`str` methods. + + See also :ref:`syntax`. + \endrst + */ + void write(BasicStringRef format, ArgList args) { + BasicFormatter(*this).format(format, args); + } + FMT_VARIADIC_VOID(write, BasicStringRef) + + BasicWriter &operator<<(int value) { + return *this << IntFormatSpec(value); + } + BasicWriter &operator<<(unsigned value) { + return *this << IntFormatSpec(value); + } + BasicWriter &operator<<(long value) { + return *this << IntFormatSpec(value); + } + BasicWriter &operator<<(unsigned long value) { + return *this << IntFormatSpec(value); + } + BasicWriter &operator<<(LongLong value) { + return *this << IntFormatSpec(value); + } + + /** + Formats *value* and writes it to the stream. + */ + BasicWriter &operator<<(ULongLong value) { + return *this << IntFormatSpec(value); + } + + BasicWriter &operator<<(double value) { + write_double(value, FormatSpec()); + return *this; + } + + /** + Formats *value* using the general format for floating-point numbers + (``'g'``) and writes it to the stream. + */ + BasicWriter &operator<<(long double value) { + write_double(value, FormatSpec()); + return *this; + } + + /** + Writes a character to the stream. + */ + BasicWriter &operator<<(char value) { + buffer_.push_back(value); + return *this; + } + + BasicWriter &operator<<(wchar_t value) { + buffer_.push_back(internal::CharTraits::convert(value)); + return *this; + } + + /** + Writes *value* to the stream. + */ + BasicWriter &operator<<(fmt::BasicStringRef value) { + const Char *str = value.c_str(); + buffer_.append(str, str + value.size()); + return *this; + } + + template + BasicWriter &operator<<(IntFormatSpec spec) { + internal::CharTraits::convert(FillChar()); + write_int(spec.value(), spec); + return *this; + } + + template + BasicWriter &operator<<(const StrFormatSpec &spec) { + const StrChar *s = spec.str(); + // TODO: error if fill is not convertible to Char + write_str(s, std::char_traits::length(s), spec); + return *this; + } + + void clear() FMT_NOEXCEPT { buffer_.clear(); } +}; + +template +template +typename BasicWriter::CharPtr BasicWriter::write_str( + const StrChar *s, std::size_t size, const AlignSpec &spec) { + CharPtr out = CharPtr(); + if (spec.width() > size) { + out = grow_buffer(spec.width()); + Char fill = static_cast(spec.fill()); + if (spec.align() == ALIGN_RIGHT) { + std::fill_n(out, spec.width() - size, fill); + out += spec.width() - size; + } else if (spec.align() == ALIGN_CENTER) { + out = fill_padding(out, spec.width(), size, fill); + } else { + std::fill_n(out + size, spec.width() - size, fill); + } + } else { + out = grow_buffer(size); + } + std::copy(s, s + size, out); + return out; +} + +template +typename BasicWriter::CharPtr + BasicWriter::fill_padding( + CharPtr buffer, unsigned total_size, + std::size_t content_size, wchar_t fill) { + std::size_t padding = total_size - content_size; + std::size_t left_padding = padding / 2; + Char fill_char = static_cast(fill); + std::fill_n(buffer, left_padding, fill_char); + buffer += left_padding; + CharPtr content = buffer; + std::fill_n(buffer + content_size, padding - left_padding, fill_char); + return content; +} + +template +template +typename BasicWriter::CharPtr + BasicWriter::prepare_int_buffer( + unsigned num_digits, const Spec &spec, + const char *prefix, unsigned prefix_size) { + unsigned width = spec.width(); + Alignment align = spec.align(); + Char fill = static_cast(spec.fill()); + if (spec.precision() > static_cast(num_digits)) { + // Octal prefix '0' is counted as a digit, so ignore it if precision + // is specified. + if (prefix_size > 0 && prefix[prefix_size - 1] == '0') + --prefix_size; + unsigned number_size = prefix_size + spec.precision(); + AlignSpec subspec(number_size, '0', ALIGN_NUMERIC); + if (number_size >= width) + return prepare_int_buffer(num_digits, subspec, prefix, prefix_size); + buffer_.reserve(width); + unsigned fill_size = width - number_size; + if (align != ALIGN_LEFT) { + CharPtr p = grow_buffer(fill_size); + std::fill(p, p + fill_size, fill); + } + CharPtr result = prepare_int_buffer( + num_digits, subspec, prefix, prefix_size); + if (align == ALIGN_LEFT) { + CharPtr p = grow_buffer(fill_size); + std::fill(p, p + fill_size, fill); + } + return result; + } + unsigned size = prefix_size + num_digits; + if (width <= size) { + CharPtr p = grow_buffer(size); + std::copy(prefix, prefix + prefix_size, p); + return p + size - 1; + } + CharPtr p = grow_buffer(width); + CharPtr end = p + width; + if (align == ALIGN_LEFT) { + std::copy(prefix, prefix + prefix_size, p); + p += size; + std::fill(p, end, fill); + } else if (align == ALIGN_CENTER) { + p = fill_padding(p, width, size, fill); + std::copy(prefix, prefix + prefix_size, p); + p += size; + } else { + if (align == ALIGN_NUMERIC) { + if (prefix_size != 0) { + p = std::copy(prefix, prefix + prefix_size, p); + size -= prefix_size; + } + } else { + std::copy(prefix, prefix + prefix_size, end - size); + } + std::fill(p, end - size, fill); + p = end; + } + return p - 1; +} + +template +template +void BasicWriter::write_int(T value, Spec spec) { + unsigned prefix_size = 0; + typedef typename internal::IntTraits::MainType UnsignedType; + UnsignedType abs_value = value; + char prefix[4] = ""; + if (internal::is_negative(value)) { + prefix[0] = '-'; + ++prefix_size; + abs_value = 0 - abs_value; + } else if (spec.flag(SIGN_FLAG)) { + prefix[0] = spec.flag(PLUS_FLAG) ? '+' : ' '; + ++prefix_size; + } + switch (spec.type()) { + case 0: case 'd': { + unsigned num_digits = internal::count_digits(abs_value); + CharPtr p = prepare_int_buffer( + num_digits, spec, prefix, prefix_size) + 1 - num_digits; + internal::format_decimal(get(p), abs_value, num_digits); + break; + } + case 'x': case 'X': { + UnsignedType n = abs_value; + if (spec.flag(HASH_FLAG)) { + prefix[prefix_size++] = '0'; + prefix[prefix_size++] = spec.type(); + } + unsigned num_digits = 0; + do { + ++num_digits; + } while ((n >>= 4) != 0); + Char *p = get(prepare_int_buffer( + num_digits, spec, prefix, prefix_size)); + n = abs_value; + const char *digits = spec.type() == 'x' ? + "0123456789abcdef" : "0123456789ABCDEF"; + do { + *p-- = digits[n & 0xf]; + } while ((n >>= 4) != 0); + break; + } + case 'b': case 'B': { + UnsignedType n = abs_value; + if (spec.flag(HASH_FLAG)) { + prefix[prefix_size++] = '0'; + prefix[prefix_size++] = spec.type(); + } + unsigned num_digits = 0; + do { + ++num_digits; + } while ((n >>= 1) != 0); + Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size)); + n = abs_value; + do { + *p-- = '0' + (n & 1); + } while ((n >>= 1) != 0); + break; + } + case 'o': { + UnsignedType n = abs_value; + if (spec.flag(HASH_FLAG)) + prefix[prefix_size++] = '0'; + unsigned num_digits = 0; + do { + ++num_digits; + } while ((n >>= 3) != 0); + Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size)); + n = abs_value; + do { + *p-- = '0' + (n & 7); + } while ((n >>= 3) != 0); + break; + } + default: + internal::report_unknown_type( + spec.type(), spec.flag(CHAR_FLAG) ? "char" : "integer"); + break; + } +} + +template +template +void BasicWriter::write_double( + T value, const FormatSpec &spec) { + // Check type. + char type = spec.type(); + bool upper = false; + switch (type) { + case 0: + type = 'g'; + break; + case 'e': case 'f': case 'g': case 'a': + break; + case 'F': +#ifdef _MSC_VER + // MSVC's printf doesn't support 'F'. + type = 'f'; +#endif + // Fall through. + case 'E': case 'G': case 'A': + upper = true; + break; + default: + internal::report_unknown_type(type, "double"); + break; + } + + char sign = 0; + // Use getsign instead of value < 0 because the latter is always + // false for NaN. + if (internal::getsign(static_cast(value))) { + sign = '-'; + value = -value; + } else if (spec.flag(SIGN_FLAG)) { + sign = spec.flag(PLUS_FLAG) ? '+' : ' '; + } + + if (value != value) { + // Format NaN ourselves because sprintf's output is not consistent + // across platforms. + std::size_t nan_size = 4; + const char *nan = upper ? " NAN" : " nan"; + if (!sign) { + --nan_size; + ++nan; + } + CharPtr out = write_str(nan, nan_size, spec); + if (sign) + *out = sign; + return; + } + + if (internal::isinfinity(value)) { + // Format infinity ourselves because sprintf's output is not consistent + // across platforms. + std::size_t inf_size = 4; + const char *inf = upper ? " INF" : " inf"; + if (!sign) { + --inf_size; + ++inf; + } + CharPtr out = write_str(inf, inf_size, spec); + if (sign) + *out = sign; + return; + } + + std::size_t offset = buffer_.size(); + unsigned width = spec.width(); + if (sign) { + buffer_.reserve(buffer_.size() + (std::max)(width, 1u)); + if (width > 0) + --width; + ++offset; + } + + // Build format string. + enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg + Char format[MAX_FORMAT_SIZE]; + Char *format_ptr = format; + *format_ptr++ = '%'; + unsigned width_for_sprintf = width; + if (spec.flag(HASH_FLAG)) + *format_ptr++ = '#'; + if (spec.align() == ALIGN_CENTER) { + width_for_sprintf = 0; + } else { + if (spec.align() == ALIGN_LEFT) + *format_ptr++ = '-'; + if (width != 0) + *format_ptr++ = '*'; + } + if (spec.precision() >= 0) { + *format_ptr++ = '.'; + *format_ptr++ = '*'; + } + + append_float_length(format_ptr, value); + *format_ptr++ = type; + *format_ptr = '\0'; + + // Format using snprintf. + Char fill = static_cast(spec.fill()); + for (;;) { + std::size_t buffer_size = buffer_.capacity() - offset; +#if _MSC_VER + // MSVC's vsnprintf_s doesn't work with zero size, so reserve + // space for at least one extra character to make the size non-zero. + // Note that the buffer's capacity will increase by more than 1. + if (buffer_size == 0) { + buffer_.reserve(offset + 1); + buffer_size = buffer_.capacity() - offset; + } +#endif + Char *start = &buffer_[offset]; + int n = internal::CharTraits::format_float( + start, buffer_size, format, width_for_sprintf, spec.precision(), value); + if (n >= 0 && offset + n < buffer_.capacity()) { + if (sign) { + if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) || + *start != ' ') { + *(start - 1) = sign; + sign = 0; + } else { + *(start - 1) = fill; + } + ++n; + } + if (spec.align() == ALIGN_CENTER && + spec.width() > static_cast(n)) { + width = spec.width(); + CharPtr p = grow_buffer(width); + std::copy(p, p + n, p + (width - n) / 2); + fill_padding(p, spec.width(), n, fill); + return; + } + if (spec.fill() != ' ' || sign) { + while (*start == ' ') + *start++ = fill; + if (sign) + *(start - 1) = sign; + } + grow_buffer(n); + return; + } + // If n is negative we ask to increase the capacity by at least 1, + // but as std::vector, the buffer grows exponentially. + buffer_.reserve(n >= 0 ? offset + n + 1 : buffer_.capacity() + 1); + } +} + +/** + \rst + This template provides operations for formatting and writing data into + a character stream. The output is stored in a memory buffer that grows + dynamically. + + You can use one of the following typedefs for common character types + and the standard allocator: + + +---------------+-----------------------------------------------+ + | Type | Definition | + +===============+===============================================+ + | MemoryWriter | BasicWriter> | + +---------------+-----------------------------------------------+ + | WMemoryWriter | BasicWriter> | + +---------------+-----------------------------------------------+ + + **Example**:: + + MemoryWriter out; + out << "The answer is " << 42 << "\n"; + out.write("({:+f}, {:+f})", -3.14, 3.14); + + This will write the following output to the ``out`` object: + + .. code-block:: none + + The answer is 42 + (-3.140000, +3.140000) + + The output can be converted to an ``std::string`` with ``out.str()`` or + accessed as a C string with ``out.c_str()``. + \endrst + */ +template > +class BasicMemoryWriter : public BasicWriter { + private: + internal::MemoryBuffer buffer_; + + public: + explicit BasicMemoryWriter(const Allocator& alloc = Allocator()) + : BasicWriter(buffer_), buffer_(alloc) {} + +#if FMT_USE_RVALUE_REFERENCES + /** + Constructs a :class:`fmt::BasicMemoryWriter` object moving the content + of the other object to it. + */ + BasicMemoryWriter(BasicMemoryWriter &&other) + : BasicWriter(buffer_), buffer_(std::move(other.buffer_)) { + } + + /** + Moves the content of the other ``BasicMemoryWriter`` object to this one. + */ + BasicMemoryWriter &operator=(BasicMemoryWriter &&other) { + buffer_ = std::move(other.buffer_); + return *this; + } +#endif +}; + +typedef BasicMemoryWriter MemoryWriter; +typedef BasicMemoryWriter WMemoryWriter; + +// Formats a value. +template +void format(BasicFormatter &f, const Char *&format_str, const T &value) { + std::basic_ostringstream os; + os << value; + internal::Arg arg; + internal::Value &arg_value = arg; + std::basic_string str = os.str(); + arg_value = internal::MakeValue(str); + arg.type = static_cast(internal::MakeValue::type(str)); + format_str = f.format(format_str, arg); +} + +// Reports a system error without throwing an exception. +// Can be used to report errors from destructors. +void report_system_error(int error_code, StringRef message) FMT_NOEXCEPT; + +#ifdef _WIN32 + +/** A Windows error. */ +class WindowsError : public SystemError { + private: + void init(int error_code, StringRef format_str, ArgList args); + + public: + /** + \rst + Constructs a :class:`fmt::WindowsError` object with the description + of the form + + .. parsed-literal:: + **: ** + + where ** is the formatted message and ** is the system + message corresponding to the error code. + *error_code* is a Windows error code as given by ``GetLastError``. + If *error_code* is not a valid error code such as -1, the system message + will look like "error -1". + + **Example**:: + + // This throws a WindowsError with the description + // cannot open file 'madeup': The system cannot find the file specified. + // or similar (system message may vary). + const char *filename = "madeup"; + LPOFSTRUCT of = LPOFSTRUCT(); + HFILE file = OpenFile(filename, &of, OF_READ); + if (file == HFILE_ERROR) + throw fmt::WindowsError(GetLastError(), "cannot open file '{}'", filename); + \endrst + */ + WindowsError(int error_code, StringRef message) { + init(error_code, message, ArgList()); + } + FMT_VARIADIC_CTOR(WindowsError, init, int, StringRef) +}; + +// Reports a Windows error without throwing an exception. +// Can be used to report errors from destructors. +void report_windows_error(int error_code, StringRef message) FMT_NOEXCEPT; + +#endif + +enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE }; + +/** + Formats a string and prints it to stdout using ANSI escape sequences + to specify color (experimental). + Example: + PrintColored(fmt::RED, "Elapsed time: {0:.2f} seconds") << 1.23; + */ +void print_colored(Color c, StringRef format, ArgList args); + +/** + \rst + Formats arguments and returns the result as a string. + + **Example**:: + + std::string message = format("The answer is {}", 42); + \endrst +*/ +inline std::string format(StringRef format_str, ArgList args) { + MemoryWriter w; + w.write(format_str, args); + return w.str(); +} + +inline std::wstring format(WStringRef format_str, ArgList args) { + WMemoryWriter w; + w.write(format_str, args); + return w.str(); +} + +/** + \rst + Prints formatted data to the file *f*. + + **Example**:: + + print(stderr, "Don't {}!", "panic"); + \endrst + */ +void print(std::FILE *f, StringRef format_str, ArgList args); + +/** + \rst + Prints formatted data to ``stdout``. + + **Example**:: + + print("Elapsed time: {0:.2f} seconds", 1.23); + \endrst + */ +void print(StringRef format_str, ArgList args); + +/** + \rst + Prints formatted data to the stream *os*. + + **Example**:: + + print(cerr, "Don't {}!", "panic"); + \endrst + */ +void print(std::ostream &os, StringRef format_str, ArgList args); + +template +void printf(BasicWriter &w, BasicStringRef format, ArgList args) { + internal::PrintfFormatter().format(w, format, args); +} + +/** + \rst + Formats arguments and returns the result as a string. + + **Example**:: + + std::string message = fmt::sprintf("The answer is %d", 42); + \endrst +*/ +inline std::string sprintf(StringRef format, ArgList args) { + MemoryWriter w; + printf(w, format, args); + return w.str(); +} + +/** + \rst + Prints formatted data to the file *f*. + + **Example**:: + + fmt::fprintf(stderr, "Don't %s!", "panic"); + \endrst + */ +int fprintf(std::FILE *f, StringRef format, ArgList args); + +/** + \rst + Prints formatted data to ``stdout``. + + **Example**:: + + fmt::printf("Elapsed time: %.2f seconds", 1.23); + \endrst + */ +inline int printf(StringRef format, ArgList args) { + return fprintf(stdout, format, args); +} + +/** + Fast integer formatter. + */ +class FormatInt { + private: + // Buffer should be large enough to hold all digits (digits10 + 1), + // a sign and a null character. + enum {BUFFER_SIZE = std::numeric_limits::digits10 + 3}; + mutable char buffer_[BUFFER_SIZE]; + char *str_; + + // Formats value in reverse and returns the number of digits. + char *format_decimal(ULongLong value) { + char *buffer_end = buffer_ + BUFFER_SIZE - 1; + while (value >= 100) { + // Integer division is slow so do it for a group of two digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + unsigned index = (value % 100) * 2; + value /= 100; + *--buffer_end = internal::Data::DIGITS[index + 1]; + *--buffer_end = internal::Data::DIGITS[index]; + } + if (value < 10) { + *--buffer_end = static_cast('0' + value); + return buffer_end; + } + unsigned index = static_cast(value * 2); + *--buffer_end = internal::Data::DIGITS[index + 1]; + *--buffer_end = internal::Data::DIGITS[index]; + return buffer_end; + } + + void FormatSigned(LongLong value) { + ULongLong abs_value = static_cast(value); + bool negative = value < 0; + if (negative) + abs_value = 0 - abs_value; + str_ = format_decimal(abs_value); + if (negative) + *--str_ = '-'; + } + + public: + explicit FormatInt(int value) { FormatSigned(value); } + explicit FormatInt(long value) { FormatSigned(value); } + explicit FormatInt(LongLong value) { FormatSigned(value); } + explicit FormatInt(unsigned value) : str_(format_decimal(value)) {} + explicit FormatInt(unsigned long value) : str_(format_decimal(value)) {} + explicit FormatInt(ULongLong value) : str_(format_decimal(value)) {} + + /** + Returns the number of characters written to the output buffer. + */ + std::size_t size() const { return buffer_ - str_ + BUFFER_SIZE - 1; } + + /** + Returns a pointer to the output buffer content. No terminating null + character is appended. + */ + const char *data() const { return str_; } + + /** + Returns a pointer to the output buffer content with terminating null + character appended. + */ + const char *c_str() const { + buffer_[BUFFER_SIZE - 1] = '\0'; + return str_; + } + + /** + Returns the content of the output buffer as an `std::string`. + */ + std::string str() const { return std::string(str_, size()); } +}; + +// Formats a decimal integer value writing into buffer and returns +// a pointer to the end of the formatted string. This function doesn't +// write a terminating null character. +template +inline void format_decimal(char *&buffer, T value) { + typename internal::IntTraits::MainType abs_value = value; + if (internal::is_negative(value)) { + *buffer++ = '-'; + abs_value = 0 - abs_value; + } + if (abs_value < 100) { + if (abs_value < 10) { + *buffer++ = static_cast('0' + abs_value); + return; + } + unsigned index = static_cast(abs_value * 2); + *buffer++ = internal::Data::DIGITS[index]; + *buffer++ = internal::Data::DIGITS[index + 1]; + return; + } + unsigned num_digits = internal::count_digits(abs_value); + internal::format_decimal(buffer, abs_value, num_digits); + buffer += num_digits; +} +} + +#if FMT_GCC_VERSION +// Use the system_header pragma to suppress warnings about variadic macros +// because suppressing -Wvariadic-macros with the diagnostic pragma doesn't +// work. It is used at the end because we want to suppress as little warnings +// as possible. +# pragma GCC system_header +#endif + +// This is used to work around VC++ bugs in handling variadic macros. +#define FMT_EXPAND(args) args + +// Returns the number of arguments. +// Based on https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s. +#define FMT_NARG(...) FMT_NARG_(__VA_ARGS__, FMT_RSEQ_N()) +#define FMT_NARG_(...) FMT_EXPAND(FMT_ARG_N(__VA_ARGS__)) +#define FMT_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N +#define FMT_RSEQ_N() 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 + +#define FMT_CONCAT(a, b) a##b +#define FMT_FOR_EACH_(N, f, ...) \ + FMT_EXPAND(FMT_CONCAT(FMT_FOR_EACH, N)(f, __VA_ARGS__)) +#define FMT_FOR_EACH(f, ...) \ + FMT_EXPAND(FMT_FOR_EACH_(FMT_NARG(__VA_ARGS__), f, __VA_ARGS__)) + +#define FMT_ADD_ARG_NAME(type, index) type arg##index +#define FMT_GET_ARG_NAME(type, index) arg##index + +#if FMT_USE_VARIADIC_TEMPLATES +# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ + template \ + ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ + const Args & ... args) { \ + using fmt::internal::Value; \ + const Value values[fmt::internal::NonZero::VALUE] = { \ + fmt::internal::MakeValue(args)... \ + }; \ + call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \ + fmt::internal::make_type(args...), values)); \ + } +#else +// Defines a wrapper for a function taking __VA_ARGS__ arguments +// and n additional arguments of arbitrary types. +# define FMT_WRAP(Char, ReturnType, func, call, n, ...) \ + template \ + inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ + FMT_GEN(n, FMT_MAKE_ARG)) { \ + const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF_##Char)}; \ + call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \ + fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), vals)); \ + } + +# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ + inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__)) { \ + call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList()); \ + } \ + FMT_WRAP(Char, ReturnType, func, call, 1, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 2, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 3, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 4, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 5, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 6, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 7, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 8, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 9, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 10, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 11, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 12, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 13, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 14, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 15, __VA_ARGS__) +#endif // FMT_USE_VARIADIC_TEMPLATES + +/** + \rst + Defines a variadic function with the specified return type, function name + and argument types passed as variable arguments to this macro. + + **Example**:: + + void print_error(const char *file, int line, const char *format, + fmt::ArgList args) { + fmt::print("{}: {}: ", file, line); + fmt::print(format, args); + } + FMT_VARIADIC(void, print_error, const char *, int, const char *) + + ``FMT_VARIADIC`` is used for compatibility with legacy C++ compilers that + don't implement variadic templates. You don't have to use this macro if + you don't need legacy compiler support and can use variadic templates + directly:: + + template + void print_error(const char *file, int line, const char *format, + const Args & ... args) { + fmt::print("{}: {}: ", file, line); + fmt::print(format, args...); + } + \endrst + */ +#define FMT_VARIADIC(ReturnType, func, ...) \ + FMT_VARIADIC_(char, ReturnType, func, return func, __VA_ARGS__) + +#define FMT_VARIADIC_W(ReturnType, func, ...) \ + FMT_VARIADIC_(wchar_t, ReturnType, func, return func, __VA_ARGS__) + +namespace fmt { +FMT_VARIADIC(std::string, format, StringRef) +FMT_VARIADIC_W(std::wstring, format, WStringRef) +FMT_VARIADIC(void, print, StringRef) +FMT_VARIADIC(void, print, std::FILE *, StringRef) +FMT_VARIADIC(void, print, std::ostream &, StringRef) +FMT_VARIADIC(void, print_colored, Color, StringRef) +FMT_VARIADIC(std::string, sprintf, StringRef) +FMT_VARIADIC(int, printf, StringRef) +FMT_VARIADIC(int, fprintf, std::FILE *, StringRef) +} + +// Restore warnings. +#if FMT_GCC_VERSION >= 406 +# pragma GCC diagnostic pop +#endif + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + +#ifdef FMT_HEADER_ONLY +# include "format.cc" +#endif + +#endif // FMT_FORMAT_H_ diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.h new file mode 100644 index 0000000000..5602141cb8 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.h @@ -0,0 +1,136 @@ +#ifndef BOX_H +#define BOX_H + +#include +#include + +#include "grid.h" +#include "vertices.h" + +namespace grid +{ + +namespace ba = boost::adaptors; + +template +class Box +{ + public: + typedef GridRef GridProxy; + typedef typename GridProxy::Vertex Position; + + struct InternalTest; + struct BoundaryTest; + struct BoundsTest; + struct PositionToVertex; + + class FreudenthalLinkIterator; + typedef boost::iterator_range FreudenthalLinkRange; + + typedef VerticesIterator VI; + typedef boost::transformed_range + > VertexRange; + + // Topology interface + typedef typename GridProxy::Index Vertex; + typedef boost::transformed_range + > Link; + + + Box(): g_(0, Position()) {} + Box(const Position& shape): + g_(0, shape), to_(shape - Position::one()) {} + Box(const Position& shape, + const Position& from, + const Position& to): + g_(0, shape), from_(from), to_(to) {} + + + const Position& from() const { return from_; } + const Position& to() const { return to_; } + Position& from() { return from_; } + Position& to() { return to_; } + Position shape() const { return to_ - from_ + Position::one(); } + const Position& grid_shape() const { return g_.shape(); } + static unsigned dimension() { return D; } + + size_t size() const { size_t c = 1; for (unsigned i = 0; i < D; ++i) c *= (to_[i] - from_[i] + 1); return c; } + + VertexRange vertices() const { return boost::iterator_range(VI::begin(from_, to_), VI::end(from_, to_)) + | ba::transformed(position_to_vertex()); } + Link link(const Position& p) const { return FreudenthalLinkRange(FreudenthalLinkIterator::begin(p), FreudenthalLinkIterator::end(p)) + | ba::filtered(bounds_test()) + | ba::transformed(position_to_vertex()); } + Link link(const Vertex& v) const { return link(position(v)); } + + Box intersect(const Box& other) const; + bool intersects(const Box& other) const; + void merge(const Box& other); + + bool contains(const Position& p) const; + bool contains(const Vertex& v) const { return contains(position(v)); } + + bool boundary(const Position& p, bool degenerate = false) const; + bool boundary(const Vertex& v, bool deg = false) const { return boundary(position(v), deg); } + Box side(unsigned axis, bool upper) const; + + InternalTest internal_test() const { return InternalTest(*this); } + BoundaryTest boundary_test() const { return BoundaryTest(*this); } + BoundsTest bounds_test() const { return BoundsTest(*this); } + PositionToVertex position_to_vertex() const { return PositionToVertex(*this); } + + + void swap(Box& other) { g_.swap(other.g_); std::swap(from_, other.from_); std::swap(to_, other.to_); } + + bool operator==(const Box& other) const { return from_ == other.from_ && to_ == other.to_; } + + template + friend std::basic_ostream& + operator<<(std::basic_ostream& out, const Box& b) { out << "Box: " << b.from_ << " - " << b.to_ << " inside " << b.g_.shape(); return out; } + + struct InternalTest + { + InternalTest(const Box& box): box_(box) {} + bool operator()(const Vertex& v) const { return !box_.boundary(v); } + const Box& box_; + }; + + struct BoundaryTest + { + BoundaryTest(const Box& box): box_(box) {} + bool operator()(const Vertex& v) const { return box_.boundary(v); } + const Box& box_; + }; + + struct BoundsTest + { + BoundsTest(const Box& box): box_(box) {} + bool operator()(const Position& p) const { return box_.contains(p); } + bool operator()(const Vertex& v) const { return box_.contains(v); } + const Box& box_; + }; + + struct PositionToVertex + { + typedef Vertex result_type; + PositionToVertex(const Box& box): box_(box) {} + Vertex operator()(Position p) const { for (unsigned i = 0; i < D; ++i) p[i] %= box_.grid_shape()[i]; return box_.g_.index(p); } + const Box& box_; + }; + + // computes position inside the box (adjusted for the wrap-around, if need be) + Position position(const Vertex& v) const { Position p = g_.vertex(v); for (unsigned i = 0; i < D; ++i) if (p[i] < from()[i]) p[i] += grid_shape()[i]; return p; } + + private: + GridProxy g_; + Position from_, to_; +}; + +} + +#include "box.hpp" + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.hpp new file mode 100644 index 0000000000..f3af50c047 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.hpp @@ -0,0 +1,141 @@ +template +grid::Box +grid::Box:: +intersect(const Box& other) const +{ + Position from, to; + for (unsigned i = 0; i < D; ++i) + { + from[i] = std::max(from_[i], other.from_[i]); + to[i] = std::min(to_[i], other.to_[i]); + } + + return Box(g_, from, to); +} + +template +bool +grid::Box:: +intersects(const Box& other) const +{ + for (unsigned i = 0; i < D; ++i) + if (std::max(from_[i], other.from_[i]) > std::min(to_[i], other.to_[i])) + return false; + + return true; +} + +template +bool +grid::Box:: +contains(const Position& p) const +{ + for (unsigned i = 0; i < D; ++i) + if (p[i] > to_[i] || p[i] < from_[i]) + return false; + return true; +} + +template +bool +grid::Box:: +boundary(const Position& p, bool degenerate) const +{ + for (unsigned i = 0; i < D; ++i) + { + if (degenerate && from_[i] == to_[i]) continue; + if (p[i] == from_[i] || p[i] == to_[i]) + return true; + } + + return false; +} + +template +grid::Box +grid::Box:: +side(unsigned axis, bool upper) const +{ + Box res(*this); + + if (upper) + res.from()[axis] = res.to()[axis]; + else + res.to()[axis] = res.from()[axis]; + + return res; +} + +template +void +grid::Box:: +merge(const Box& other) +{ + for (unsigned i = 0; i < D; ++i) + { + from_[i] = std::min(from_[i], other.from_[i]); + to_[i] = std::max(to_[i], other.to_[i]); + } +} + +/* Box::FreudenthalLinkIterator */ +template +class grid::Box::FreudenthalLinkIterator: + public boost::iterator_facade +{ + typedef boost::iterator_facade Parent; + + + public: + typedef typename Parent::value_type value_type; + typedef typename Parent::difference_type difference_type; + typedef typename Parent::reference reference; + + FreudenthalLinkIterator(): loc_(0), dir_(0) {} + FreudenthalLinkIterator(const Position& p, int loc = 0, int dir = 1): + p_(p), v_(p), loc_(loc), dir_(dir) {} + + static FreudenthalLinkIterator + begin(const Position& p) { FreudenthalLinkIterator it(p); ++it; return it; } + static FreudenthalLinkIterator + end(const Position& p) { return FreudenthalLinkIterator(p, 0, -1); } + + private: + void increment(); + bool equal(const FreudenthalLinkIterator& other) const { return v_ == other.v_; } + reference dereference() const { return v_; } + + friend class ::boost::iterator_core_access; + + private: + Position p_, v_; + int loc_; + int dir_; +}; + +template +void +grid::Box::FreudenthalLinkIterator:: +increment() +{ + loc_ += dir_; + if (loc_ == (1 << D)) + { + dir_ = -1; + loc_ += dir_; + } + + for (unsigned i = 0; i < D; ++i) + if (loc_ & (1 << i)) + v_[i] = p_[i] + dir_; + else + v_[i] = p_[i]; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/grid.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/grid.h new file mode 100644 index 0000000000..c63fc7c598 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/grid.h @@ -0,0 +1,143 @@ +#ifndef GRID_H +#define GRID_H + +#include "point.h" + +namespace grid +{ + +template +struct Grid; + +template +struct GridRef +{ + public: + typedef C Value; + + typedef Point Vertex; + typedef size_t Index; + + public: + template + GridRef(C* data, const Point& shape, bool c_order = true): + data_(data), shape_(shape), c_order_(c_order) { set_stride(); } + + GridRef(Grid& g): + data_(g.data()), shape_(g.shape()), + c_order_(g.c_order()) { set_stride(); } + + template + C operator()(const Point& v) const { return data_[v*stride_]; } + + template + C& operator()(const Point& v) { return data_[v*stride_]; } + + C operator()(Index i) const { return data_[i]; } + C& operator()(Index i) { return data_[i]; } + + const Vertex& + shape() const { return shape_; } + + const C* + data() const { return data_; } + C* data() { return data_; } + + // Set every element to the given value + GridRef& operator=(C value) { Index s = size(); for (Index i = 0; i < s; ++i) data_[i] = value; return *this; } + GridRef& operator/=(C value) { Index s = size(); for (Index i = 0; i < s; ++i) data_[i] /= value; return *this; } + + Vertex vertex(Index idx) const { Vertex v; for (unsigned i = 0; i < D; ++i) { v[i] = idx / stride_[i]; idx %= stride_[i]; } return v; } + Index index(const Vertex& v) const { return v*stride_; } + + Index size() const { return size(shape()); } + void swap(GridRef& other) { std::swap(data_, other.data_); std::swap(shape_, other.shape_); std::swap(stride_, other.stride_); } + + bool c_order() const { return c_order_; } + + protected: + static Index + size(const Vertex& v) { Index res = 1; for (unsigned i = 0; i < D; ++i) res *= v[i]; return res; } + + void set_stride() + { + Index cur = 1; + if (c_order_) + for (unsigned i = D; i > 0; --i) { stride_[i-1] = cur; cur *= shape_[i-1]; } + else + for (unsigned i = 0; i < D; ++i) { stride_[i] = cur; cur *= shape_[i]; } + + } + void set_shape(const Vertex& v) { shape_ = v; set_stride(); } + void set_data(C* data) { data_ = data; } + void set_c_order(bool order) { c_order_ = order; } + + private: + C* data_; + Vertex shape_; + Vertex stride_; + bool c_order_; +}; + + +template +struct Grid: public GridRef +{ + public: + typedef GridRef Parent; + typedef typename Parent::Value Value; + typedef typename Parent::Index Index; + typedef Parent Reference; + + template + struct rebind { typedef Grid type; }; + + public: + template + Grid(const Point& shape, bool c_order = true): + Parent(new C[size(shape)], shape, c_order) + {} + + Grid(const Parent& g): + Parent(new C[size(g.shape())], g.shape(), + g.c_order()) { copy_data(g.data()); } + + template + Grid(const OtherGrid& g): + Parent(new C[size(g.shape())], + g.shape(), + g.c_order()) { copy_data(g.data()); } + + ~Grid() { delete[] Parent::data(); } + + template + Grid& operator=(const GridRef& other) + { + delete[] Parent::data(); + Parent::set_c_order(other.c_order()); // NB: order needs to be set before the shape, to set the stride correctly + Parent::set_shape(other.shape()); + Index s = size(shape()); + Parent::set_data(new C[s]); + copy_data(other.data()); + return *this; + } + + using Parent::data; + using Parent::shape; + using Parent::operator(); + using Parent::operator=; + using Parent::size; + + private: + template + void copy_data(const OC* data) + { + Index s = size(shape()); + for (Index i = 0; i < s; ++i) + Parent::data()[i] = data[i]; + } +}; + +} + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/point.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/point.h new file mode 100644 index 0000000000..0e867c34aa --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/point.h @@ -0,0 +1,132 @@ +#ifndef POINT_H +#define POINT_H + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace grid +{ + +template +class Point: public boost::array, + private boost::addable< Point // Point + Point + , boost::subtractable< Point // Point - Point + , boost::dividable2< Point, Coordinate_ // Point / Coordinate + , boost::multipliable2< Point, Coordinate_ // Point * Coordinate, Coordinate * Point + > > > > +{ + public: + typedef Coordinate_ Coordinate; + typedef boost::array ArrayParent; + + typedef Point LPoint; + typedef Point UPoint; + + template + struct rebind { typedef Point type; }; + + public: + Point() { for (unsigned i = 0; i < D; ++i) (*this)[i] = 0; } + Point(const ArrayParent& a): + ArrayParent(a) {} + template Point(const Point& p) { for (size_t i = 0; i < D; ++i) (*this)[i] = p[i]; } + template Point(const T* a) { for (unsigned i = 0; i < D; ++i) (*this)[i] = a[i]; } + template Point(const std::vector& a) { for (unsigned i = 0; i < D; ++i) (*this)[i] = a[i]; } + + static + unsigned dimension() { return D; } + + static Point zero() { return Point(); } + static Point one() { Point p; for (unsigned i = 0; i < D; ++i) p[i] = 1; return p; } + + LPoint drop(int dim) const { LPoint p; unsigned c = 0; for (unsigned i = 0; i < D; ++i) { if (i == dim) continue; p[c++] = (*this)[i]; } return p; } + UPoint lift(int dim, Coordinate x) const { UPoint p; for (unsigned i = 0; i < D+1; ++i) { if (i < dim) p[i] = (*this)[i]; else if (i == dim) p[i] = x; else if (i > dim) p[i] = (*this)[i-1]; } return p; } + + using ArrayParent::operator[]; + + Point& operator+=(const Point& y) { for (unsigned i = 0; i < D; ++i) (*this)[i] += y[i]; return *this; } + Point& operator-=(const Point& y) { for (unsigned i = 0; i < D; ++i) (*this)[i] -= y[i]; return *this; } + Point& operator*=(Coordinate a) { for (unsigned i = 0; i < D; ++i) (*this)[i] *= a; return *this; } + Point& operator/=(Coordinate a) { for (unsigned i = 0; i < D; ++i) (*this)[i] /= a; return *this; } + + Point operator-() const { Point res; for (unsigned i = 0; i < D; ++i) res[i] = -(*this)[i]; return res; } + + Coordinate norm() const { return (*this)*(*this); } + + std::ostream& operator<<(std::ostream& out) const { out << (*this)[0]; for (unsigned i = 1; i < D; ++i) out << " " << (*this)[i]; return out; } + std::istream& operator>>(std::istream& in); + + friend + Coordinate operator*(const Point& x, const Point& y) { Coordinate n = 0; for (size_t i = 0; i < D; ++i) n += x[i] * y[i]; return n; } + + template + friend + Coordinate operator*(const Point& x, const Point& y) { Coordinate n = 0; for (size_t i = 0; i < D; ++i) n += x[i] * y[i]; return n; } + + private: + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { ar & boost::serialization::base_object(*this); } +}; + +template +std::istream& +Point:: +operator>>(std::istream& in) +{ + std::string point_str; + in >> point_str; // read until ' ' + std::stringstream ps(point_str); + + char x; + for (unsigned i = 0; i < dimension(); ++i) + { + ps >> (*this)[i]; + ps >> x; + } + + return in; +} + + +template +Coordinate norm2(const Point& p) +{ Coordinate res = 0; for (unsigned i = 0; i < D; ++i) res += p[i]*p[i]; return res; } + +template +std::ostream& +operator<<(std::ostream& out, const Point& p) +{ return p.operator<<(out); } + +template +std::istream& +operator>>(std::istream& in, Point& p) +{ return p.operator>>(in); } + +} + +namespace opts +{ + template + struct Traits; + + template + struct Traits< grid::Point > + { + static + std::string type_string() { return "POINT"; } + }; +} + + +#endif // POINT_H diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/vertices.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/vertices.h new file mode 100644 index 0000000000..339782e8c4 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/vertices.h @@ -0,0 +1,86 @@ +#ifndef VERTICES_H +#define VERTICES_H + +#include + +namespace grid +{ + +template +class VerticesIterator: + public boost::iterator_facade, + Vertex_, + boost::forward_traversal_tag, + Vertex_, + std::ptrdiff_t> +{ + typedef boost::iterator_facade Parent; + + + public: + typedef typename Parent::value_type value_type; + typedef typename Parent::difference_type difference_type; + typedef typename Parent::reference reference; + + typedef value_type Vertex; + typedef typename Vertex::Coordinate Coordinate; + + // upper bounds are non-inclusive + VerticesIterator(const Vertex& bounds): + to_(bounds - Vertex::one()) {} + + VerticesIterator(const Vertex& pos, + const Vertex& bounds): + pos_(pos), to_(bounds - Vertex::one()) {} + + VerticesIterator(const Vertex& pos, + const Vertex& from, + const Vertex& to): + pos_(pos), from_(from), + to_(to) {} + + + static VerticesIterator + begin(const Vertex& bounds) { return VerticesIterator(bounds); } + static VerticesIterator + end(const Vertex& bounds) { Vertex e; e[0] = bounds[0]; return VerticesIterator(e, bounds); } + + static VerticesIterator + begin(const Vertex& from, const Vertex& to) { return VerticesIterator(from, from, to); } + static VerticesIterator + end(const Vertex& from, const Vertex& to) { Vertex e = from; e[0] = to[0] + 1; return VerticesIterator(e, from, to); } + + private: + void increment(); + bool equal(const VerticesIterator& other) const { return pos_ == other.pos_; } + reference dereference() const { return pos_; } + + friend class ::boost::iterator_core_access; + + private: + Vertex pos_; + Vertex from_; + Vertex to_; +}; + +} + +template +void +grid::VerticesIterator:: +increment() +{ + unsigned j = Vertex::dimension() - 1; + while (j > 0 && pos_[j] == to_[j]) + { + pos_[j] = from_[j]; + --j; + } + ++pos_[j]; +} + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/matrix-filtration.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/matrix-filtration.h new file mode 100644 index 0000000000..516f27b2d1 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/matrix-filtration.h @@ -0,0 +1,120 @@ +#pragma once + +#include +#include +#include + +namespace dionysus +{ + + +template +class MatrixFiltrationCell; + + +// adapt Matrix as a Filtration to make it possible to feed into reduction algorithms +template +class MatrixFiltration +{ + public: + using Matrix = Matrix_; + using CellValue = CellValue_; + using Dimensions = std::vector; + using Values = std::vector; + using Cell = MatrixFiltrationCell; + + + public: + MatrixFiltration(Matrix m, Dimensions dimensions, Values values): + m_(std::move(m)), + dimensions_(dimensions), + values_(values) { assert(m_->size() == dimensions_.size()); assert(m_->size() == values_.size()); } + + Cell operator[](size_t i) const { return Cell(this, i); } + size_t size() const { return m_.size(); } + + size_t index(const Cell& c) const; + + Cell begin() const { return Cell(this, 0); } + Cell end() const { return Cell(this, size()); } + + const Dimensions& dimensions() const { return dimensions_; } + const Values& values() const { return values_; } + + private: + Matrix m_; + Dimensions dimensions_; + Values values_; + + friend class MatrixFiltrationCell; +}; + + +template +class MatrixFiltrationCell +{ + public: + using MatrixFiltration = MatrixFiltration_; + using Matrix = typename MatrixFiltration::Matrix; + using Data = typename MatrixFiltration::CellValue; + using Field = typename Matrix::Field; + + template + using Entry = ChainEntry; + + template + using BoundaryChain = std::vector>; + + public: + MatrixFiltrationCell(const MatrixFiltration* mf, size_t i): + mf_(mf), i_(i) {} + + short unsigned dimension() const { return mf_->dimensions_[i_]; } + const Data& data() const { return mf_->values_[i_]; } + + bool operator==(const MatrixFiltrationCell& other) const { return i_ == other.i_; } + bool operator!=(const MatrixFiltrationCell& other) const { return i_ != other.i_; } + + BoundaryChain<> boundary() const + { + BoundaryChain<> bdry; + for (auto& entry : (mf_->m_)[i_]) + bdry.emplace_back(Entry<> { entry.e, MatrixFiltrationCell(mf_, entry.i) }); + return bdry; + } + + template + BoundaryChain boundary(const Field_& field) const + { + BoundaryChain bdry; + for (auto& entry : (mf_->m_)[i_]) + bdry.emplace_back(Entry { field.init(entry.e), MatrixFiltrationCell(mf_, entry.i) }); + return bdry; + } + + // iterator interface + MatrixFiltrationCell operator++(int) { MatrixFiltrationCell copy = *this; i_++; return copy; } + MatrixFiltrationCell& operator++() { ++i_; return *this; } + + const MatrixFiltrationCell& operator*() const { return *this; } + MatrixFiltrationCell& operator*() { return *this; } + + size_t i() const { return i_; } + + friend + std::ostream& operator<<(std::ostream& out, const MatrixFiltrationCell& c) + { out << c.i_; return out; } + + private: + const MatrixFiltration* mf_ = nullptr; + size_t i_; +}; + +template +size_t +MatrixFiltration::index(const Cell& c) const +{ + return c.i(); +} + +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.h new file mode 100644 index 0000000000..3d83d4ae44 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.h @@ -0,0 +1,145 @@ +#ifndef DIONYSUS_OMNI_FIELD_REDUCTION_H +#define DIONYSUS_OMNI_FIELD_REDUCTION_H + +#include +#include + +#include "reduction.h" // for unpaired +#include "fields/q.h" +#include "fields/zp.h" +#include "chain.h" + +namespace dionysus +{ + +template, class Q_ = ::dionysus::Q<>, class Zp_ = ::dionysus::ZpField> +class OmniFieldPersistence +{ + public: + using Index = Index_; + using Q = Q_; + using Field = Q; + using Comparison = Comparison_; + + using BaseElement = typename Q::BaseElement; + using Zp = Zp_; + using Zps = std::unordered_map; + + using QElement = typename Q::Element; + using QEntry = ChainEntry; + using QChain = std::vector; + + using ZpElement = typename Zp::Element; + using ZpEntry = ChainEntry; + using ZpChain = std::vector; + + using QChains = std::vector; + using ZpChains = std::unordered_map>; + + using QLows = std::unordered_map; + using ZpLows = std::unordered_map>; + + using QPairs = std::vector; + using ZpPairs = std::unordered_map>; + + using Factors = std::vector; + + using Specials = std::unordered_map>; + + const Field& field() const { return q_; } + + void sort(QChain& c) { std::sort(c.begin(), c.end(), + [this](const QEntry& e1, const QEntry& e2) + { return this->cmp_(e1.index(), e2.index()); }); } + + template + void add(const ChainRange& chain) { return add(QChain(std::begin(chain), std::end(chain))); } + void add(QChain&& chain); + + void reserve(size_t s) { q_chains_.reserve(s); q_pairs_.reserve(s); } + size_t size() const { return q_pairs_.size(); } + + void reduce(ZpChain& zp_chain, BaseElement p); + ZpChain convert(const QChain& c, const Zp& field) const; + bool special(Index i, BaseElement p) const { auto it = zp_chains_.find(i); if (it == zp_chains_.end()) return false; if (it->second.find(p) == it->second.end()) return false; return true; } + Specials specials() const + { + Specials specials; + for (auto& x : zp_chains_) + for (auto& y : x.second) + specials[x.first].push_back(y.first); + return specials; + } + + const Zp& zp(BaseElement p) const { auto it = zps_.find(p); if (it != zps_.end()) return it->second; return zps_.emplace(p, Zp(p)).first->second; } + + static Factors factor(BaseElement x); + + const QChains& q_chains() const { return q_chains_; } + const ZpChains& zp_chains() const { return zp_chains_; } + + // This is a bit of a hack; it takes advantage of the fact that zp(p) + // generates field on-demand and memoizes them. So there is an entry in + // zps_ only if something special happened over the prime. + Factors primes() const { Factors result; result.reserve(zps_.size()); for (auto& x : zps_) result.push_back(x.first); return result; } + + // TODO: no skip support for now + bool skip(Index) const { return false; } + void add_skip() {} + void set_skip(Index, bool flag = true) {} + + Index pair(Index i, BaseElement p) const; + void set_pair(Index i, Index j); + void set_pair(Index i, Index j, BaseElement p); + static const Index unpaired() { return Reduction::unpaired; } + + private: + QChains q_chains_; + ZpChains zp_chains_; + + QLows q_lows_; + ZpLows zp_lows_; + + QPairs q_pairs_; + ZpPairs zp_pairs_; + + Q q_; + mutable Zps zps_; + + Comparison cmp_; +}; + +// Make OmniFieldPersistence act like a ReducedMatrix (e.g., for the purpose of constructing a persistence diagram) +template +struct PrimeAdapter +{ + using Persistence = OmniFieldPersistence; + using Prime = typename Persistence::BaseElement; + using Index = typename Persistence::Index; + + PrimeAdapter(const Persistence& persistence, Prime p): + persistence_(persistence), p_(p) {} + + bool skip(Index i) const { return persistence_.skip(i); } + + size_t size() const { return persistence_.size(); } + Index pair(Index i) const { return persistence_.pair(i, p_); } + static const Index unpaired() { return Persistence::unpaired(); } + + const Persistence& persistence_; + Prime p_; +}; + +template +PrimeAdapter +prime_adapter(const OmniFieldPersistence& persistence, + typename PrimeAdapter::Prime p) +{ + return PrimeAdapter(persistence, p); +} + +} // dionysus + +#include "omni-field-persistence.hpp" + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.hpp new file mode 100644 index 0000000000..68d5fbede7 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.hpp @@ -0,0 +1,250 @@ +template +void +dionysus::OmniFieldPersistence:: +add(QChain&& chain) +{ + sort(chain); + + q_chains_.emplace_back(std::move(chain)); + q_pairs_.emplace_back(unpaired()); + Index i = q_chains_.size() - 1; + + QChain& c = q_chains_.back(); + + auto reduce = [this,&c,i](BaseElement p) + { + auto zp_chain = convert(c, zp(p)); + + this->reduce(zp_chain, p); + + if (!zp_chain.empty()) + { + auto l = zp_chain.back().index(); + zp_lows_[l].emplace(p,i); + set_pair(l,i,p); + } + + zp_chains_[i].emplace(p, std::move(zp_chain)); // empty chain is still a valid indicator that we don't need to bother with this field + }; + + // reduce + auto entry_cmp = [this](const QEntry& e1, const QEntry& e2) { return this->cmp_(e1.index(), e2.index()); }; + while (!c.empty()) + { + auto& low = c.back(); + + auto e = low.element(); + auto l = low.index(); + assert(!q_.is_zero(e)); + if (e != q_.id()) + { + auto factors = factor(q_.numerator(e)); + for (auto p : factors) + { + if (!special(i, p)) // there is already a dedicated column over p + reduce(p); + } + } + + auto it_zp = zp_lows_.find(l); + if (it_zp != zp_lows_.end()) + for (auto& x : it_zp->second) + { + auto p = x.first; + if (!special(i,p)) + reduce(p); + } + + auto it_q = q_lows_.find(l); + if (it_q != q_lows_.end()) + { + Index j = it_q->second; + + // add the primes from j to i + auto it_zp = zp_chains_.find(j); + if (it_zp != zp_chains_.end()) + for (auto& x : it_zp->second) + { + auto p = x.first; + if (!special(i,p)) + reduce(p); + } + + // reduce over Q + auto j_chain = q_chains_[j]; + auto j_e = j_chain.back().element(); + + auto m = q_.neg(q_.div(e,j_e)); + Chain::addto(c, m, j_chain, q_, entry_cmp); + assert(c.empty() || !q_.is_zero(c.back().element())); + } else + { + q_lows_.emplace(l,i); + set_pair(l,i); + break; + } + } +} + +template +void +dionysus::OmniFieldPersistence:: +reduce(ZpChain& zp_chain, BaseElement p) +{ + auto& field = zp(p); + + auto entry_cmp = [this](const ZpEntry& e1, const ZpEntry& e2) { return this->cmp_(e1.index(), e2.index()); }; + + while (!zp_chain.empty()) + { + auto& low = zp_chain.back(); + auto j = low.index(); + + auto it = zp_lows_.find(j); + if (it != zp_lows_.end()) + { + auto it2 = it->second.find(p); + if (it2 != it->second.end()) + { + const ZpChain& co = zp_chains_[it2->second][p]; + + auto m = field.neg(field.div(low.element(), co.back().element())); + assert(m < p); + Chain::addto(zp_chain, m, co, field, entry_cmp); + continue; + } + } + + auto qit = q_lows_.find(j); + if (qit == q_lows_.end() || special(qit->second, p)) // no valid pivot over Q + return; + + // TODO: this could be optimized (add and convert on the fly) + auto& q_chain = q_chains_[qit->second]; + assert(q_chain.empty() || !q_.is_zero(q_chain.back().element())); + + auto co = convert(q_chain, field); + auto m = field.neg(field.div(low.element(), co.back().element())); + Chain::addto(zp_chain, m, co, field, entry_cmp); + + assert(!zp_chain.empty() || zp_chain.back().index() != j); + } +} + +template +typename dionysus::OmniFieldPersistence::ZpChain +dionysus::OmniFieldPersistence:: +convert(const QChain& c, const Zp& field) const +{ + ZpChain result; + result.reserve(c.size()); + auto p = field.prime(); + for (auto& x : c) + { + auto num = q_.numerator(x.element()) % p; + if (num != 0) + { + while (num < 0) num += p; + auto denom = q_.denominator(x.element()) % p; + while (denom < 0) denom += p; + assert(denom % p != 0); + result.emplace_back(field.div(num, denom), x.index()); + } + } + return result; +} + + +template +typename dionysus::OmniFieldPersistence::Factors +dionysus::OmniFieldPersistence:: +factor(BaseElement x) +{ + if (x < 0) + x = -x; + Factors result; + + if (Q::is_prime(x)) + { + result.push_back(x); + return result; + } + + BaseElement p { 2 }; + while (p*p <= x) + { + if (x % p == 0) + { + result.push_back(p); + do { x /= p; } while (x % p == 0); + if (Q::is_prime(x)) + { + result.push_back(x); + break; + } + } + ++p; + } + if (x > 1) + result.push_back(x); + + return result; +} + +template +typename dionysus::OmniFieldPersistence::Index +dionysus::OmniFieldPersistence:: +pair(Index i, BaseElement p) const +{ + if (p == 1) + return q_pairs_[i]; + else + { + auto it = zp_pairs_.find(p); + if (it == zp_pairs_.end()) + return q_pairs_[i]; + else + { + auto pit = it->second.find(i); + if (pit == it->second.end()) + return q_pairs_[i]; + else + return pit->second; + } + } +} + +template +void +dionysus::OmniFieldPersistence:: +set_pair(Index i, Index j, BaseElement p) +{ + auto& pairs = zp_pairs_[p]; + pairs[i] = j; + pairs[j] = i; +} + +template +void +dionysus::OmniFieldPersistence:: +set_pair(Index i, Index j) +{ + q_pairs_[i] = j; + q_pairs_[j] = i; + + auto it = zp_chains_.find(j); + if (it == zp_chains_.end()) + return; + + auto& chains = it->second; + for (auto& x : chains) + { + auto p = x.first; + auto& chain = x.second; + if (chain.empty()) + { + zp_pairs_[p][j] = unpaired(); + zp_pairs_[p][i] = unpaired(); + } + } +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/opts/opts.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/opts/opts.h new file mode 100644 index 0000000000..1a9bbf71bf --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/opts/opts.h @@ -0,0 +1,499 @@ +/** + * Author: Dmitriy Morozov + * The interface is heavily influenced by GetOptPP (https://code.google.com/p/getoptpp/). + * The parsing logic is from ProgramOptions.hxx (https://github.com/Fytch/ProgramOptions.hxx). + * + * History: + * - 2015-06-01: added Traits<...>::type_string() for long, unsigned long + * - ... + * - 2018-04-27: replace parsing logic with the one from ProgramOptions.hxx to + * make the parser compliant with [GNU Program Argument Syntax + * Conventions](https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html) + * - 2018-05-11: add dashed_non_option(), to accept arguments that are negative numbers + */ + +#ifndef OPTS_OPTS_H +#define OPTS_OPTS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace opts { + +// Converters +template +struct Converter +{ + Converter() {} + static + bool convert(const std::string& val, T& res) + { + std::istringstream iss(val); + iss >> res; + return !iss.fail() && iss.eof(); + } +}; + +// Type +template +struct Traits +{ + static std::string type_string() { return "UNKNOWN TYPE"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "INT"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "SHORT INT"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "LONG"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "UNSIGNED INT"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "SHORT UNSIGNED INT"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "UNSIGNED LONG"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "FLOAT"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "DOUBLE"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "STRING"; } +}; + + +struct BasicOption +{ + using IsShort = std::function; + + BasicOption(char s_, + std::string l_, + std::string default_, + std::string type_, + std::string help_): + s(s_), l(l_), d(default_), t(type_), help(help_) {} + virtual ~BasicOption() {} + + int long_size() const { return l.size() + 1 + t.size(); } + + void output(std::ostream& out, int max_long) const + { + out << " "; + if (s) + out << '-' << s << ", "; + else + out << " "; + + out << "--" << l << ' '; + + if (!t.empty()) + out << t; + + for (int i = long_size(); i < max_long; ++i) + out << ' '; + + out << " " << help; + + if (!d.empty()) + { + out << " [default: " << d << "]"; + } + out << '\n'; + } + + virtual bool flag() const { return false; } + virtual bool parse(int argc, char** argv, int& i, int j, IsShort is_short); + virtual bool set(std::string arg) =0; + + char s; + std::string l; + std::string d; + std::string t; + std::string help; +}; + +// Option +template +struct OptionContainer: public BasicOption +{ + OptionContainer(char s_, + const std::string& l_, + T& var_, + const std::string& help_, + const std::string& type_ = Traits::type_string()): + BasicOption(s_, l_, default_value(var_), type_, help_), + var(&var_) {} + + static + std::string default_value(const T& def) + { + std::ostringstream oss; + oss << def; + return oss.str(); + } + + bool set(std::string s) override { return Converter::convert(s, *var); } + + T* var; +}; + +template<> +struct OptionContainer: public BasicOption +{ + OptionContainer(char s_, + const std::string& l_, + bool& var_, + const std::string& help_): + BasicOption(s_, l_, "", "", help_), + var(&var_) { *var = false; } + + bool parse(int, char**, int&, int, IsShort) override { *var = true; return true; } + bool set(std::string) override { return true; } + bool flag() const override { return true; } + + bool* var; +}; + +template +struct OptionContainer< std::vector >: public BasicOption +{ + OptionContainer(char s_, + const std::string& l_, + std::vector& var_, + const std::string& help_, + const std::string& type_ = "SEQUENCE"): + BasicOption(s_, l_, default_value(var_), type_, help_), + var(&var_), first(true) { } + + static + std::string default_value(const std::vector& def) + { + std::ostringstream oss; + oss << "("; + if (def.size()) + oss << def[0]; + for (size_t i = 1; i < def.size(); ++i) + oss << ", " << def[i]; + oss << ")"; + return oss.str(); + } + + bool set(std::string s) override + { + if (first) + { + var->clear(); + first = false; + } + + T x; + bool result = Converter::convert(s,x); + var->emplace_back(std::move(x)); + return result; + } + + std::vector* var; + mutable bool first; +}; + + +template +std::unique_ptr +Option(char s, const std::string& l, T& var, const std::string& help) { return std::unique_ptr{new OptionContainer(s, l, var, help)}; } + +template +std::unique_ptr +Option(char s, const std::string& l, T& var, + const std::string& type, const std::string& help) { return std::unique_ptr{new OptionContainer(s, l, var, help, type)}; } + +template +std::unique_ptr +Option(const std::string& l, T& var, const std::string& help) { return std::unique_ptr{new OptionContainer(0, l, var, help)}; } + +template +std::unique_ptr +Option(const std::string& l, T& var, + const std::string& type, const std::string& help) { return std::unique_ptr{new OptionContainer(0, l, var, help, type)}; } + +// PosOption +template +struct PosOptionContainer +{ + PosOptionContainer(T& var_): + var(&var_) {} + + bool parse(std::list& args) const + { + if (args.empty()) + return false; + + bool result = Converter::convert(args.front(), *var); + if (!result) + std::cerr << "error: failed to parse " << args.front() << '\n'; + args.pop_front(); + return result; + } + + T* var; +}; + +template +PosOptionContainer +PosOption(T& var) { return PosOptionContainer(var); } + + +// Options +struct Options +{ + Options(): + failed(false) {} + + inline + Options& operator>>(std::unique_ptr opt); + template + Options& operator>>(const PosOptionContainer& poc); + + operator bool() { return !failed; } + + + friend + std::ostream& + operator<<(std::ostream& out, const Options& ops) + { + int max_long = 0; + for (auto& cur : ops.options) + { + int cur_long = cur->long_size(); + if (cur_long > max_long) + max_long = cur_long; + } + + out << "Options:\n"; + for (auto& cur : ops.options) + cur->output(out, max_long); + + return out; + } + + bool parse(int argc, char** argv); + + void unrecognized_option(std::string arg) const + { + std::cerr << "error: unrecognized option " << arg << '\n'; + } + + static bool dashed_non_option(char* arg, BasicOption::IsShort is_short) + { + return arg[ 0 ] == '-' + && (std::isdigit(arg[ 1 ]) || arg[ 1 ] == '.') + && !is_short(arg[ 1 ]); + } + + private: + std::list args; + std::list> options; + bool failed; +}; + +bool +BasicOption::parse(int argc, char** argv, int& i, int j, IsShort is_short) +{ + char* argument; + char* cur_arg = argv[i]; + // -v... + if (argv[i][j] == '\0') + { + // -v data + if (i + 1 < argc && (argv[i+1][0] != '-' || Options::dashed_non_option(argv[i+1], is_short))) + { + ++i; + argument = argv[i]; + } else + { + std::cerr << "error: cannot find the argument; ignoring " << argv[i] << '\n'; + return false; + } + } else if (argv[i][j] == '=') + { + // -v=data + argument = &argv[i][j+1]; + } else if( j == 2 ) { // only for short options + // -vdata + argument = &argv[i][j]; + } else + { + std::cerr << "error: unexpected character \'" << argv[i][j] << "\' ignoring " << argv[i] << '\n'; + return false; + } + bool result = set(argument); + if (!result) + std::cerr << "error: failed to parse " << argument << " in " << cur_arg << '\n'; + return result; +} + +bool +Options::parse(int argc, char** argv) +{ + std::map short_opts; + std::map long_opts; + + for (auto& opt : options) + { + if (opt->s) + short_opts[opt->s] = opt.get(); + + long_opts[opt->l] = opt.get(); + } + + auto is_short = [&short_opts](char c) -> bool { return short_opts.find(c) != short_opts.end(); }; + + for (int i = 1; i < argc; ++i) + { + if( argv[ i ][ 0 ] == '\0' ) + continue; + if( argv[ i ][ 0 ] != '-' || dashed_non_option(argv[i], is_short)) + args.push_back(argv[i]); + else + { + // -... + if( argv[ i ][ 1 ] == '\0' ) + { + // - + args.push_back(argv[i]); + } else if( argv[ i ][ 1 ] == '-' ) + { + if( argv[ i ][ 2 ] == '\0' ) + { + // -- + while( ++i < argc ) + args.push_back(argv[i]); + } else { + // --... + char* first = &argv[ i ][ 2 ]; + char* last = first; + for(; *last != '=' && *last != '\0'; ++last); + if (first == last) + { + failed = true; + unrecognized_option(argv[i]); + } else + { + auto opt_it = long_opts.find(std::string{first,last}); + if (opt_it == long_opts.end()) + { + failed = true; + unrecognized_option(argv[i]); + } else + { + failed |= !opt_it->second->parse(argc, argv, i, last - argv[i], is_short); + } + } + } + } else + { + // -f... + auto opt_it = short_opts.find(argv[i][1]); + if (opt_it == short_opts.end()) + { + failed = true; + unrecognized_option(argv[i]); + } else if (opt_it->second->flag()) + { + opt_it->second->parse(argc, argv, i, 0, is_short); // arguments are meaningless; just sets the flag + + // -fgh + char c; + for(int j = 1; (c = argv[i][j]) != '\0'; ++j) + { + if (!std::isprint(c) || c == '-') + { + failed = true; + std::cerr << "error: invalid character\'" << c << " ignoring " << &argv[i][j] << '\n'; + break; + } + opt_it = short_opts.find(c); + if (opt_it == short_opts.end()) + { + failed = true; + unrecognized_option("-" + std::string(1, c)); + continue; + } + if (!opt_it->second->flag()) + { + failed = true; + std::cerr << "error: non-void options not allowed in option packs; ignoring " << c << '\n'; + continue; + } + opt_it->second->parse(argc, argv, i, 0, is_short); // arguments are meaningless; just sets the flag + } + } else + { + failed |= !opt_it->second->parse(argc, argv, i, 2, is_short); + } + } + } + } + + return !failed; +} + +Options& +Options::operator>>(std::unique_ptr opt) +{ + options.emplace_back(std::move(opt)); + return *this; +} + +template +Options& +Options::operator>>(const PosOptionContainer& poc) +{ + if (!failed) + failed = !poc.parse(args); + return *this; +} + +} + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/ordinary-persistence.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/ordinary-persistence.h new file mode 100644 index 0000000000..5f26bd2a1b --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/ordinary-persistence.h @@ -0,0 +1,64 @@ +#ifndef DIONYSUS_ORDINARY_PERSISTENCE_H +#define DIONYSUS_ORDINARY_PERSISTENCE_H + +#include "reduced-matrix.h" + +namespace dionysus +{ + +/* Move this into a ReducedMatrix class */ + +// Ordinary D -> R reduction +template, + template class... Visitors> +using OrdinaryPersistence = ReducedMatrix; + +// No negative optimization +template> +struct NoNegative +{ + template + struct Visitor: public EmptyVisitor + { + template + void chain_initialized(Self* matrix, Chain& c) + { + for (auto cur = std::begin(c); cur != std::end(c); ++cur) + { + Index i = cur->index(); + Index p = matrix->pair(i); + if (!(p == Self::unpaired() || (*matrix)[i].empty())) + c.erase(cur--); + } + } + }; + + template + using V2 = EmptyVisitor; +}; + +template, + template class... Visitors> +using OrdinaryPersistenceNoNegative = ReducedMatrix::template Visitor, + Visitors...>; + +// TODO: add clearing optimization (possibly bake it into the code itself) + +template, + template class... Visitors> +using FastPersistence = ReducedMatrix::template Visitor, + //Clearing::template Visitor, // FIXME + Visitors...>; + + +} + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/pair-recorder.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/pair-recorder.h new file mode 100644 index 0000000000..81c066bda6 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/pair-recorder.h @@ -0,0 +1,78 @@ +#ifndef DIONYSUS_PAIR_RECORDER_H +#define DIONYSUS_PAIR_RECORDER_H + +namespace dionysus +{ + +template +struct PairRecorder: public Persistence_ +{ + typedef Persistence_ Persistence; + typedef typename Persistence::Index Index; + + + using Persistence::Persistence; + + template + Index add(const ChainRange& chain) + { + Index p = Persistence::add(chain); + pairs_.push_back(p); + if (p != unpaired()) + pairs_[p] = pairs_.size() - 1; + + return p; + } + + Index pair(Index i) const { return pairs_[i]; } + + void resize(size_t s) { Persistence::resize(s); pairs_.resize(s, unpaired()); } + size_t size() const { return pairs_.size(); } + static const Index unpaired() { return Reduction::unpaired; } + + std::vector pairs_; +}; + +template +struct PairChainRecorder: public PairRecorder +{ + using Persistence = Persistence_; + using Parent = PairRecorder; + using Index = typename Persistence_::Index; + using Chain = typename Persistence_::Chain; + + using Parent::Parent; + + template + Index add(const ChainRange& chain) + { + auto p_chain = Persistence::add(chain, keep_cocycles); + Index p = std::get<0>(p_chain); + + pairs_.push_back(p); + chains_.emplace_back(); + + if (p != unpaired()) + { + pairs_[p] = pairs_.size() - 1; + chains_[p] = std::move(std::get<1>(p_chain)); + } + + return p; + } + + using Parent::unpaired; + + Index pair(Index i) const { return pairs_[i]; } + const Chain& chain(Index i) const { return chains_[i]; } // chain that dies at i + void resize(size_t s) { Parent::resize(s); chains_.resize(s); } + + std::vector chains_; + using Parent::pairs_; + + bool keep_cocycles = true; +}; + +} + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.h new file mode 100644 index 0000000000..f7a04b130d --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.h @@ -0,0 +1,170 @@ +#ifndef DIONYSUS_REDUCED_MATRIX_H +#define DIONYSUS_REDUCED_MATRIX_H + +#include +#include + +#include "chain.h" +#include "reduction.h" + +namespace dionysus +{ + +template, template class... Visitors> +class ReducedMatrix +{ + public: + typedef ReducedMatrix Self; + + typedef Field_ Field; + typedef Index_ Index; + typedef Comparison_ Comparison; + + typedef std::tuple...> VisitorsTuple; + template + using Visitor = std::tuple_element; + + typedef typename Field::Element FieldElement; + typedef ChainEntry Entry; + typedef std::vector Chain; + + typedef std::vector Chains; + typedef std::vector Indices; + typedef std::vector SkipFlags; + + public: + ReducedMatrix(const Field& field): + field_(field) {} + + ReducedMatrix(const Field& field, + const Comparison& cmp, + const Visitors&... visitors): + field_(field), + cmp_(cmp), + visitors_(visitors...) {} + + ReducedMatrix(Field&& field, + Comparison&& cmp, + Visitors&&... visitors): + field_(std::move(field)), + cmp_(std::move(cmp)), + visitors_(visitors...) {} + + ReducedMatrix(Self&& m) = default; + ReducedMatrix(const Self& m) = default; + + template class... OtherVisitors> + ReducedMatrix(ReducedMatrix&& other): + field_(other.field_), + cmp_(other.cmp_), + reduced_(std::move(other.reduced_)), + pairs_(std::move(other.pairs_)), + skip_(std::move(other.skip_)) {} + + template + Index add(const ChainRange& chain) { return add(Chain(std::begin(chain), std::end(chain))); } + Index add(Chain&& chain); + + template + void set(Index i, const ChainRange& chain) { return set(i, Chain(std::begin(chain), std::end(chain))); } + void set(Index i, Chain&& chain); + + Index reduce(Index i); + Index reduce(Chain& c) { return reduce(c, reduced_, pairs_); } + template + Index reduce(Chain& c, const ChainsLookup& chains, const LowLookup& low); + + Index reduce_upto(Index i); // TODO + + size_t size() const { return pairs_.size(); } + void clear() { Chains().swap(reduced_); Indices().swap(pairs_); } + + void sort(Chain& c) { std::sort(c.begin(), c.end(), [this](const Entry& e1, const Entry& e2) { return this->cmp_(e1.index(), e2.index()); }); } + + const Chain& operator[](Index i) const { return reduced_[i]; } + Index pair(Index i) const { return pairs_[i]; } + void set_pair(Index i, Index j) { pairs_[i] = j; pairs_[j] = i; } + + Chain& column(Index i) { return reduced_[i]; } + + bool skip(Index i) const { return skip_[i]; } + void add_skip(); + void set_skip(Index i, bool flag = true) { skip_[i] = flag; } + + const Field& field() const { return field_; } + const Comparison& cmp() const { return cmp_; } + void reserve(size_t s) { reduced_.reserve(s); pairs_.reserve(s); } + void resize(size_t s); + + const Chains& columns() const { return reduced_; } + + template + Visitor& visitor() { return std::get(visitors_); } + + static const Index unpaired() { return Reduction::unpaired; } + + private: + template class... Vs> + friend class ReducedMatrix; // let's all be friends + + public: + // Visitors::chain_initialized(c) + template + typename std::enable_if::type + visitors_chain_initialized(Chain& c) {} + + template + typename std::enable_if::type + visitors_chain_initialized(Chain& c) { std::get(visitors_).chain_initialized(this, c); visitors_chain_initialized(c); } + + // Visitors::addto(m, cl) + template + typename std::enable_if::type + visitors_addto(FieldElement m, Index cl) {} + + template + typename std::enable_if::type + visitors_addto(FieldElement m, Index cl) { std::get(visitors_).addto(this, m, cl); visitors_addto(m, cl); } + + // Visitors::reduction_finished(m, cl) + template + typename std::enable_if::type + visitors_reduction_finished() {} + + template + typename std::enable_if::type + visitors_reduction_finished() { std::get(visitors_).reduction_finished(this); visitors_reduction_finished(); } + + private: + Field field_; + Comparison cmp_; + Chains reduced_; // matrix R + Indices pairs_; + SkipFlags skip_; // indicates whether the column should be skipped (e.g., for relative homology) + VisitorsTuple visitors_; +}; + +/* Visitors */ + +// The prototypical visitor. Others may (and probably should) inherit from it. +template +struct EmptyVisitor +{ + EmptyVisitor() = default; + + template + EmptyVisitor(const EmptyVisitor&) {} + + + template + void chain_initialized(Self*, Chain& c) {} + + void addto(Self*, typename Field::Element m, Index cl) {} + void reduction_finished(Self*) {} +}; + +} + +#include "reduced-matrix.hpp" + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.hpp new file mode 100644 index 0000000000..3e4aca8f29 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.hpp @@ -0,0 +1,78 @@ +template class... V> +void +dionysus::ReducedMatrix:: +resize(size_t s) +{ + reduced_.resize(s); + pairs_.resize(s, unpaired()); + skip_.resize(s, false); +} + +template class... V> +typename dionysus::ReducedMatrix::Index +dionysus::ReducedMatrix:: +add(Chain&& chain) +{ + // TODO: skip the computation entirely if we already know this is positive (in case of the clearing optimization) + Index i = pairs_.size(); + pairs_.emplace_back(unpaired()); + reduced_.emplace_back(); + skip_.push_back(false); + + set(i, std::move(chain)); + + return reduce(i); +} + +template class... V> +void +dionysus::ReducedMatrix:: +add_skip() +{ + pairs_.emplace_back(unpaired()); + reduced_.emplace_back(); + skip_.push_back(true); +} + +template class... V> +void +dionysus::ReducedMatrix:: +set(Index i, Chain&& c) +{ + sort(c); + visitors_chain_initialized(c); + reduced_[i] = std::move(c); +} + +template class... V> +typename dionysus::ReducedMatrix::Index +dionysus::ReducedMatrix:: +reduce(Index i) +{ + Chain& c = column(i); + Index pair = reduce(c); + + if (pair != unpaired()) + pairs_[pair] = i; + + pairs_[i] = pair; + visitors_reduction_finished<>(); + + return pair; +} + +template class... V> +template +typename dionysus::ReducedMatrix::Index +dionysus::ReducedMatrix:: +reduce( Chain& c, + const ChainsLookup& chains, + const LowLookup& lows) +{ + auto entry_cmp = [this](const Entry& e1, const Entry& e2) { return this->cmp_(e1.index(), e2.index()); }; + return Reduction::reduce(c, chains, lows, field_, + [this](FieldElement m, Index cl) + { this->visitors_addto<>(m, cl); }, + entry_cmp); +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduction.h new file mode 100644 index 0000000000..2afd333d41 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduction.h @@ -0,0 +1,109 @@ +#ifndef DIONYSUS_REDUCTION_H +#define DIONYSUS_REDUCTION_H + +#include +#include +#include +#include +#include "chain.h" + +namespace dionysus +{ + +namespace detail +{ + +template +struct Unpaired +{ static constexpr Index value() { return std::numeric_limits::max(); } }; + +} + +template +struct Reduction +{ + typedef Index_ Index; + + template + using AddtoVisitor = std::function; + + template + struct CallToSub; + + static const Index unpaired; + + template> + static + Index reduce(Chain1& c, + const ChainsLookup& chains, + const LowLookup& lows, + const Field& field, + const AddtoVisitor& visitor = [](typename Field::Element, Index) {}, + const Comparison& cmp = Comparison()) + { + typedef typename Field::Element FieldElement; + + while (!c.empty()) + { + //auto& low = c.back(); + auto& low = *(std::prev(c.end())); + Index l = low.index(); + Index cl = lows(l); + // std::cout << "idx: " << std::get<0>(cl) << ", " << std::get<1>(cl) << "\n"; + if (cl == unpaired) + return l; + else + { + // Reduce further + auto& co = chains(cl); + auto& co_low = co.back(); + FieldElement m = field.neg(field.div(low.element(), co_low.element())); + // c += m*co + Chain::addto(c, m, co, field, cmp); + visitor(m, cl); + } + } + return unpaired; + } + + template> + static + Index reduce(Chain1& c, + const std::vector& chains, + const std::vector& lows, + const Field& field, + const AddtoVisitor& visitor = [](typename Field::Element, Index) {}, + const Comparison& cmp = Comparison()) + { + return reduce(c, + CallToSub(chains), + CallToSub(lows), + field, visitor, cmp); + } + + // This is a work-around a bug in GCC (should really be a lambda function) + template + struct CallToSub + { + CallToSub(const std::vector& items_): + items(items_) {} + const Item& operator()(Index i) const { return items[i]; } + const std::vector& items; + }; +}; + + +template +const Index +Reduction::unpaired = detail::Unpaired::value(); + +} + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.h new file mode 100644 index 0000000000..167a32779d --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.h @@ -0,0 +1,84 @@ +#ifndef RELATIVE_HOMOLOGY_ZIGZAG_H +#define RELATIVE_HOMOLOGY_ZIGZAG_H + +#include +#include + +#include "zigzag-persistence.h" + +namespace dionysus +{ + +namespace ba = boost::adaptors; + +template> +class RelativeHomologyZigzag +{ + public: + typedef Field_ Field; + typedef Index_ Index; + typedef Comparison_ Comparison; + + typedef ZigzagPersistence ZZP; + typedef typename ZZP::IndexChain IndexChain; + typedef typename ZZP::FieldElement FieldElement; + typedef typename IndexChain::value_type ChainEntry; + + + typedef Comparison Cmp; + + RelativeHomologyZigzag(const Field& field, + const Comparison& cmp = Comparison()): + zzp_(field, cmp) + { + zzp_.add( IndexChain() ); // vertex w + ++zzp_op_; + ++zzp_cell_; + } + + template + void add_both(const ChainRange& chain); + + void remove_both(Index cell); + + // index of the absolute cell; chain = its boundary + template + Index add(Index cell, const ChainRange& chain); // add to the relative part + + Index remove(Index cell); // remove from the relative part + + const Field& field() const { return zzp_.field(); } + const Cmp& cmp() const { return zzp_.cmp(); } + + size_t alive_size() const { return zzp_.alive_size() - 1; } // -1 for the cone vertex + + static + const Index unpaired() { return ZZP::unpaired(); } + + private: + template + IndexChain relative_chain(Index cell, const ChainRange& chain) const; + + template + IndexChain absolute_chain(const ChainRange& chain) const; + + Index abs_index(Index idx) const { return absolute_.left.find(idx)->second; } + Index rel_index(Index idx) const { return relative_.left.find(idx)->second; } + Index decode_pair(Index pair); + + private: + ZZP zzp_; // underlying (cone) implementation + boost::bimap absolute_; // bimap between our cells and zzp absolute cells + boost::bimap relative_; // bimap between our cells and zzp relative cells + std::unordered_map op_map_; // map from zzp_op to our op + Index op_ = 0, + zzp_op_ = 0, + cell_ = 0, + zzp_cell_ = 0; +}; + +} + +#include "relative-homology-zigzag.hpp" + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.hpp new file mode 100644 index 0000000000..499807106c --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.hpp @@ -0,0 +1,122 @@ +template +template +void +dionysus::RelativeHomologyZigzag:: +add_both(const ChainRange& chain) +{ + zzp_.add(absolute_chain(chain)); + op_map_.insert( { zzp_op_++, op_ } ); + absolute_.left.insert( { cell_, zzp_cell_++ } ); + + zzp_.add(relative_chain(cell_, chain)); + op_map_.insert( { zzp_op_++, op_ } ); + relative_.left.insert( { cell_, zzp_cell_++ } ); + + cell_++; + op_++; +} + +template +void +dionysus::RelativeHomologyZigzag:: +remove_both(Index cell) +{ + Index abs_cell = absolute_.left.find(cell)->second; + Index rel_cell = relative_.left.find(cell)->second; + + zzp_.remove(rel_cell); + zzp_.remove(abs_cell); + + absolute_.left.erase(cell); + relative_.left.erase(cell); + + op_map_.insert( { zzp_op_++, op_ } ); + op_map_.insert( { zzp_op_++, op_ } ); + + op_++; +} + +template +template +typename dionysus::RelativeHomologyZigzag::Index +dionysus::RelativeHomologyZigzag:: +add(Index cell, const ChainRange& chain) +{ + Index pair = zzp_.add(relative_chain(cell, chain)); + op_map_.insert( { zzp_op_++, op_++ } ); + relative_.left.insert( { cell, zzp_cell_++ } ); + + return decode_pair(pair); +} + + +template +typename dionysus::RelativeHomologyZigzag::Index +dionysus::RelativeHomologyZigzag:: +decode_pair(Index pair) +{ + if (pair == unpaired()) + return pair; + + Index decoded = op_map_.find(pair)->second; + op_map_.erase(pair); + return decoded; +} + +template +template +typename dionysus::RelativeHomologyZigzag::IndexChain +dionysus::RelativeHomologyZigzag:: +absolute_chain(const ChainRange& chain) const +{ + IndexChain res; + for (const auto& e : chain) + res.push_back(ChainEntry(e.element(), abs_index(e.index()))); + return res; +} + +template +template +typename dionysus::RelativeHomologyZigzag::IndexChain +dionysus::RelativeHomologyZigzag:: +relative_chain(Index cell, const ChainRange& chain) const +{ + // NB: to compute the signs correctly, + // this assumes that the cone vertex w is the last vertex in some total order + + typedef typename IndexChain::value_type ChainEntry; + + IndexChain res; + if (!chain.empty()) + { + for (const auto& e : chain) + res.push_back(ChainEntry(e.element(), rel_index(e.index()))); + + FieldElement a = field().id(); + if (chain.size() % 2 == 0) // TODO: double-check + a = field().neg(a); + res.push_back(ChainEntry(a, abs_index(cell))); // add the base space cell + } else + { + res.reserve(2); + res.push_back(ChainEntry(field().id(), abs_index(cell))); + res.push_back(ChainEntry(field().neg(field().id()), 0)); + } + return res; +} + + +template +typename dionysus::RelativeHomologyZigzag::Index +dionysus::RelativeHomologyZigzag:: +remove(Index cell) +{ + Index rel_cell = rel_index(cell); + Index pair = zzp_.remove(rel_cell); + pair = decode_pair(pair); + + op_map_.insert( { zzp_op_++, op_++ } ); + relative_.left.erase(cell); + + return pair; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.h new file mode 100644 index 0000000000..c7ccb1189e --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.h @@ -0,0 +1,147 @@ +#ifndef DIONYSUS_RIPS_H +#define DIONYSUS_RIPS_H + +#include +#include + +#include + +#include "simplex.h" + +namespace dionysus +{ + +/** + * Rips class + * + * Class providing basic operations to work with Rips complexes. It implements Bron-Kerbosch algorithm, + * and provides simple wrappers for various functions. + * + * Distances_ is expected to define types IndexType and DistanceType as well as + * provide operator()(...) which given two IndexTypes should return + * the distance between them. There should be methods begin() and end() + * for iterating over IndexTypes as well as a method size(). + */ +template > +class Rips +{ + public: + typedef Distances_ Distances; + typedef typename Distances::IndexType IndexType; + typedef typename Distances::DistanceType DistanceType; + + typedef Simplex_ Simplex; + typedef typename Simplex::Vertex Vertex; // should be the same as IndexType + typedef std::vector VertexContainer; + + typedef short unsigned Dimension; + + class Evaluator; + class Comparison; + + public: + Rips(const Distances& distances): + distances_(distances) {} + + // Calls functor f on each simplex in the k-skeleton of the Rips complex + template + void generate(Dimension k, DistanceType max, const Functor& f, + Iterator candidates_begin, Iterator candidates_end) const; + + // Calls functor f on all the simplices of the Rips complex that contain the given vertex v + template + void vertex_cofaces(IndexType v, Dimension k, DistanceType max, const Functor& f, + Iterator candidates_begin, Iterator candidates_end) const; + + // Calls functor f on all the simplices of the Rips complex that contain the given edge [u,v] + template + void edge_cofaces(IndexType u, IndexType v, Dimension k, DistanceType max, const Functor& f, + Iterator candidates_begin, Iterator candidates_end) const; + + // Calls functor f on all the simplices of the Rips complex that contain the given Simplex s + // (unlike the previous methods it does not call the functor on the Simplex s itself) + template + void cofaces(const Simplex& s, Dimension k, DistanceType max, const Functor& f, + Iterator candidates_begin, Iterator candidates_end) const; + + + /* No Iterator argument means Iterator = IndexType and the range is [distances().begin(), distances().end()) */ + template + void generate(Dimension k, DistanceType max, const Functor& f) const + { generate(k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); } + + template + void vertex_cofaces(IndexType v, Dimension k, DistanceType max, const Functor& f) const + { vertex_cofaces(v, k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); } + + template + void edge_cofaces(IndexType u, IndexType v, Dimension k, DistanceType max, const Functor& f) const + { edge_cofaces(u, v, k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); } + + template + void cofaces(const Simplex& s, Dimension k, DistanceType max, const Functor& f) const + { cofaces(s, k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); } + + + const Distances& distances() const { return distances_; } + DistanceType max_distance() const; + + DistanceType distance(const Simplex& s1, const Simplex& s2) const; + + + template + static void bron_kerbosch(VertexContainer& current, + const VertexContainer& candidates, + typename VertexContainer::const_iterator excluded, + Dimension max_dim, + const NeighborTest& neighbor, + const Functor& functor, + bool check_initial = true); + + protected: + const Distances& distances_; +}; + +template +class Rips::Evaluator +{ + public: + typedef Simplex_ Simplex; + + Evaluator(const Distances& distances): + distances_(distances) {} + + DistanceType operator()(const Simplex& s) const; + + protected: + const Distances& distances_; +}; + +template +class Rips::Comparison +{ + public: + typedef Simplex_ Simplex; + + Comparison(const Distances& distances): + eval_(distances) {} + + bool operator()(const Simplex& s1, const Simplex& s2) const + { + DistanceType e1 = eval_(s1), + e2 = eval_(s2); + if (e1 == e2) + return s1.dimension() < s2.dimension(); + + return e1 < e2; + } + + protected: + Evaluator eval_; +}; + +} + +#include "rips.hpp" + +#endif // DIONYSUS_RIPS_H diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.hpp new file mode 100644 index 0000000000..2fdda34a7a --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.hpp @@ -0,0 +1,162 @@ +#include +#include +#include +#include + +#include +#include + +template +template +void +dionysus::Rips:: +generate(Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const +{ + auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; }; + + // current = empty + // candidates = everything + VertexContainer current; + VertexContainer candidates(bg, end); + bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f); +} + +template +template +void +dionysus::Rips:: +vertex_cofaces(IndexType v, Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const +{ + auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; }; + + // current = [v] + // candidates = everything - [v] + VertexContainer current; current.push_back(v); + VertexContainer candidates; + for (Iterator cur = bg; cur != end; ++cur) + if (*cur != v && neighbor(v, *cur)) + candidates.push_back(*cur); + + bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f); +} + +template +template +void +dionysus::Rips:: +edge_cofaces(IndexType u, IndexType v, Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const +{ + auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; }; + + // current = [u,v] + // candidates = everything - [u,v] + VertexContainer current; current.push_back(u); current.push_back(v); + + VertexContainer candidates; + for (Iterator cur = bg; cur != end; ++cur) + if (*cur != u && *cur != v && neighbor(v,*cur) && neighbor(u,*cur)) + candidates.push_back(*cur); + + bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f); +} + +template +template +void +dionysus::Rips:: +cofaces(const Simplex& s, Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const +{ + namespace ba = boost::adaptors; + + auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; }; + + // current = s + VertexContainer current(s.begin(), s.end()); + + // candidates = everything - s that is a neighbor of every vertex in the simplex + VertexContainer candidates; + boost::set_difference(std::make_pair(bg, end) | + ba::filtered([this,&s,&neighbor](Vertex cur) + { for (auto& v : s) + if (!neighbor(v, cur)) + return false; + }), + s, + std::back_inserter(candidates)); + + bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f, false); +} + + +template +template +void +dionysus::Rips:: +bron_kerbosch(VertexContainer& current, + const VertexContainer& candidates, + typename VertexContainer::const_iterator excluded, + Dimension max_dim, + const NeighborTest& neighbor, + const Functor& functor, + bool check_initial) +{ + if (check_initial && !current.empty()) + functor(Simplex(current)); + + if (current.size() == static_cast(max_dim) + 1) + return; + + for (auto cur = std::next(excluded); cur != candidates.end(); ++cur) + { + current.push_back(*cur); + + VertexContainer new_candidates; + for (auto ccur = candidates.begin(); ccur != cur; ++ccur) + if (neighbor(*ccur, *cur)) + new_candidates.push_back(*ccur); + size_t ex = new_candidates.size(); + for (auto ccur = std::next(cur); ccur != candidates.end(); ++ccur) + if (neighbor(*ccur, *cur)) + new_candidates.push_back(*ccur); + excluded = new_candidates.begin() + (ex - 1); + + bron_kerbosch(current, new_candidates, excluded, max_dim, neighbor, functor); + current.pop_back(); + } +} + +template +typename dionysus::Rips::DistanceType +dionysus::Rips:: +distance(const Simplex& s1, const Simplex& s2) const +{ + DistanceType mx = 0; + for (auto a : s1) + for (auto b : s2) + mx = std::max(mx, distances_(a,b)); + return mx; +} + +template +typename dionysus::Rips::DistanceType +dionysus::Rips:: +max_distance() const +{ + DistanceType mx = 0; + for (IndexType a = distances_.begin(); a != distances_.end(); ++a) + for (IndexType b = std::next(a); b != distances_.end(); ++b) + mx = std::max(mx, distances_(a,b)); + return mx; +} + +template +typename dionysus::Rips::DistanceType +dionysus::Rips::Evaluator:: +operator()(const Simplex& s) const +{ + DistanceType mx = 0; + for (auto a = s.begin(); a != s.end(); ++a) + for (auto b = std::next(a); b != s.end(); ++b) + mx = std::max(mx, distances_(*a,*b)); + return mx; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.h new file mode 100644 index 0000000000..e2481ce080 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.h @@ -0,0 +1,54 @@ +#ifndef DIONYSUS_ROW_REDUCTION_H +#define DIONYSUS_ROW_REDUCTION_H + +#include "reduced-matrix.h" + +namespace dionysus +{ + +// Mid-level interface +template, template class... Visitors> +class RowReduction +{ + public: + typedef Field_ Field; + typedef Index_ Index; + typedef Comparison_ Comparison; + + typedef ReducedMatrix Persistence; + + public: + RowReduction(const Field& field): + persistence_(field) {} + + RowReduction(const Field& field, + const Comparison& cmp, + const Visitors&... visitors): + persistence_(field, cmp, visitors...) {} + + template + void operator()(const Filtration& f, const Relative& relative, const ReportPair& report_pair, const Progress& progress); + + template + void operator()(const Filtration& f, const ReportPair& report_pair); + + template + void operator()(const Filtration& f) { return (*this)(f, &no_report_pair); } + + static void no_report_pair(int, Index, Index) {} + static void no_progress() {} + + const Persistence& + persistence() const { return persistence_; } + Persistence& persistence() { return persistence_; } + + private: + Persistence persistence_; +}; + +} + +#include "row-reduction.hpp" + +#endif + diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.hpp new file mode 100644 index 0000000000..edb1652872 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.hpp @@ -0,0 +1,103 @@ +#include +namespace ba = boost::adaptors; + +template class... V> +template +void +dionysus::RowReduction:: +operator()(const Filtration& filtration, const ReportPair& report_pair) +{ + using Cell = typename Filtration::Cell; + (*this)(filtration, [](const Cell&) { return false; }, report_pair, &no_progress); +} + +template class... V> +template +void +dionysus::RowReduction:: +operator()(const Filtration& filtration, const Relative& relative, const ReportPair& report_pair, const Progress& progress) +{ + persistence_.resize(filtration.size()); + + typedef typename Persistence::Index Index; + typedef typename Persistence::FieldElement Element; + typedef typename Persistence::Chain Chain; + typedef typename Filtration::Cell Cell; + typedef ChainEntry CellChainEntry; + typedef ChainEntry ChainEntry; + + std::vector rows(persistence_.size()); + + auto& field = persistence_.field(); + + // fill the matrix + Index i = 0; + for(auto& c : filtration) + { + progress(); + + if (relative(c)) + { + persistence_.set_skip(i); + ++i; + continue; + } + + persistence_.set(i, c.boundary(field) | + ba::filtered([relative](const CellChainEntry& e) { return !relative(e.index()); }) | + ba::transformed([this,&filtration](const CellChainEntry& e) + { return ChainEntry(e.element(), filtration.index(e.index())); })); + if (!persistence_[i].empty()) + { + auto& x = persistence_[i].back(); + rows[x.index()].emplace_back(x.element(),i); + } + ++i; + } + + auto entry_cmp = [this](const ChainEntry& e1, const ChainEntry& e2) { return this->persistence_.cmp()(e1.index(), e2.index()); }; + + // reduce the matrix from the bottom up + for (auto it = rows.rbegin(); it != rows.rend(); ++it) + { + auto& row = *it; + Index r = rows.rend() - it - 1; + + if (row.empty()) + continue; + + // add the first column to every other column + Index c = row.front().index(); + Element e = row.front().element(); + Chain& first = persistence_.column(c); + for (size_t i = 1; i < row.size(); ++i) + { + Index cur_idx = row[i].index(); + Element cur_elem = row[i].element(); + Chain& cur = persistence_.column(cur_idx); + if (cur.empty()) // zeroed out by the clearing optimization + continue; + + Element m = field.neg(field.div(cur_elem, e)); + // cur += m*first + ::dionysus::Chain::addto(cur, m, first, field, entry_cmp); + + // update row + if (!cur.empty()) + { + ChainEntry ce = cur.back(); + auto& new_row = rows[ce.index()]; + new_row.emplace_back(ce.element(), cur_idx); + if (entry_cmp(new_row.back(), new_row.front())) + std::swap(new_row.back(), new_row.front()); + } + } + + persistence_.set_pair(r,c); + report_pair(filtration[r].dimension(), r, c); + + // zero out the corresponding column (the clearing optimization) + persistence_.column(r).clear(); + } +} + diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/simplex.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/simplex.h new file mode 100644 index 0000000000..4ac5cb6945 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/simplex.h @@ -0,0 +1,280 @@ +#ifndef DIONYSUS_SIMPLEX_H +#define DIONYSUS_SIMPLEX_H + +#include +#include + +//#include +#include +#include +#include + +#include "chain.h" + +namespace dionysus +{ + +struct Empty {}; + +template +class Simplex +{ + public: + typedef Vertex_ Vertex; + typedef T Data; + typedef std::unique_ptr Vertices; + + template + struct BoundaryChainIterator; + struct BoundaryIterator; + + template + using BoundaryChainRange = boost::iterator_range>; + using BoundaryRange = boost::iterator_range; + + template + using Entry = ChainEntry; + + public: + Simplex(const Data& d = Data()): + dim_(-1), data_(d) {} + + Simplex(const std::initializer_list& vertices, + Data&& d = Data()): + Simplex(vertices.size() - 1, vertices.begin(), vertices.end(), std::move(d)) + {} + + Simplex(const std::initializer_list& vertices, + const Data& d): + Simplex(vertices.size() - 1, vertices.begin(), vertices.end(), d) {} + + Simplex(short unsigned dim, Vertices&& vertices, Data&& data = Data()): + dim_(dim), vertices_(std::move(vertices)), data_(std::move(data)) { std::sort(begin(), end()); } + + template + Simplex(const VertexRange& vertices, + Data&& d = Data()): + Simplex(vertices.size() - 1, vertices.begin(), vertices.end(), std::move(d)) + {} + + template + Simplex(const VertexRange& vertices, + const Data& d): + Simplex(vertices.size() - 1, vertices.begin(), vertices.end(), d) {} + + Simplex(const Simplex& other): + Simplex(other.dim_, other.begin(), other.end(), other.data_) {} + Simplex& operator=(const Simplex& other) { dim_ = other.dim_; vertices_ = Vertices(new Vertex[dim_+1]); std::copy(other.begin(), other.end(), begin()); data_ = other.data_; return *this; } + + Simplex(Simplex&& other) noexcept: + dim_(other.dim_), + vertices_(std::move(other.vertices_)), + data_(std::move(other.data_)) {} + Simplex& operator=(Simplex&& other) = default; + + template + Simplex(short unsigned dim, + Iterator b, Iterator e, + Data&& d = Data()): + dim_(dim), + vertices_(new Vertex[dim_+1]), + data_(std::move(d)) { std::copy(b, e, begin()); std::sort(begin(), end()); } + + template + Simplex(short unsigned dim, + Iterator b, Iterator e, + const Data& d): + dim_(dim), + vertices_(new Vertex[dim_+1]), + data_(d) { std::copy(b, e, begin()); std::sort(begin(), end()); } + + short unsigned dimension() const { return dim_; } + + BoundaryRange boundary() const { return BoundaryRange(boundary_begin(), boundary_end()); } + BoundaryIterator boundary_begin() const; + BoundaryIterator boundary_end() const; + + template + BoundaryChainRange + boundary(const Field& field) const { return BoundaryChainRange(boundary_begin(field), boundary_end(field)); } + + template + BoundaryChainIterator + boundary_begin(const Field& field) const; + template + BoundaryChainIterator + boundary_end(const Field& field) const; + + const Vertex* begin() const { return vertices_.get(); } + const Vertex* end() const { return begin() + dim_ + 1; } + size_t size() const { return dim_ + 1; } + + std::pair + range() const { return std::make_pair(begin(), end()); } + + Simplex join(const Vertex& v) const { Vertices vertices(new Vertex[dim_+2]); std::copy(begin(), end(), vertices.get()); vertices[dim_+1] = v; return Simplex(dim_ + 1, std::move(vertices), Data(data_)); } + + bool operator==(const Simplex& other) const { return dim_ == other.dim_ && std::equal(begin(), end(), other.begin()); } + bool operator!=(const Simplex& other) const { return !operator==(other); } + bool operator<(const Simplex& other) const { return dim_ < other.dim_ || (dim_ == other.dim_ && std::lexicographical_compare(begin(), end(), other.begin(), other.end())); } + bool operator>(const Simplex& other) const { return other < (*this); } + + Vertex operator[](short unsigned i) const { return vertices_[i]; } + const Data& data() const { return data_; } + Data& data() { return data_; } + + friend + std::ostream& operator<<(std::ostream& out, const Simplex& s) + { out << '<' << *s.begin(); for (auto it = s.begin() + 1; it != s.end(); ++it) out << ',' << *it; out << '>'; return out; } + + private: + Vertex* begin() { return vertices_.get(); } + Vertex* end() { return begin() + dim_ + 1; } + + private: + short unsigned dim_; + //boost::compressed_pair vertices_data_; + Vertices vertices_; + Data data_; // TODO: optimize +}; + +template +size_t hash_value(const Simplex& s) { return boost::hash_range(s.begin(), s.end()); } + + +template +struct Simplex::BoundaryIterator: + public boost::iterator_adaptor, // Value + boost::use_default, + Simplex> // Reference +{ + public: + typedef const V* Iterator; + typedef Simplex Value; + + typedef boost::iterator_adaptor Parent; + + BoundaryIterator() {} + explicit BoundaryIterator(short unsigned dim, Iterator iter, Iterator bg, Iterator end): + Parent(iter), dim_(dim), bg_(bg), end_(end) {} + + Iterator begin() const { return bg_; } + + private: + friend class boost::iterator_core_access; + Value dereference() const + { + typedef std::not_equal_to NotEqualVertex; + + using std::placeholders::_1; + return Simplex(dim_ - 1, + boost::make_filter_iterator(std::bind(NotEqualVertex(), _1, *(this->base())), bg_, end_), + boost::make_filter_iterator(std::bind(NotEqualVertex(), _1, *(this->base())), end_, end_)); + } + + short unsigned dim_; + Iterator bg_; + Iterator end_; +}; + +template +template +struct Simplex::BoundaryChainIterator: + public boost::iterator_adaptor, // Derived + BoundaryIterator, + ChainEntry>, // Value + boost::use_default, + ChainEntry>> // Reference +{ + public: + typedef F Field; + typedef BoundaryIterator Iterator; + typedef ChainEntry> Value; + + typedef boost::iterator_adaptor Parent; + + BoundaryChainIterator() {} + explicit BoundaryChainIterator(const Field& field, Iterator iter): + Parent(iter), field_(&field) {} + + private: + friend class boost::iterator_core_access; + Value dereference() const + { + return Value(((this->base().base() - this->base().begin()) % 2 == 0)? field_->id() : field_->neg(field_->id()), + *(this->base())); + } + + const Field* field_ = nullptr; +}; + + +/* Simplex */ +template +typename Simplex::BoundaryIterator +Simplex:: +boundary_begin() const +{ + if (dimension() == 0) return boundary_end(); + return BoundaryIterator(dimension(), begin(), begin(), end()); +} + +template +typename Simplex::BoundaryIterator +Simplex:: +boundary_end() const +{ + return BoundaryIterator(dimension(), end(), begin(), end()); +} + +template +template +#if defined(_MSC_VER) +typename Simplex::BoundaryChainIterator +#else +typename Simplex::template BoundaryChainIterator +#endif +Simplex:: +boundary_begin(const F& field) const +{ + if (dimension() == 0) return boundary_end(field); + return BoundaryChainIterator(field, boundary_begin()); +} + +template +template +#if defined(_MSC_VER) +typename Simplex::BoundaryChainIterator +#else +typename Simplex::template BoundaryChainIterator +#endif +Simplex:: +boundary_end(const F& field) const +{ + return BoundaryChainIterator(field, boundary_end()); +} + +} // dionysus + +namespace std +{ + +template +struct hash> +{ + size_t operator()(const dionysus::Simplex& s) const { return hash_value(s); } +}; + +} // std + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.h new file mode 100644 index 0000000000..fb1e929e02 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.h @@ -0,0 +1,184 @@ +#ifndef DIONYSUS_SPARSE_ROW_MATRIX_H +#define DIONYSUS_SPARSE_ROW_MATRIX_H + +#include +#include +#include +#include // for debugging output + +#include + +#include "chain.h" +#include "reduction.h" + +namespace dionysus +{ + +namespace bi = boost::intrusive; + +namespace detail +{ + typedef bi::list_base_hook> auto_unlink_hook; + + template + struct SparseRowMatrixEntry: + public ChainEntry, auto_unlink_hook> + { + typedef I Index; + typedef typename F::Element FieldElement; + typedef std::tuple IndexPair; // (id, pair) + typedef ChainEntry Parent; + typedef SparseRowMatrixEntry Entry; + + SparseRowMatrixEntry(FieldElement e, const IndexPair& ip): + Parent(e,ip) {} + + SparseRowMatrixEntry(FieldElement e, const Index& r, const Index& c): + Parent(e,IndexPair(r,c)) {} + + SparseRowMatrixEntry(const Entry& other) = default; + SparseRowMatrixEntry(Entry&& other) = default; + Entry& operator=(Entry&& other) = default; + + void unlink() { auto_unlink_hook::unlink(); } + bool is_linked() const { return auto_unlink_hook::is_linked(); } + }; +} + +template, + template class Column_ = std::vector> +class SparseRowMatrix +{ + public: + typedef Field_ Field; + typedef Index_ Index; + typedef Comparison_ Comparison; + + typedef typename Field::Element FieldElement; + + typedef detail::SparseRowMatrixEntry Entry; + typedef Column_ Column; + typedef typename Entry::IndexPair IndexPair; + typedef bi::list> Row; + + typedef std::vector> IndexChain; + + typedef std::unordered_map Columns; + typedef std::unordered_map Rows; + typedef std::unordered_map LowMap; + + public: + SparseRowMatrix(const Field& field, + const Comparison& cmp = Comparison()): + field_(field), cmp_(cmp) {} + + SparseRowMatrix(SparseRowMatrix&& other) = default; + + + template + Column reduce(const ChainRange& chain, IndexChain& trail); + + Index set(Index i, Column&& chain); // returns previous column with this low + void fix(Index c, Column& column); + void fix(Index c) { fix(c, col(c)); } + + const Row& prepend_row(Index r, FieldElement m, const Row& chain); // could be horribly inefficient if Column is chosen poorly + + void drop_row(Index r) { rows_.erase(r); if (is_low(r)) lows_.erase(r); } + void drop_col(Index c) + { + auto cit = columns_.find(c); + Column& column = cit->second; + if (!column.empty()) + { + Index rlow = std::get<0>(column.back().index()); + auto it = lows_.find(rlow); + if (it != lows_.end() && it->second == c) + lows_.erase(it); + } + columns_.erase(cit); + } + void drop_low(Index r) { lows_.erase(r); } + + // accessors + Row& row(Index r) { return rows_[r]; } + Column& col(Index c) { assert(col_exists(c)); return columns_.find(c)->second; } + const Column& col(Index c) const { assert(col_exists(c)); return columns_.find(c)->second; } + Index low(Index r) const { return lows_.find(r)->second; } + bool is_low(Index r) const { return lows_.find(r) != lows_.end(); } + void update_low(Index c) { lows_[std::get<0>(col(c).back().index())] = c; } + + const Field& field() const { return field_; } + void reserve(size_t) {} // here for compatibility only + const Comparison& cmp() const { return cmp_; } + + // debug + bool col_exists(Index c) const { return columns_.find(c) != columns_.end(); } + const Columns& columns() const { return columns_; } + void check_columns() const + { + for (auto& x : columns_) + { + Index c = x.first; + if (x.second.empty()) + std::cout << "Warning: empty column " << c << std::endl; + Index rl = std::get<0>(x.second.back().index()); + if (!is_low(rl) || low(rl) != c) + { + std::cout << "Columns don't check out: lows don't match" << std::endl; + std::cout << " " << c << ' ' << rl << ' ' << ' ' << low(rl) << std::endl; + std::cout << "---\n"; + for (auto& x : col(c)) + std::cout << " " << x.element() << ' ' << std::get<0>(x.index()) << ' ' << std::get<1>(x.index()) << '\n'; + std::cout << "---\n"; + for (auto& x : col(low(rl))) + std::cout << " " << x.element() << ' ' << std::get<0>(x.index()) << ' ' << std::get<1>(x.index()) << '\n'; + assert(0); + } + + for (auto& x : lows_) + { + if (!col_exists(x.second)) + { + std::cout << "Still keeping low of a removed column" << std::endl; + assert(0); + } + else if (std::get<0>(col(x.second).back().index()) != x.first) + { + std::cout << "Low mismatch: " << x.second << ' ' << std::get<0>(col(x.second).back().index()) << ' ' << x.first << '\n'; + assert(0); + } + } + } + } + + private: + Field field_; + Comparison cmp_; + + Columns columns_; + Rows rows_; + LowMap lows_; // column that has this low +}; + + +namespace detail +{ + +template +struct Unpaired> +{ + static + constexpr std::tuple + value() + { return std::make_tuple(std::numeric_limits::max(), + std::numeric_limits::max()); } +}; + +} + +} + +#include "sparse-row-matrix.hpp" + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.hpp new file mode 100644 index 0000000000..10f4808c17 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.hpp @@ -0,0 +1,103 @@ +template class Col> +template +typename dionysus::SparseRowMatrix::Column +dionysus::SparseRowMatrix:: +reduce(const ChainRange& chain_, IndexChain& trail) +{ + auto row_cmp = [this](const Entry& e1, const Entry& e2) + { return this->cmp_(std::get<0>(e1.index()), std::get<0>(e2.index())); }; + +#define __DIONYSUS_USE_VECTOR_CHAINS 1 + +#if !(__DIONYSUS_USE_VECTOR_CHAINS) + std::set chain(row_cmp); + for (auto x : chain_) + chain.insert(Entry(x.element(), IndexPair(x.index(), 0))); +#else + Column chain; + for (auto x : chain_) + chain.emplace_back(x.element(), IndexPair(x.index(), 0)); + std::sort(chain.begin(), chain.end(), row_cmp); +#endif + + typedef Reduction ReductionIP; + + auto chains = [this](const IndexPair& rc) -> const Column& { return this->col(std::get<1>(rc)); }; + auto lows = [this](const IndexPair& rc) -> IndexPair + { + Index r = std::get<0>(rc); + auto it = this->lows_.find(r); + if (it == this->lows_.end()) + return ReductionIP::unpaired; + else + { + Index rr = std::get<0>(col(it->second).back().index()); + if (rr != r) + std::cout << "Mismatch: " << rr << ' ' << r << std::endl; + return IndexPair(r, it->second); + } + }; + + auto addto = [&trail](FieldElement m, const IndexPair& rc) { trail.emplace_back(m, std::get<1>(rc)); }; + + ReductionIP::reduce(chain, + chains, lows, + field_, addto, row_cmp); + +#if !(__DIONYSUS_USE_VECTOR_CHAINS) + return Column(std::begin(chain), std::end(chain)); +#else + return chain; +#endif +} + +template class Col> +typename dionysus::SparseRowMatrix::Index +dionysus::SparseRowMatrix:: +set(Index col, Column&& chain) +{ + Column& column = columns_.emplace(col, std::move(chain)).first->second; + + fix(col, column); + + Index r = std::get<0>(column.back().index()); + Index res; + if (is_low(r)) + res = low(r); + else + res = col; + lows_[r] = col; + + return res; +} + +template class Col> +void +dionysus::SparseRowMatrix:: +fix(Index col, Column& column) +{ + for (auto& x : column) + { + std::get<1>(x.index()) = col; + Index r = std::get<0>(x.index()); + row(r).push_back(x); + } +} + +template class Col> +const typename dionysus::SparseRowMatrix::Row& +dionysus::SparseRowMatrix:: +prepend_row(Index r, FieldElement m, const Row& chain) +{ + Row& new_row = row(r); + + for (auto& x : chain) + { + Index c = std::get<1>(x.index()); + Column& column = col(c); + auto it = column.emplace(column.begin(), field().mul(x.element(), m), r, c); + new_row.push_back(*it); + } + + return new_row; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.h new file mode 100644 index 0000000000..0477d4683f --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.h @@ -0,0 +1,44 @@ +#ifndef DIONYSUS_STANDARD_REDUCTION_H +#define DIONYSUS_STANDARD_REDUCTION_H + +namespace dionysus +{ + +// Mid-level interface +template +class StandardReduction +{ + public: + typedef Persistence_ Persistence; + typedef typename Persistence::Field Field; + typedef typename Persistence::Index Index; + + public: + StandardReduction(Persistence& persistence): + persistence_(persistence) {} + + template + void operator()(const Filtration& f, const Relative& relative, const ReportPair& report_pair, const Progress& progress); + + template + void operator()(const Filtration& f, const ReportPair& report_pair); + + template + void operator()(const Filtration& f) { return (*this)(f, &no_report_pair); } + + static void no_report_pair(int, Index, Index) {} + static void no_progress() {} + + const Persistence& + persistence() const { return persistence_; } + Persistence& persistence() { return persistence_; } + + private: + Persistence& persistence_; +}; + +} + +#include "standard-reduction.hpp" + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.hpp new file mode 100644 index 0000000000..9aa3396a8c --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.hpp @@ -0,0 +1,47 @@ +#include +namespace ba = boost::adaptors; + +template +template +void +dionysus::StandardReduction

:: +operator()(const Filtration& filtration, const ReportPair& report_pair) +{ + using Cell = typename Filtration::Cell; + (*this)(filtration, [](const Cell&) { return false; }, report_pair, no_progress); +} + +template +template +void +dionysus::StandardReduction

:: +operator()(const Filtration& filtration, const Relative& relative, const ReportPair& report_pair, const Progress& progress) +{ + persistence_.reserve(filtration.size()); + + typedef typename Filtration::Cell Cell; + typedef ChainEntry CellChainEntry; + typedef ChainEntry ChainEntry; + + unsigned i = 0; + for(auto& c : filtration) + { + progress(); + + if (relative(c)) + { + ++i; + persistence_.add_skip(); + continue; + } + + //std::cout << "Adding: " << c << " : " << boost::distance(c.boundary(persistence_.field())) << std::endl; + Index pair = persistence_.add(c.boundary(persistence_.field()) | + ba::filtered([relative](const CellChainEntry& e) { return !relative(e.index()); }) | + ba::transformed([this,&filtration](const CellChainEntry& e) + { return ChainEntry(e.element(), filtration.index(e.index())); })); + if (pair != persistence_.unpaired()) + report_pair(c.dimension(), pair, i); + ++i; + } +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/trails-chains.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/trails-chains.h new file mode 100644 index 0000000000..f18ff897e4 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/trails-chains.h @@ -0,0 +1,17 @@ +#ifndef DIONYSUS_TRAILS_CHAINS_H +#define DIONYSUS_TRAILS_CHAINS_H + +#include "ordinary-persistence.h" + +template +struct ChainsVisitor: public EmptyVisitor +{ + template + void chain_initialized(Chain& c) { } + + void addto(typename Field::Element m, Index cl) {} + void reduction_finished() {} +}; + + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.h new file mode 100644 index 0000000000..e9423099aa --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.h @@ -0,0 +1,142 @@ +#ifndef DIONYSUS_ZIGZAG_PERSISTENCE_H +#define DIONYSUS_ZIGZAG_PERSISTENCE_H + +#include +#include +#include + +#include +#include + +#include "sparse-row-matrix.h" + +namespace dionysus +{ + +namespace ba = boost::adaptors; + +template> +class ZigzagPersistence +{ + static_assert(std::is_signed::value, "Index type used in ZigzagPersistence must be a *signed* integer"); + + public: + typedef Field_ Field; + typedef Index_ Index; + typedef Comparison_ Comparison; + + typedef SparseRowMatrix RowMatrix; + typedef SparseRowMatrix DequeRowMatrix; + typedef typename RowMatrix::IndexPair IndexPair; + typedef typename RowMatrix::FieldElement FieldElement; + typedef typename RowMatrix::IndexChain IndexChain; + typedef typename RowMatrix::Column Column; + typedef typename RowMatrix::Row Row; + typedef typename DequeRowMatrix::Column DequeColumn; + typedef typename DequeRowMatrix::Row DequeRow; + + typedef std::unordered_map BirthIndexMap; + + + ZigzagPersistence(const Field& field, + const Comparison& cmp = Comparison()): + Z(field, cmp), C(field, cmp), B(field, cmp), + operations(0), + cell_indices(0), + z_indicies_last(0), + z_indicies_first(-1), + b_indices(0) {} + + template + Index add(const ChainRange& chain) // returns the id of the dying cycle (or unpaired) + { + Index res = add_impl(chain); +#ifdef DIONYSUS_ZIGZAG_DEBUG + check_sorted(); + check_b_cols(); + Z.check_columns(); +#endif + return res; + } + Index remove(Index cell) + { + Index res = remove_impl(cell); +#ifdef DIONYSUS_ZIGZAG_DEBUG + check_sorted(); + check_b_cols(); + Z.check_columns(); +#endif + return res; + } + + struct IsAlive + { + IsAlive(const ZigzagPersistence& zz_): zz(&zz_) {} + bool operator()(const std::pair& x) const { return zz->is_alive(x.first); } + const ZigzagPersistence* zz; + }; + + bool is_alive(Index x) const { return !B.is_low(x); } + + auto alive_ops() const -> decltype(BirthIndexMap() | ba::filtered(IsAlive(*this)) | ba::map_values) + { return birth_index | ba::filtered(IsAlive(*this)) | ba::map_values; } + + auto alive_cycles() const -> decltype(BirthIndexMap() | ba::filtered(IsAlive(*this)) | ba::map_keys) + { return birth_index | ba::filtered(IsAlive(*this)) | ba::map_keys; } + + size_t alive_size() const { return Z.columns().size() - B.columns().size(); } + + void reserve(size_t) {} // here for compatibility only + const Field& field() const { return Z.field(); } + const Comparison& cmp() const { return Z.cmp(); } + + template + static Index row(const Entry& e) { return std::get<0>(e.index()); } + template + static Index col(const Entry& e) { return std::get<1>(e.index()); } + + static + const Index unpaired() { return Reduction::unpaired; } + + const Column& cycle(Index i) const { return Z.col(i); } + + // debug + void check_b_cols() const; + + template + void check_boundaries(const SimplexToIndex& s2i, const IndexToSimplex& i2s) const; + template + void check_cycles(const SimplexToIndex& s2i, const IndexToSimplex& i2s) const; + + Column zb_dot(Index c) const; + + template + Column dc_dot(Index c, const SimplexToIndex& s2i, const IndexToSimplex& i2s) const; + + template + Column boundary(Index i, const SimplexToIndex& s2i, const IndexToSimplex& i2s) const; + + void check_sorted() const; + + private: + template + Index add_impl(const ChainRange& chain); + Index remove_impl(Index cell); + + private: + RowMatrix Z, C; + DequeRowMatrix B; + + BirthIndexMap birth_index; + Index operations; + Index cell_indices; + Index z_indicies_last, z_indicies_first; + Index b_indices; +}; + +} + +#include "zigzag-persistence.hpp" + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.hpp new file mode 100644 index 0000000000..96331a3b15 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.hpp @@ -0,0 +1,541 @@ +#include + +template +template +typename dionysus::ZigzagPersistence::Index +dionysus::ZigzagPersistence:: +add_impl(const ChainRange& chain_) +{ + // std::cout << "add(" << cell_indices << ")" << std::endl; + Index op = operations++; + + IndexChain cycles; // chain_ -> Z*cycles + Column z_remainder = Z.reduce(chain_, cycles); + // std::cout << "cycle: "; + // for (auto& v : cycles){ + // std::cout << v.index() << " "; + // } + // std::cout << "\n"; + assert(z_remainder.empty()); + + IndexChain boundaries; + DequeColumn b_remainder = B.reduce(cycles, boundaries); + + // add up columns of C indexed by boundaries + typedef typename Column::value_type Entry; + auto row_cmp = [this](const Entry& e1, const Entry& e2) + { return this->cmp()(row(e1), row(e2)); }; + Column chain; + for (auto& x : boundaries) + Chain::addto(chain, x.element(), C.col(x.index()), field(), row_cmp); + chain.push_back(Entry(field().neg(field().id()), IndexPair(cell_indices++,0))); + + if (b_remainder.empty()) // birth + { + // std::cout << " birth" << std::endl; + Index z_col = z_indicies_last++; + Z.set(z_col, std::move(chain)); + birth_index[z_col] = op; + return unpaired(); + } + else // death + { + // std::cout << " death" << std::endl; + Index b_col = b_indices++; + Index pair = row(b_remainder.back()); + B.set(b_col, std::move(b_remainder)); + C.set(b_col, std::move(chain)); + return birth_index[pair]; + } +} + +template +typename dionysus::ZigzagPersistence::Index +dionysus::ZigzagPersistence:: +remove_impl(Index cell) +{ + //std::cout << "remove(" << cell << ")" << std::endl; + + Index op = operations++; + + typedef typename Column::value_type Entry; + auto row_cmp = [this](const Entry& e1, const Entry& e2) + { return this->cmp()(row(e1), row(e2)); }; + typedef typename DequeColumn::value_type DequeEntry; + auto b_row_cmp = [this](const DequeEntry& e1, const DequeEntry& e2) + { return this->cmp()(row(e1), row(e2)); }; + + IndexChain z_row; + for (auto& x : Z.row(cell)) + z_row.emplace_back(x.element(), col(x)); + + if (z_row.empty()) // birth + { + //std::cout << " birth" << std::endl; + Row& c_row = C.row(cell); + // c_row.front() may not be the first column in order, but that doesn't really matter, does it? (TODO) + auto& c_front = c_row.front(); + + Index j = col(c_front); + Index l = row(B.col(j).back()); + + //std::cout << j << ' ' << l << std::endl; + + // cycle = ZB[j] = DC[j] + Column cycle; + for (auto& x : B.col(j)) + Chain::addto(cycle, x.element(), Z.col(row(x)), field(), row_cmp); + + //std::cout << "Cycle:" << std::endl; + //for (auto& x : cycle) + // std::cout << x.element() << ' ' << row(x) << std::endl; + + // 1: prepend the cycle + Index znew = z_indicies_first--; + Index oth = Z.set(znew, std::move(cycle)); // oth records our collision (used in step 6) + birth_index[znew] = op; + + //std::cout << "znew oth: " << znew << ' ' << oth << std::endl; + //std::cout << "oth column:" << std::endl; + //for (auto& x : Z.col(oth)) + // std::cout << x.element() << ' ' << row(x) << std::endl; + + // 2: prepend the row to B + FieldElement m = field().neg(field().inv(c_front.element())); // m = -1/c + const DequeRow& b_row = B.prepend_row(znew, m, c_row); + //std::cout << "Prepended row with multiplier: " << m << " (" << b_row.size() << ")" << std::endl; + + // 3: subtract C[j] from every C[k] + const Column& Cj = C.col(j); + + // use the copy of c_row in B, since c_row will be modified in the following loop + for (auto it = std::next(b_row.begin()); it != b_row.end(); ++it) + { + Index c = col(*it); + assert(c != j); + //std::cout << "adding to " << c << " in C" << std::endl; + Chain::addto(C.col(c), it->element(), Cj, field(), row_cmp); // using it->element() since b_row = m*c_row + C.fix(c); // old elements got removed via auto_unlink_hook + // we don't need lows in C, so not updating them + } + //std::cout << "Done with step 3" << std::endl; + + // 4: subtract B[j] from every B[k] that has l + // (we don't need to update C because ZB[j] = 0 after step 2) + DequeColumn& Bj = B.col(j); + FieldElement bm = field().neg(field().inv(Bj.back().element())); // bm = -1/B[l,j] + IndexChain Bl_row; // make a copy of Bl_row, since it will be changing + for (auto& x : B.row(l)) + { + if (col(x) == j) + continue; + Bl_row.emplace_back(x.element(), col(x)); + } + for (auto& x : Bl_row) + { + Index c = x.index(); + assert(c != j); + Chain::addto(B.col(c), field().mul(bm, x.element()), Bj, field(), b_row_cmp); + B.fix(c); // old elements got removed via auto_unlink_hook + // l cannot be the low in c, so no need to update lows + } + //std::cout << "Done with step 4" << std::endl; + + // 5: drop row l and column j from B; drop column l from Z; drop column j from C + B.drop_col(j); + assert(B.row(l).empty()); + B.drop_row(l); + Index Zl_low = row(Z.col(l).back()); + Z.drop_col(l); + birth_index.erase(l); + C.drop_col(j); + assert(Z.row(cell).empty()); + assert(C.row(cell).empty()); + C.drop_row(cell); + Z.drop_row(cell); + //std::cout << "Done with step 5" << std::endl; + if (oth == l) // we just dropped our collision in Z + oth = znew; + else + Z.drop_low(Zl_low); + + // 6: reduce Z + std::unordered_map b_changes; // the columns to add in B to apply row changes + Index cur = znew; + while (oth != cur) + { + Column& cur_col = Z.col(cur); + Column& oth_col = Z.col(oth); + assert(row(cur_col.back()) == row(oth_col.back())); + //std::cout << "--- " << cur << " (" << cur_col.size() << ") " << oth << " (" << oth_col.size() << ")" << std::endl; + FieldElement m1 = cur_col.back().element(); + FieldElement m2 = oth_col.back().element(); + FieldElement m2_div_m1 = field().div(m2, m1); + Chain::addto(oth_col, field().neg(m2_div_m1), cur_col, field(), row_cmp); + Z.fix(oth, oth_col); + + // record the changes we need to make in B; + // because there is only one collision in the matrix during the reduction, + // once we use a row as the source, we never revisit it. This means once the row is updated in B, + // we never touch it again, so below record is fine. + for (auto& x : this->B.row(oth)) + b_changes[col(x)].emplace_back(field().mul(x.element(), m2_div_m1), cur, col(x)); + + cur = oth; + Index low = row(oth_col.back()); + if (Z.is_low(low)) + oth = Z.low(low); + //std::cout << "--- -- new low: " << low << ' ' << cur << ' ' << oth << std::endl; + + if (cmp()(oth, cur)) + std::swap(oth, cur); + else + Z.update_low(cur); + } + + // apply changes in B (the complexity here could get ugly) + for (auto& bx : b_changes) + { + std::sort(bx.second.begin(), bx.second.end(), b_row_cmp); + Chain::addto(B.col(bx.first), field().id(), bx.second, field(), b_row_cmp); + B.fix(bx.first); + // no need to update low (additions from bottom up) + } + //std::cout << "Done with step 6" << std::endl; + + return unpaired(); + } + else // death + { + //std::cout << " death" << std::endl; + + auto index_chain_cmp = [this](const typename IndexChain::value_type& e1, const typename IndexChain::value_type& e2) + { return this->cmp()(e1.index(), e2.index()); }; + + // 1: change basis to clear z_row + std::sort(z_row.begin(), z_row.end(), index_chain_cmp); // this adds a log factor, but it makes life easier + Index j = z_row.front().index(); + FieldElement e = z_row.front().element(); + + if (z_row.size() > 1) + { + // figure out the columns we use for reduction + typedef typename IndexChain::const_iterator RowIterator; + std::vector reducers; + reducers.push_back(z_row.begin()); + for (RowIterator it = std::next(z_row.begin()); it != z_row.end(); ++it) + { + Index c = it->index(); + + assert(Z.col_exists(c)); + assert(Z.col_exists(reducers.back()->index())); + if (cmp()(row(Z.col(c).back()), + row(Z.col(reducers.back()->index()).back()))) + reducers.push_back(it); + } + reducers.push_back(z_row.end()); + //std::cout << "reducers.size(): " << reducers.size() << std::endl; + //std::cout << "z_row.size(): " << z_row.size() << std::endl; + + + std::map b_changes; // the rows to add to B + auto add_in_z = [this,&b_changes,&row_cmp,&index_chain_cmp](Index to, Index from, FieldElement m, FieldElement e) + { + //std::cout << " add_in_z: " << from << ' ' << to << std::endl; + + FieldElement mult = this->field().mul(m, e); + assert(Z.col_exists(to)); + assert(Z.col_exists(from)); + Chain::addto(Z.col(to), mult, Z.col(from), this->field(), row_cmp); + assert(!Z.col(to).empty()); + this->Z.fix(to); // NB: rows will be linked in the back, so the iterators are Ok + this->Z.update_low(to); + + // subtract B.row(to) from B.row(from) + IndexChain Bto_row; + for (auto& x : this->B.row(to)) + Bto_row.emplace_back(x.element(), col(x)); + std::sort(Bto_row.begin(), Bto_row.end(), index_chain_cmp); + +#if 0 + for (auto& x : this->B.row(to)) + std::cout << x.element() << ' ' << row(x) << ' ' << col(x) << std::endl; + + std::cout << "---\n"; + + for (auto& x : this->B.row(from)) + std::cout << x.element() << ' ' << row(x) << ' ' << col(x) << std::endl; +#endif + + Chain::addto(b_changes[from], this->field().neg(mult), Bto_row, this->field(), index_chain_cmp); + + // if there is b_changes[to] add it, too + auto it = b_changes.find(to); + if (it != b_changes.end()) + Chain::addto(b_changes[from], this->field().neg(mult), it->second, this->field(), index_chain_cmp); + }; + Index last_low = row(Z.col(reducers[reducers.size() - 2]->index()).back()); + for (int i = reducers.size() - 2; i >= 0; --i) + { + auto rit = reducers[i]; + FieldElement m = field().neg(field().inv(rit->element())); + + for (auto it = std::next(rit); it != reducers[i+1]; ++it) + add_in_z(it->index(), rit->index(), m, it->element()); + + if (static_cast(i + 1) != reducers.size() - 1) + { + auto it = reducers[i+1]; + add_in_z(it->index(), rit->index(), m, it->element()); + } + } + if (reducers.size() > 2) + Z.drop_low(last_low); + + // apply changes in b (the complexity here could get ugly) + // Specifically, transpose b_changes and add it in + std::unordered_map b_changes_transposed; + for (auto& b_row : b_changes) + for (auto& bx : b_row.second) + b_changes_transposed[bx.index()].emplace_back(bx.element(), b_row.first, bx.index()); + + for (auto& b_col : b_changes_transposed) + { +#if 0 + std::cout << "Adding:" << std::endl; + for (auto& x : b_col.second) + std::cout << x.element() << ' ' << row(x) << ' ' << col(x) << std::endl; +#endif + Chain::addto(B.col(b_col.first), field().id(), b_col.second, field(), b_row_cmp); + assert(!B.col(b_col.first).empty()); + B.fix(b_col.first); + // no need to update low (additions from bottom up) + } + } // z_row.size() > 1 + + // 2: subtract cycle from every chain in C + const Column& Zj = Z.col(j); + //std::cout << "Zj:" << std::endl; + //for (auto& x : Zj) + // std::cout << x.element() << " * " << row(x) << std::endl; + + IndexChain Ccols; // save the columns in C, we'll be modifying C.row(cell) + for (auto& x : C.row(cell)) + Ccols.emplace_back(x.element(), col(x)); + + for (auto& x : Ccols) + { + Index c = x.index(); + FieldElement m = field().neg(field().div(x.element(), e)); // m = -C[k][cell]/Z[j][cell] + //std::cout << "Adding to C: " << c << std::endl; + Chain::addto(C.col(c), m, Zj, field(), row_cmp); + C.fix(c); + // we don't care about lows in C, so don't update them + } + + // 3: drop + assert(Z.row(cell).size() == 1); + Z.drop_col(j); + assert(Z.row(cell).empty()); + assert(C.row(cell).empty()); + Z.drop_row(cell); + C.drop_row(cell); + assert(B.row(j).empty()); + B.drop_row(j); + + Index birth = birth_index[j]; + birth_index.erase(j); + + return birth; + } +} + + +/* debug routines */ +template +void +dionysus::ZigzagPersistence:: +check_b_cols() const +{ + // check that entries in B refer to existing Z columns + bool stop = false; + for (auto& b : B.columns()) + for (auto& x : b.second) + if (!Z.col_exists(row(x))) + { + std::cout << "B refers to a non-existent column in Z: " << row(x) << std::endl; + stop = true; + } + if (stop) + assert(0); +} + +template +template +void +dionysus::ZigzagPersistence:: +check_cycles(const SimplexToIndex& s2i, const IndexToSimplex& i2s) const +{ + typedef typename Column::value_type Entry; + auto row_cmp = [this](const Entry& e1, const Entry& e2) + { return this->cmp()(row(e1), row(e2)); }; + + for (auto& z : Z.columns()) + { + Column res; + for (auto& x : z.second) + { + Column bdry = boundary(row(x), s2i, i2s); + Chain::addto(res, x.element(), bdry, field(), row_cmp); + } + assert(res.empty()); + } +} + +template +template +void +dionysus::ZigzagPersistence:: +check_boundaries(const SimplexToIndex& s2i, const IndexToSimplex& i2s) const +{ + check_cycles(s2i, i2s); + + for (auto& x : B.columns()) + if (!C.col_exists(x.first)) + { + std::cout << x.first << " in B, but not in C" << std::endl; + assert(0); + } + + for (auto& x : C.columns()) + if (!B.col_exists(x.first)) + { + std::cout << x.first << " in B, but not in C" << std::endl; + assert(0); + } + + for (auto& x : B.columns()) + { + auto zb = zb_dot(x.first); + auto dc = dc_dot(x.first, s2i, i2s); + + auto it_zb = zb.begin(), + it_dc = dc.begin(); + for (; it_zb != zb.end(); ++it_zb, ++it_dc) + { + if (it_zb->element() != it_dc->element() || row(*it_zb) != row(*it_dc)) + { + std::cout << "Boundary mismatch: " << x.first << std::endl; + std::cout << "===" << std::endl; + for (auto& x : zb) + std::cout << " " << x.element() << ' ' << row(x) << std::endl; + for (auto& y : B.col(x.first)) + { + std::cout << " " << y.element() << " * " << row(y) << std::endl; + for (auto& z : Z.col(row(y))) + std::cout << " " << z.element() << ' ' << row(z) << std::endl; + std::cout << " ---" << std::endl; + } + std::cout << "===" << std::endl; + for (auto& x : dc) + std::cout << " " << x.element() << ' ' << row(x) << std::endl; + for (auto& y : C.col(x.first)) + { + std::cout << " " << y.element() << " * " << row(y) << std::endl; + for (auto& z : boundary(row(y), s2i, i2s)) + std::cout << " " << z.element() << ' ' << row(z) << std::endl; + std::cout << " ---" << std::endl; + } + assert(0); + } + } + if (it_zb != zb.end() || it_dc != dc.end()) + { + std::cout << "zb.end() doesn't match dc.end()" << std::endl; + assert(0); + } + } +} + +template +typename dionysus::ZigzagPersistence::Column +dionysus::ZigzagPersistence:: +zb_dot(Index c) const +{ + typedef typename Column::value_type Entry; + auto row_cmp = [this](const Entry& e1, const Entry& e2) + { return this->cmp()(row(e1), row(e2)); }; + Column res; + for (auto& x : B.col(c)) + Chain::addto(res, x.element(), Z.col(row(x)), field(), row_cmp); + + return res; +} + +template +template +typename dionysus::ZigzagPersistence::Column +dionysus::ZigzagPersistence:: +dc_dot(Index c, const SimplexToIndex& s2i, const IndexToSimplex& i2s) const +{ + typedef typename Column::value_type Entry; + auto row_cmp = [this](const Entry& e1, const Entry& e2) + { return this->cmp()(row(e1), row(e2)); }; + Column res; + for (auto& x : C.col(c)) + { + Column bdry = boundary(row(x), s2i, i2s); + Chain::addto(res, x.element(), bdry, field(), row_cmp); + } + return res; +} + +template +template +typename dionysus::ZigzagPersistence::Column +dionysus::ZigzagPersistence:: +boundary(Index i, const SimplexToIndex& s2i, const IndexToSimplex& i2s) const +{ + typedef typename Column::value_type Entry; + auto row_cmp = [this](const Entry& e1, const Entry& e2) + { return this->cmp()(row(e1), row(e2)); }; + Column bdry; + auto s = i2s(i); + for (auto y : s.boundary(field())) + bdry.emplace_back(y.element(), s2i(y.index()), 0); + std::sort(bdry.begin(), bdry.end(), row_cmp); + return bdry; +} + +template +void +dionysus::ZigzagPersistence:: +check_sorted() const +{ + typedef typename Column::value_type Entry; + auto row_cmp = [this](const Entry& e1, const Entry& e2) + { return this->cmp()(row(e1), row(e2)); }; + typedef typename DequeColumn::value_type DequeEntry; + auto b_row_cmp = [this](const DequeEntry& e1, const DequeEntry& e2) + { return this->cmp()(row(e1), row(e2)); }; + + for (auto& x : Z.columns()) + if (!std::is_sorted(x.second.begin(), x.second.end(), row_cmp)) + { + std::cout << "Z column not sorted: " << x.first << std::endl; + assert(0); + } + for (auto& x : C.columns()) + if (!std::is_sorted(x.second.begin(), x.second.end(), row_cmp)) + { + std::cout << "C column not sorted: " << x.first << std::endl; + assert(0); + } + for (auto& x : B.columns()) + if (!std::is_sorted(x.second.begin(), x.second.end(), b_row_cmp)) + { + std::cout << "B column not sorted: " << x.first << std::endl; + assert(0); + } +} + diff --git a/src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.cpp b/src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.cpp new file mode 100644 index 0000000000..5217a4f38c --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.cpp @@ -0,0 +1,206 @@ +#include "fzz.h" + +#include + +// phat headers +// wrapper algorithm that computes the persistence pairs of a given boundary matrix using a specified algorithm +#include + +// main data structure (choice affects performance) +#include +#include + +// algorithm (choice affects performance) +#include +#include +#include +#include + +namespace FZZ { + +template +class VecHash { +public: + size_t operator()(const std::vector& v) const; +}; + +template +size_t VecHash + ::operator()(const std::vector& v) const { + + std::size_t seed = 0; + + for (auto e : v) { boost::hash_combine(seed, e); } + + return seed; +} + +template +class VecEqual { +public: + bool operator()(const std::vector& v1, + const std::vector& v2) const; +}; + +template +bool VecEqual + ::operator()(const std::vector& v1, + const std::vector& v2) const { + + if (v1.size() != v2.size()) { return false; } + + for (unsigned int i = 0; i < v1.size(); i ++) { + if (v1[i] != v2[i]) { + return false; + } + } + + return true; +} + +typedef std::unordered_map< Simplex, Integer, + VecHash, VecEqual > SimplexIdMap; + +void getBoundaryChainPhat(const std::vector &id_maps, + const Simplex &simp, std::vector &bound_c) { + + bound_c.clear(); + + if (simp.size() <= 1) { return; } + + bound_c.reserve(simp.size()); + + Simplex bound_simp(simp.begin()+1, simp.end()); + bound_c.push_back(id_maps.at(bound_simp.size() - 1).at(bound_simp)); + + for (unsigned int i = 0; i < simp.size()-1; ++i) { + bound_simp[i] = simp[i]; + bound_c.push_back(id_maps.at(bound_simp.size() - 1).at(bound_simp)); + } + + std::sort(bound_c.begin(), bound_c.end()); +} + +inline Integer getDim(const std::vector &bound_c) { + if (bound_c.size() == 0) { return 0; } + return bound_c.size() - 1; +} + +void FastZigzag::compute(const std::vector &filt_simp, + const std::vector &filt_op, + std::vector< std::tuple > *persistence) { + + orig_f_add_id.clear(); + orig_f_del_id.clear(); + persistence->clear(); + + simp_num = 0; + Integer max_dim = 0; + for (unsigned int i = 0; i < filt_op.size(); ++i) { + if (filt_op[i]) { + ++simp_num; + if (static_cast(filt_simp[i].size()) - 1 > max_dim) { max_dim = filt_simp[i].size() - 1; } + } + } + + std::vector bound_c; + // phat::boundary_matrix< phat::vector_vector > bound_chains; + phat::boundary_matrix< phat::bit_tree_pivot_column > bound_chains; + bound_chains.set_num_cols(simp_num * 2 + 1); + + // Add the Omega vertex for the coning + bound_chains.set_col(0, bound_c); + bound_chains.set_dim(0, 0); + + orig_f_add_id.reserve(simp_num); + orig_f_del_id.reserve(simp_num); + + std::vector del_ids; + del_ids.reserve(simp_num); + + std::vector *p_id_maps = new std::vector(max_dim+1); + std::vector &id_maps = *p_id_maps; + + Integer orig_f_id = 0; + Integer s_id = 1; + + for (unsigned int i = 0; i < filt_simp.size(); ++i) { + const Simplex &simp = filt_simp[i]; + + if (filt_op[i]) { + getBoundaryChainPhat(id_maps, simp, bound_c); + bound_chains.set_col(s_id, bound_c); + bound_chains.set_dim(s_id, getDim(bound_c)); + + // assert(s_id == bound_chains.size()-1); + id_maps.at(simp.size() - 1)[simp] = s_id; + orig_f_add_id.push_back(orig_f_id); + s_id ++; + } else { + del_ids.push_back(id_maps.at(simp.size() - 1)[simp]); + id_maps.at(simp.size() - 1).erase(simp); + orig_f_del_id.push_back(orig_f_id); + } + + orig_f_id ++; + } + + for (Integer i = id_maps.size() - 1; i >= 0; -- i) { + for (const auto &it : id_maps.at(i)) { + del_ids.push_back(it.second); + orig_f_del_id.push_back(orig_f_id); + orig_f_id ++; + } + } + + assert(del_ids.size() == s_id-1); + delete p_id_maps; + + assert(simp_num == del_ids.size()); + + std::vector cone_sid(simp_num+1); + + for (auto del_id_it = del_ids.rbegin(); del_id_it != del_ids.rend(); ++del_id_it) { + bound_c.clear(); + bound_c.push_back(*del_id_it); + + std::vector orig_bound_c; + bound_chains.get_col(*del_id_it, orig_bound_c); + + if (orig_bound_c.size() == 0) { + bound_c.push_back(0); + } else { + for (auto bsimp : orig_bound_c) { + // assert(cone_sid[bsimp] >= 0); + bound_c.push_back(cone_sid[bsimp]); + } + } + + std::sort(bound_c.begin(), bound_c.end()); + + bound_chains.set_col(s_id, bound_c); + bound_chains.set_dim(s_id, getDim(bound_c)); + + cone_sid[*del_id_it] = s_id; + + s_id ++; + } + + phat::persistence_pairs pairs; + phat::compute_persistence_pairs< phat::twist_reduction >( pairs, bound_chains ); + + for (phat::index idx = 0; idx < pairs.get_num_pairs(); idx++) { + Integer b = pairs.get_pair(idx).first; + Integer d = pairs.get_pair(idx).second - 1; + Integer p = bound_chains.get_dim(b); + + if (d < simp_num) { mapOrdIntv(b, d); } + else { mapRelExtIntv(p, b, d); } + + if (b > static_cast(filt_simp.size())) { continue; } + if (d > static_cast(filt_simp.size())) { d = filt_simp.size(); } + persistence->emplace_back(b, d, p); + } +} + +} // namespace FZZ { diff --git a/src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.h b/src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.h new file mode 100644 index 0000000000..8613bc3793 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.h @@ -0,0 +1,87 @@ +#ifndef _FZZ_H_ +#define _FZZ_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace FZZ { + +typedef int Integer; +typedef std::vector Simplex; + +class FastZigzag { +public: + /* + 'filt_simp' and 'filt_op' should have the same length which altogether + specify the input zigzag filtration. 'filt_simp' specifies the simplices + being added or deleted (following the order of the filtration) and + 'filt_op' specifies whether it's an addition (true) or deletion (false). + 'persistence' returns the barcode, with the first element of the tuple + being the birth, the second element being the death, and the third + being the dimension. + */ + void compute( + const std::vector &filt_simp, + const std::vector &filt_op, + std::vector< std::tuple > *persistence); + +private: + void mapOrdIntv(Integer &b, Integer &d) { + // assert(b-1 > 0); + // assert(d < orig_f_add_id.size()); + + // Up-down interval is same, + // so directly map to interval of input filtration + b = orig_f_add_id[b-1] + 1; + d = orig_f_add_id[d]; + } + + void mapRelExtIntv(Integer &p, Integer &b, Integer &d) { + // assert(d >= simp_num); + + if (b > simp_num) { // Open-closed + // Map to up-down interval + std::swap(b, d); + b = 3*simp_num - b; + d = 3*simp_num - d; + p --; + + // Map to interval of input filtration + b = orig_f_del_id[b-1-simp_num] + 1; + d = orig_f_del_id[d-simp_num]; + } else { // Closed-closed + // Map to up-down interval + d = 3*simp_num - d-1; + + // Map to interval of input filtration + b = orig_f_add_id[b-1]; + d = orig_f_del_id[d-simp_num]; + + if (b < d) { + b = b+1; + } else { + std::swap(b, d); + b = b+1; + p = p-1; + } + } + } + +private: + // 'orig_f_add_id' and 'orig_f_del_id' form a mapping + // from the up-down filtration to the original filtration + std::vector orig_f_add_id; + std::vector orig_f_del_id; + + Integer simp_num; +}; + +} + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/chunk_reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/chunk_reduction.h new file mode 100644 index 0000000000..179702312f --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/chunk_reduction.h @@ -0,0 +1,223 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + class chunk_reduction { + public: + enum column_type { GLOBAL + , LOCAL_POSITIVE + , LOCAL_NEGATIVE }; + + public: + template< typename Representation > + void operator() ( boundary_matrix< Representation >& boundary_matrix ) { + + + const index nr_columns = boundary_matrix.get_num_cols(); + if( omp_get_max_threads( ) > nr_columns ) + omp_set_num_threads( 1 ); + + const dimension max_dim = boundary_matrix.get_max_dim(); + + std::vector< index > lowest_one_lookup( nr_columns, -1 ); + std::vector < column_type > column_type( nr_columns, GLOBAL ); + std::vector< char > is_active( nr_columns, false ); + + const index chunk_size = omp_get_max_threads() == 1 ? (index)sqrt( (double)nr_columns ) : nr_columns / omp_get_max_threads(); + + std::vector< index > chunk_boundaries; + for( index cur_boundary = 0; cur_boundary < nr_columns; cur_boundary += chunk_size ) + chunk_boundaries.push_back( cur_boundary ); + chunk_boundaries.push_back( nr_columns ); + + for( dimension cur_dim = max_dim; cur_dim >= 1; cur_dim-- ) { + // Phase 1: Reduce chunks locally -- 1st pass + #pragma omp parallel for schedule( guided, 1 ) + for( index chunk_id = 0; chunk_id < (index)chunk_boundaries.size() - 1; chunk_id++ ) + _local_chunk_reduction( boundary_matrix, lowest_one_lookup, column_type, cur_dim, + chunk_boundaries[ chunk_id ], chunk_boundaries[ chunk_id + 1 ], chunk_boundaries[ chunk_id ] ); + boundary_matrix.sync(); + + // Phase 1: Reduce chunks locally -- 2nd pass + #pragma omp parallel for schedule( guided, 1 ) + for( index chunk_id = 1; chunk_id < (index)chunk_boundaries.size( ) - 1; chunk_id++ ) + _local_chunk_reduction( boundary_matrix, lowest_one_lookup, column_type, cur_dim, + chunk_boundaries[ chunk_id ], chunk_boundaries[ chunk_id + 1 ], chunk_boundaries[ chunk_id - 1 ] ); + boundary_matrix.sync( ); + } + + // get global columns + std::vector< index > global_columns; + for( index cur_col_idx = 0; cur_col_idx < nr_columns; cur_col_idx++ ) + if( column_type[ cur_col_idx ] == GLOBAL ) + global_columns.push_back( cur_col_idx ); + + // get active columns + #pragma omp parallel for + for( index idx = 0; idx < (index)global_columns.size(); idx++ ) + is_active[ global_columns[ idx ] ] = true; + _get_active_columns( boundary_matrix, lowest_one_lookup, column_type, global_columns, is_active ); + + // Phase 2+3: Simplify columns and reduce them + for( dimension cur_dim = max_dim; cur_dim >= 1; cur_dim-- ) { + // Phase 2: Simplify columns + std::vector< index > temp_col; + #pragma omp parallel for schedule( guided, 1 ), private( temp_col ) + for( index idx = 0; idx < (index)global_columns.size(); idx++ ) + if( boundary_matrix.get_dim( global_columns[ idx ] ) == cur_dim ) + _global_column_simplification( global_columns[ idx ], boundary_matrix, lowest_one_lookup, column_type, is_active, temp_col ); + boundary_matrix.sync(); + + // Phase 3: Reduce columns + for( index idx = 0; idx < (index)global_columns.size(); idx++ ) { + index cur_col = global_columns[ idx ]; + if( boundary_matrix.get_dim( cur_col ) == cur_dim && column_type[ cur_col ] == GLOBAL ) { + index lowest_one = boundary_matrix.get_max_index( cur_col ); + while( lowest_one != -1 && lowest_one_lookup[ lowest_one ] != -1 ) { + boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); + lowest_one = boundary_matrix.get_max_index( cur_col ); + } + if( lowest_one != -1 ) { + lowest_one_lookup[ lowest_one ] = cur_col; + boundary_matrix.clear( lowest_one ); + } + boundary_matrix.finalize( cur_col ); + } + } + } + + boundary_matrix.sync(); + } + + protected: + template< typename Representation > + void _local_chunk_reduction( boundary_matrix< Representation >& boundary_matrix + , std::vector& lowest_one_lookup + , std::vector< column_type >& column_type + , const dimension cur_dim + , const index chunk_begin + , const index chunk_end + , const index row_begin ) { + + for( index cur_col = chunk_begin; cur_col < chunk_end; cur_col++ ) { + if( column_type[ cur_col ] == GLOBAL && boundary_matrix.get_dim( cur_col ) == cur_dim ) { + index lowest_one = boundary_matrix.get_max_index( cur_col ); + while( lowest_one != -1 && lowest_one >= row_begin && lowest_one_lookup[ lowest_one ] != -1 ) { + boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); + lowest_one = boundary_matrix.get_max_index( cur_col ); + } + if( lowest_one >= row_begin ) { + lowest_one_lookup[ lowest_one ] = cur_col; + column_type[ cur_col ] = LOCAL_NEGATIVE; + column_type[ lowest_one ] = LOCAL_POSITIVE; + boundary_matrix.clear( lowest_one ); + boundary_matrix.finalize( cur_col ); + } + } + } + } + + template< typename Representation > + void _get_active_columns( const boundary_matrix< Representation >& boundary_matrix + , const std::vector< index >& lowest_one_lookup + , const std::vector< column_type >& column_type + , const std::vector< index >& global_columns + , std::vector< char >& is_active ) { + + const index nr_columns = boundary_matrix.get_num_cols(); + std::vector< char > finished( nr_columns, false ); + + std::vector< std::pair < index, index > > stack; + std::vector< index > cur_col_values; + #pragma omp parallel for schedule( guided, 1 ), private( stack, cur_col_values ) + for( index idx = 0; idx < (index)global_columns.size(); idx++ ) { + bool pop_next = false; + index start_col = global_columns[ idx ]; + stack.push_back( std::pair< index, index >( start_col, -1 ) ); + while( !stack.empty() ) { + index cur_col = stack.back().first; + index prev_col = stack.back().second; + if( pop_next ) { + stack.pop_back(); + pop_next = false; + if( prev_col != -1 ) { + if( is_active[ cur_col ] ) { + is_active[ prev_col ] = true; + } + if( prev_col == stack.back().first ) { + finished[ prev_col ] = true; + pop_next = true; + } + } + } else { + pop_next = true; + boundary_matrix.get_col( cur_col, cur_col_values ); + for( index idx = 0; idx < (index) cur_col_values.size(); idx++ ) { + index cur_row = cur_col_values[ idx ]; + if( ( column_type[ cur_row ] == GLOBAL ) ) { + is_active[ cur_col ] = true; + } else if( column_type[ cur_row ] == LOCAL_POSITIVE ) { + index next_col = lowest_one_lookup[ cur_row ]; + if( next_col != cur_col && !finished[ cur_col ] ) { + stack.push_back( std::make_pair( next_col, cur_col ) ); + pop_next = false; + } + } + } + } + } + } + } + + template< typename Representation > + void _global_column_simplification( const index col_idx + , boundary_matrix< Representation >& boundary_matrix + , const std::vector< index >& lowest_one_lookup + , const std::vector< column_type >& column_type + , const std::vector< char >& is_active + , std::vector< index >& temp_col ) + { + temp_col.clear(); + while( !boundary_matrix.is_empty( col_idx ) ) { + index cur_row = boundary_matrix.get_max_index( col_idx ); + switch( column_type[ cur_row ] ) { + case GLOBAL: + temp_col.push_back( cur_row ); + boundary_matrix.remove_max( col_idx ); + break; + case LOCAL_NEGATIVE: + boundary_matrix.remove_max( col_idx ); + break; + case LOCAL_POSITIVE: + if( is_active[ lowest_one_lookup[ cur_row ] ] ) + boundary_matrix.add_to( lowest_one_lookup[ cur_row ], col_idx ); + else + boundary_matrix.remove_max( col_idx ); + break; + } + } + std::reverse( temp_col.begin(), temp_col.end() ); + boundary_matrix.set_col( col_idx, temp_col ); + } + }; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/row_reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/row_reduction.h new file mode 100644 index 0000000000..cdd1a8fd18 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/row_reduction.h @@ -0,0 +1,56 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + class row_reduction { + public: + template< typename Representation > + void operator() ( boundary_matrix< Representation >& boundary_matrix ) { + + const index nr_columns = boundary_matrix.get_num_cols(); + std::vector< std::vector< index > > lowest_one_lookup( nr_columns ); + + for( index cur_col = nr_columns - 1; cur_col >= 0; cur_col-- ) { + if( !boundary_matrix.is_empty( cur_col ) ) + lowest_one_lookup[ boundary_matrix.get_max_index( cur_col ) ].push_back( cur_col ); + + if( !lowest_one_lookup[ cur_col ].empty() ) { + boundary_matrix.clear( cur_col ); + boundary_matrix.finalize( cur_col ); + std::vector< index >& cols_with_cur_lowest = lowest_one_lookup[ cur_col ]; + index source = *min_element( cols_with_cur_lowest.begin(), cols_with_cur_lowest.end() ); + for( index idx = 0; idx < (index)cols_with_cur_lowest.size(); idx++ ) { + index target = cols_with_cur_lowest[ idx ]; + if( target != source && !boundary_matrix.is_empty( target ) ) { + boundary_matrix.add_to( source, target ); + if( !boundary_matrix.is_empty( target ) ) { + index lowest_one_of_target = boundary_matrix.get_max_index( target ); + lowest_one_lookup[ lowest_one_of_target ].push_back( target ); + } + } + } + } + } + } + }; +} \ No newline at end of file diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/spectral_sequence_reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/spectral_sequence_reduction.h new file mode 100644 index 0000000000..bf442e6089 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/spectral_sequence_reduction.h @@ -0,0 +1,80 @@ +/* Copyright 2013 IST Austria + Contributed by: Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + class spectral_sequence_reduction { + public: + template< typename Representation > + void operator () ( boundary_matrix< Representation >& boundary_matrix ) { + + const index nr_columns = boundary_matrix.get_num_cols(); + std::vector< index > lowest_one_lookup( nr_columns, -1 ); + + //const index num_stripes = (index) sqrt( (double)nr_columns ); + const index num_stripes = omp_get_max_threads(); + + index block_size = ( nr_columns % num_stripes == 0 ) ? nr_columns / num_stripes : block_size = nr_columns / num_stripes + 1; + + std::vector< std::vector< index > > unreduced_cols_cur_pass( num_stripes ); + std::vector< std::vector< index > > unreduced_cols_next_pass( num_stripes ); + + for( index cur_dim = boundary_matrix.get_max_dim(); cur_dim >= 1 ; cur_dim-- ) { + #pragma omp parallel for schedule( guided, 1 ) + for( index cur_stripe = 0; cur_stripe < num_stripes; cur_stripe++ ) { + index col_begin = cur_stripe * block_size; + index col_end = std::min( (cur_stripe+1) * block_size, nr_columns ); + for( index cur_col = col_begin; cur_col < col_end; cur_col++ ) + if( boundary_matrix.get_dim( cur_col ) == cur_dim && boundary_matrix.get_max_index( cur_col ) != -1 ) + unreduced_cols_cur_pass[ cur_stripe ].push_back( cur_col ); + } + for( index cur_pass = 0; cur_pass < num_stripes; cur_pass++ ) { + boundary_matrix.sync(); + #pragma omp parallel for schedule( guided, 1 ) + for( int cur_stripe = 0; cur_stripe < num_stripes; cur_stripe++ ) { + index row_begin = (cur_stripe - cur_pass) * block_size; + index row_end = row_begin + block_size; + unreduced_cols_next_pass[ cur_stripe ].clear(); + for( index idx = 0; idx < (index)unreduced_cols_cur_pass[ cur_stripe ].size(); idx++ ) { + index cur_col = unreduced_cols_cur_pass[ cur_stripe ][ idx ]; + index lowest_one = boundary_matrix.get_max_index( cur_col ); + while( lowest_one != -1 && lowest_one >= row_begin && lowest_one < row_end && lowest_one_lookup[ lowest_one ] != -1 ) { + boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); + lowest_one = boundary_matrix.get_max_index( cur_col ); + } + if( lowest_one != -1 ) { + if( lowest_one >= row_begin && lowest_one < row_end ) { + lowest_one_lookup[ lowest_one ] = cur_col; + boundary_matrix.clear( lowest_one ); + boundary_matrix.finalize( cur_col ); + } else { + unreduced_cols_next_pass[ cur_stripe ].push_back( cur_col ); + } + } + } + unreduced_cols_next_pass[ cur_stripe ].swap( unreduced_cols_cur_pass[ cur_stripe ] ); + } + } + } + } + }; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/standard_reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/standard_reduction.h new file mode 100644 index 0000000000..e490a5e0d1 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/standard_reduction.h @@ -0,0 +1,47 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + class standard_reduction { + public: + template< typename Representation > + void operator() ( boundary_matrix< Representation >& boundary_matrix ) { + + const index nr_columns = boundary_matrix.get_num_cols(); + std::vector< index > lowest_one_lookup( nr_columns, -1 ); + + for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { + index lowest_one = boundary_matrix.get_max_index( cur_col ); + while( lowest_one != -1 && lowest_one_lookup[ lowest_one ] != -1 ) { + boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); + lowest_one = boundary_matrix.get_max_index( cur_col ); + } + if( lowest_one != -1 ) { + lowest_one_lookup[ lowest_one ] = cur_col; + } + boundary_matrix.finalize( cur_col ); + } + } + }; +} + diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/twist_reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/twist_reduction.h new file mode 100644 index 0000000000..2357df0256 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/twist_reduction.h @@ -0,0 +1,51 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + class twist_reduction { + public: + template< typename Representation > + void operator () ( boundary_matrix< Representation >& boundary_matrix ) { + + const index nr_columns = boundary_matrix.get_num_cols(); + std::vector< index > lowest_one_lookup( nr_columns, -1 ); + + for( index cur_dim = boundary_matrix.get_max_dim(); cur_dim >= 1 ; cur_dim-- ) { + for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { + if( boundary_matrix.get_dim( cur_col ) == cur_dim ) { + index lowest_one = boundary_matrix.get_max_index( cur_col ); + while( lowest_one != -1 && lowest_one_lookup[ lowest_one ] != -1 ) { + boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); + lowest_one = boundary_matrix.get_max_index( cur_col ); + } + if( lowest_one != -1 ) { + lowest_one_lookup[ lowest_one ] = cur_col; + boundary_matrix.clear( lowest_one ); + } + boundary_matrix.finalize( cur_col ); + } + } + } + } + }; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/boundary_matrix.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/boundary_matrix.h new file mode 100644 index 0000000000..10c66cca13 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/boundary_matrix.h @@ -0,0 +1,343 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +// interface class for the main data structure -- implementations of the interface can be found in ./representations +namespace phat { + template< class Representation = bit_tree_pivot_column > + class boundary_matrix + { + + protected: + Representation rep; + + // interface functions -- actual implementation and complexity depends on chosen @Representation template + public: + // get overall number of columns in boundary_matrix + index get_num_cols() const { return rep._get_num_cols(); } + + // set overall number of columns in boundary_matrix + void set_num_cols( index nr_of_columns ) { rep._set_num_cols( nr_of_columns ); } + + // get dimension of given index + dimension get_dim( index idx ) const { return rep._get_dim( idx ); } + + // set dimension of given index + void set_dim( index idx, dimension dim ) { rep._set_dim( idx, dim ); } + + // replaces content of @col with boundary of given index + void get_col( index idx, column& col ) const { col.clear(); rep._get_col( idx, col ); } + + // set column @idx to the values contained in @col + void set_col( index idx, const column& col ) { rep._set_col( idx, col ); } + + // true iff boundary of given column is empty + bool is_empty( index idx ) const { return rep._is_empty( idx ); } + + // largest index of given column (new name for lowestOne()) -- NOT thread-safe + index get_max_index( index idx ) const { return rep._get_max_index( idx ); } + + // removes maximal index from given column + void remove_max( index idx ) { rep._remove_max( idx ); } + + // adds column @source to column @target' + void add_to( index source, index target ) { rep._add_to( source, target ); } + + // clears given column + void clear( index idx ) { rep._clear( idx ); } + + // finalizes given column + void finalize( index idx ) { rep._finalize( idx ); } + + // syncronizes all internal data structures -- has to be called before and after any multithreaded access! + void sync() { rep._sync(); } + + // info functions -- independent of chosen 'Representation' + public: + // maximal dimension + dimension get_max_dim() const { + dimension cur_max_dim = 0; + for( index idx = 0; idx < get_num_cols(); idx++ ) + cur_max_dim = get_dim( idx ) > cur_max_dim ? get_dim( idx ) : cur_max_dim; + return cur_max_dim; + } + + // number of nonzero rows for given column @idx + index get_num_rows( index idx ) const { + column cur_col; + get_col( idx, cur_col ); + return cur_col.size(); + } + + // maximal number of nonzero rows of all columns + index get_max_col_entries() const { + index max_col_entries = -1; + const index nr_of_columns = get_num_cols(); + for( index idx = 0; idx < nr_of_columns; idx++ ) + max_col_entries = get_num_rows( idx ) > max_col_entries ? get_num_rows( idx ) : max_col_entries; + return max_col_entries; + } + + // maximal number of nonzero cols of all rows + index get_max_row_entries() const { + size_t max_row_entries = 0; + const index nr_of_columns = get_num_cols(); + std::vector< std::vector< index > > transposed_matrix( nr_of_columns ); + column temp_col; + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { + get_col( cur_col, temp_col ); + for( index idx = 0; idx < (index)temp_col.size(); idx++) + transposed_matrix[ temp_col[ idx ] ].push_back( cur_col ); + } + for( index idx = 0; idx < nr_of_columns; idx++ ) + max_row_entries = transposed_matrix[ idx ].size() > max_row_entries ? transposed_matrix[ idx ].size() : max_row_entries; + return max_row_entries; + } + + // overall number of entries in the matrix + index get_num_entries() const { + index number_of_nonzero_entries = 0; + const index nr_of_columns = get_num_cols(); + for( index idx = 0; idx < nr_of_columns; idx++ ) + number_of_nonzero_entries += get_num_rows( idx ); + return number_of_nonzero_entries; + } + + // operators / constructors + public: + boundary_matrix() {}; + + template< class OtherRepresentation > + boundary_matrix( const boundary_matrix< OtherRepresentation >& other ) { + *this = other; + } + + template< typename OtherRepresentation > + bool operator==( const boundary_matrix< OtherRepresentation >& other_boundary_matrix ) const { + const index number_of_columns = this->get_num_cols(); + + if( number_of_columns != other_boundary_matrix.get_num_cols() ) + return false; + + column temp_col; + column other_temp_col; + for( index idx = 0; idx < number_of_columns; idx++ ) { + this->get_col( idx, temp_col ); + other_boundary_matrix.get_col( idx, other_temp_col ); + if( temp_col != other_temp_col || this->get_dim( idx ) != other_boundary_matrix.get_dim( idx ) ) + return false; + } + return true; + } + + template< typename OtherRepresentation > + bool operator!=( const boundary_matrix< OtherRepresentation >& other_boundary_matrix ) const { + return !( *this == other_boundary_matrix ); + } + + template< typename OtherRepresentation > + boundary_matrix< Representation >& operator=( const boundary_matrix< OtherRepresentation >& other ) + { + const index nr_of_columns = other.get_num_cols(); + this->set_num_cols( nr_of_columns ); + column temp_col; + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { + this->set_dim( cur_col, other.get_dim( cur_col ) ); + other.get_col( cur_col, temp_col ); + this->set_col( cur_col, temp_col ); + } + + // by convention, always return *this + return *this; + } + + // I/O -- independent of chosen 'Representation' + public: + + // initializes boundary_matrix from (vector, vector) pair -- untested + template< typename index_type, typename dimemsion_type > + void load_vector_vector( const std::vector< std::vector< index_type > >& input_matrix, const std::vector< dimemsion_type >& input_dims ) { + const index nr_of_columns = (index)input_matrix.size(); + this->set_num_cols( nr_of_columns ); + column temp_col; + #pragma omp parallel for private( temp_col ) + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { + this->set_dim( cur_col, (dimension)input_dims[ cur_col ] ); + + index num_rows = input_matrix[ cur_col ].size(); + temp_col.resize( num_rows ); + for( index cur_row = 0; cur_row < num_rows; cur_row++ ) + temp_col[ cur_row ] = (index)input_matrix[ cur_col ][ cur_row ]; + this->set_col( cur_col, temp_col ); + } + } + + template< typename index_type, typename dimemsion_type > + void save_vector_vector( std::vector< std::vector< index_type > >& output_matrix, std::vector< dimemsion_type >& output_dims ) { + const index nr_of_columns = get_num_cols(); + output_matrix.resize( nr_of_columns ); + output_dims.resize( nr_of_columns ); + column temp_col; + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { + output_dims[ cur_col ] = (dimemsion_type)get_dim( cur_col ); + get_col( cur_col, temp_col ); + index num_rows = temp_col.size(); + output_matrix[ cur_col ].clear(); + output_matrix[ cur_col ].resize( num_rows ); + for( index cur_row = 0; cur_row < num_rows; cur_row++ ) + output_matrix[ cur_col ][ cur_row ] = (index_type)temp_col[ cur_row ]; + } + } + + + // Loads the boundary_matrix from given file in ascii format + // Format: each line represents a column, first number is dimension, other numbers are the content of the column. + // Ignores empty lines and lines starting with a '#'. + bool load_ascii( std::string filename ) { + // first count number of columns: + std::string cur_line; + std::ifstream dummy( filename .c_str() ); + if( dummy.fail() ) + return false; + + index number_of_columns = 0; + while( getline( dummy, cur_line ) ) { + cur_line.erase(cur_line.find_last_not_of(" \t\n\r\f\v") + 1); + if( cur_line != "" && cur_line[ 0 ] != '#' ) + number_of_columns++; + + } + this->set_num_cols( number_of_columns ); + dummy.close(); + + std::ifstream input_stream( filename.c_str() ); + if( input_stream.fail() ) + return false; + + column temp_col; + index cur_col = -1; + while( getline( input_stream, cur_line ) ) { + cur_line.erase(cur_line.find_last_not_of(" \t\n\r\f\v") + 1); + if( cur_line != "" && cur_line[ 0 ] != '#' ) { + cur_col++; + std::stringstream ss( cur_line ); + + int64_t temp_dim; + ss >> temp_dim; + this->set_dim( cur_col, (dimension) temp_dim ); + + int64_t temp_index; + temp_col.clear(); + while( ss.good() ) { + ss >> temp_index; + temp_col.push_back( (index)temp_index ); + } + std::sort( temp_col.begin(), temp_col.end() ); + this->set_col( cur_col, temp_col ); + } + } + + input_stream.close(); + return true; + } + + // Saves the boundary_matrix to given file in ascii format + // Format: each line represents a column, first number is dimension, other numbers are the content of the column + bool save_ascii( std::string filename ) { + std::ofstream output_stream( filename.c_str() ); + if( output_stream.fail() ) + return false; + + const index nr_columns = this->get_num_cols(); + column tempCol; + for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { + output_stream << (int64_t)this->get_dim( cur_col ); + this->get_col( cur_col, tempCol ); + for( index cur_row_idx = 0; cur_row_idx < (index)tempCol.size(); cur_row_idx++ ) + output_stream << " " << tempCol[ cur_row_idx ]; + output_stream << std::endl; + } + + output_stream.close(); + return true; + } + + // Loads boundary_matrix from given file + // Format: nr_columns % dim1 % N1 % row1 row2 % ...% rowN1 % dim2 % N2 % ... + bool load_binary( std::string filename ) + { + std::ifstream input_stream( filename.c_str( ), std::ios_base::binary | std::ios_base::in ); + if( input_stream.fail( ) ) + return false; + + int64_t nr_columns; + input_stream.read( (char*)&nr_columns, sizeof( int64_t ) ); + this->set_num_cols( (index)nr_columns ); + + column temp_col; + for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { + int64_t cur_dim; + input_stream.read( (char*)&cur_dim, sizeof( int64_t ) ); + this->set_dim( cur_col, (dimension)cur_dim ); + int64_t nr_rows; + input_stream.read( (char*)&nr_rows, sizeof( int64_t ) ); + temp_col.resize( ( std::size_t )nr_rows ); + for( index idx = 0; idx < nr_rows; idx++ ) { + int64_t cur_row; + input_stream.read( (char*)&cur_row, sizeof( int64_t ) ); + temp_col[ idx ] = (index)cur_row; + } + this->set_col( cur_col, temp_col ); + } + + input_stream.close( ); + return true; + } + + // Saves the boundary_matrix to given file in binary format + // Format: nr_columns % dim1 % N1 % row1 row2 % ...% rowN1 % dim2 % N2 % ... + bool save_binary( std::string filename ) + { + std::ofstream output_stream( filename.c_str( ), std::ios_base::binary | std::ios_base::out ); + if( output_stream.fail( ) ) + return false; + + const int64_t nr_columns = this->get_num_cols( ); + output_stream.write( (char*)&nr_columns, sizeof( int64_t ) ); + column tempCol; + for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { + int64_t cur_dim = this->get_dim( cur_col ); + output_stream.write( (char*)&cur_dim, sizeof( int64_t ) ); + this->get_col( cur_col, tempCol ); + int64_t cur_nr_rows = tempCol.size( ); + output_stream.write( (char*)&cur_nr_rows, sizeof( int64_t ) ); + for( index cur_row_idx = 0; cur_row_idx < (index)tempCol.size( ); cur_row_idx++ ) { + int64_t cur_row = tempCol[ cur_row_idx ]; + output_stream.write( (char*)&cur_row, sizeof( int64_t ) ); + } + } + + output_stream.close( ); + return true; + } + }; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/compute_persistence_pairs.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/compute_persistence_pairs.h new file mode 100644 index 0000000000..48be65c28e --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/compute_persistence_pairs.h @@ -0,0 +1,128 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include +#include +#include + +namespace phat { + // Extracts persistence pairs in separate dimensions from a reduced + // boundary matrix representing ``double`` filtration. The pairs + // give persistent relative homology of the pair of filtrations. + // TODO: Use it with standard reduction algorithm (no template option). + template< typename ReductionAlgorithm, typename Representation > + void compute_relative_persistence_pairs(std::vector& pairs, boundary_matrix& boundary_matrix, const std::map& L) { + ReductionAlgorithm reduce; + reduce(boundary_matrix); + std::map free; + std::map invL; + for (std::map::const_iterator it = L.begin(); it != L.end(); ++it) { invL[it->second] = it->first; } + for (std::vector::iterator it = pairs.begin(); it != pairs.end(); ++it) { it->clear(); } + for (index idx = 0; idx < boundary_matrix.get_num_cols(); ++idx) { + int dimension = boundary_matrix.get_dim(idx); + if (L.find(idx) != L.end()) { ++dimension; } + free[idx] = true; + if (!boundary_matrix.is_empty(idx)) { + index birth = boundary_matrix.get_max_index(idx); + index death = idx; + pairs[dimension-1].append_pair(birth, death); + free[birth] = false; + free[death] = false; + } else { + // This is an L-simplex and a (dimension+1)-dimensional cycle + if (L.find(idx) != L.end()) { + assert(dimension < pairs.size()); + pairs[dimension].append_pair(idx, -1); + } + } + } + for (std::map::iterator it = free.begin(); it != free.end(); ++it) { + if (it->second) { + int dimension = boundary_matrix.get_dim(it->first); + if (invL.find(it->first) == invL.end() && L.find(it->first) == L.end()) { + assert(dimension < pairs.size()); + pairs[dimension].append_pair(it->first, -1); + } + } + } + } + + // Extracts persistence pairs in separate dimensions; expects a d-dimensional vector of persistent_pairs + template< typename ReductionAlgorithm, typename Representation > + void compute_persistence_pairs(std::vector& pairs, boundary_matrix& boundary_matrix) { + ReductionAlgorithm reduce; + reduce(boundary_matrix); + std::map free; + for (std::vector::iterator it = pairs.begin(); it != pairs.end(); ++it) { it->clear(); } + for (index idx = 0; idx < boundary_matrix.get_num_cols(); ++idx) { + int dimension = boundary_matrix.get_dim(idx); + free[idx] = true; + if (!boundary_matrix.is_empty(idx)) { + index birth = boundary_matrix.get_max_index(idx); + index death = idx; + pairs[dimension-1].append_pair(birth, death); + // Cannot be of the form (a, infinity) + free[birth] = false; + free[death] = false; + } + } + for (std::map::iterator it = free.begin(); it != free.end(); ++it) { + if (it->second) { + int dimension = boundary_matrix.get_dim(it->first); + pairs[dimension].append_pair(it->first, -1); + } + } + } + + template< typename ReductionAlgorithm, typename Representation > + void compute_persistence_pairs( persistence_pairs& pairs, boundary_matrix< Representation >& boundary_matrix ) { + ReductionAlgorithm reduce; + reduce( boundary_matrix ); + pairs.clear(); + for( index idx = 0; idx < boundary_matrix.get_num_cols(); idx++ ) { + if( !boundary_matrix.is_empty( idx ) ) { + index birth = boundary_matrix.get_max_index( idx ); + index death = idx; + pairs.append_pair( birth, death ); + } + } + } + + template< typename ReductionAlgorithm, typename Representation > + void compute_persistence_pairs_dualized( persistence_pairs& pairs, boundary_matrix< Representation >& boundary_matrix ) { + + dualize( boundary_matrix ); + compute_persistence_pairs< ReductionAlgorithm >( pairs, boundary_matrix ); + dualize_persistence_pairs( pairs, boundary_matrix.get_num_cols() ); + } + + template< typename Representation > + void compute_persistence_pairs( persistence_pairs& pairs, boundary_matrix< Representation >& boundary_matrix ) { + phat::compute_persistence_pairs< twist_reduction >( pairs, boundary_matrix ); + } + + + template< typename Representation > + void compute_persistence_pairs_dualized( persistence_pairs& pairs, boundary_matrix< Representation >& boundary_matrix ) { + compute_persistence_pairs_dualized< twist_reduction >( pairs, boundary_matrix ); + } + +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/dualize.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/dualize.h new file mode 100644 index 0000000000..3ffedf875f --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/dualize.h @@ -0,0 +1,74 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include +#include + + +namespace phat { + template< typename Representation > + void dualize( boundary_matrix< Representation >& boundary_matrix ) { + + std::vector< dimension > dual_dims; + std::vector< std::vector< index > > dual_matrix; + + index nr_of_columns = boundary_matrix.get_num_cols(); + dual_matrix.resize( nr_of_columns ); + dual_dims.resize( nr_of_columns ); + + std::vector< index > dual_sizes( nr_of_columns, 0 ); + + column temp_col; + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { + boundary_matrix.get_col( cur_col, temp_col ); + for( index idx = 0; idx < (index)temp_col.size(); idx++) + dual_sizes[ nr_of_columns - 1 - temp_col[ idx ] ]++; + } + + #pragma omp parallel for + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) + dual_matrix[cur_col].reserve(dual_sizes[cur_col]); + + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { + boundary_matrix.get_col( cur_col, temp_col ); + for( index idx = 0; idx < (index)temp_col.size(); idx++) + dual_matrix[ nr_of_columns - 1 - temp_col[ idx ] ].push_back( nr_of_columns - 1 - cur_col ); + } + + const dimension max_dim = boundary_matrix.get_max_dim(); + #pragma omp parallel for + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) + dual_dims[ nr_of_columns - 1 - cur_col ] = max_dim - boundary_matrix.get_dim( cur_col ); + + #pragma omp parallel for + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) + std::reverse( dual_matrix[ cur_col ].begin(), dual_matrix[ cur_col ].end() ); + + boundary_matrix.load_vector_vector( dual_matrix, dual_dims ); + } + + void dualize_persistence_pairs( persistence_pairs& pairs, const index n ) { + for (index i = 0; i < pairs.get_num_pairs(); ++i) { + std::pair< index, index > pair = pairs.get_pair( i ); + pairs.set_pair( i , n - 1 - pair.second, n - 1 - pair.first); + } + } +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/misc.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/misc.h new file mode 100644 index 0000000000..fb5c07acb0 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/misc.h @@ -0,0 +1,75 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +// STL includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// VS2008 and below unfortunately do not support stdint.h +#if defined(_MSC_VER)&& _MSC_VER < 1600 + typedef __int8 int8_t; + typedef unsigned __int8 uint8_t; + typedef __int16 int16_t; + typedef unsigned __int16 uint16_t; + typedef __int32 int32_t; + typedef unsigned __int32 uint32_t; + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; +#else + #include +#endif + +// basic types. index can be changed to int32_t to save memory on small instances +namespace phat { + typedef int64_t index; + typedef int8_t dimension; + typedef std::vector< index > column; +} + +// OpenMP (proxy) functions +#if defined _OPENMP + #include +#else + #define omp_get_thread_num() 0 + #define omp_get_max_threads() 1 + #define omp_get_num_threads() 1 + void omp_set_num_threads( int ) {}; + #include + #define omp_get_wtime() (float)clock() / (float)CLOCKS_PER_SEC +#endif + +#include + + + diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/thread_local_storage.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/thread_local_storage.h new file mode 100644 index 0000000000..d0b5332bc1 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/thread_local_storage.h @@ -0,0 +1,52 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include + +// should ideally be equal to the cache line size of the CPU +#define PHAT_TLS_SPACING_FACTOR 64 + +// ThreadLocalStorage with some spacing to avoid "false sharing" (see wikipedia) +template< typename T > +class thread_local_storage +{ +public: + + thread_local_storage() : per_thread_storage( omp_get_max_threads() * PHAT_TLS_SPACING_FACTOR ) {}; + + T& operator()() { + return per_thread_storage[ omp_get_thread_num() * PHAT_TLS_SPACING_FACTOR ]; + } + + const T& operator()() const { + return per_thread_storage[ omp_get_thread_num() * PHAT_TLS_SPACING_FACTOR ]; + } + + T& operator[]( int tid ) { + return per_thread_storage[ tid * PHAT_TLS_SPACING_FACTOR ]; + } + + const T& operator[]( int tid ) const { + return per_thread_storage[ tid * PHAT_TLS_SPACING_FACTOR ]; + } + +protected: + std::vector< T > per_thread_storage; +}; diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/persistence_pairs.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/persistence_pairs.h new file mode 100644 index 0000000000..eafc6389e2 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/persistence_pairs.h @@ -0,0 +1,155 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include + +namespace phat { + class persistence_pairs { + + protected: + std::vector< std::pair< index, index > > pairs; + + public: + index get_num_pairs() const { + return (index)pairs.size(); + } + + void append_pair( index birth, index death ) { + pairs.push_back( std::make_pair( birth, death ) ); + } + + std::pair< index, index > get_pair( index idx ) const { + return pairs[ idx ]; + } + + void set_pair( index idx, index birth, index death ) { + pairs[ idx ] = std::make_pair( birth, death ); + } + + void clear() { + pairs.clear(); + } + + void sort() { + std::sort( pairs.begin(), pairs.end() ); + } + + // Loads the persistence pairs from given file in asci format + // Format: nr_pairs % newline % birth1 % death1 % newline % birth2 % death2 % newline ... + bool load_ascii( std::string filename ) { + std::ifstream input_stream( filename.c_str() ); + if( input_stream.fail() ) + return false; + + int64_t nr_pairs; + input_stream >> nr_pairs; + pairs.clear(); + for( index idx = 0; idx < nr_pairs; idx++ ) { + int64_t birth; + input_stream >> birth; + int64_t death; + input_stream >> death; + append_pair( (index)birth, (index)death ); + } + + input_stream.close(); + return true; + } + + // Saves the persistence pairs to given file in binary format + // Format: nr_pairs % newline % birth1 % death1 % newline % birth2 % death2 % newline ... + bool save_ascii( std::string filename ) { + std::ofstream output_stream( filename.c_str() ); + if( output_stream.fail() ) + return false; + + this->sort(); + output_stream << get_num_pairs() << std::endl; + for( std::size_t idx = 0; idx < pairs.size(); idx++ ) { + output_stream << pairs[idx].first << " " << pairs[idx].second << std::endl; + } + + output_stream.close(); + return true; + } + + // Loads the persistence pairs from given file in binary format + // Format: nr_pairs % birth1 % death1 % birth2 % death2 ... + bool load_binary( std::string filename ) { + std::ifstream input_stream( filename.c_str(), std::ios_base::binary | std::ios_base::in ); + if( input_stream.fail() ) + return false; + + int64_t nr_pairs; + input_stream.read( (char*)&nr_pairs, sizeof( int64_t ) ); + for( index idx = 0; idx < nr_pairs; idx++ ) { + int64_t birth; + input_stream.read( (char*)&birth, sizeof( int64_t ) ); + int64_t death; + input_stream.read( (char*)&death, sizeof( int64_t ) ); + append_pair( (index)birth, (index)death ); + } + + input_stream.close(); + return true; + } + + // Saves the persistence pairs to given file in binary format + // Format: nr_pairs % birth1 % death1 % birth2 % death2 ... + bool save_binary( std::string filename ) { + std::ofstream output_stream( filename.c_str(), std::ios_base::binary | std::ios_base::out ); + if( output_stream.fail() ) + return false; + + this->sort(); + int64_t nr_pairs = get_num_pairs(); + output_stream.write( (char*)&nr_pairs, sizeof( int64_t ) ); + for( std::size_t idx = 0; idx < pairs.size(); idx++ ) { + int64_t birth = pairs[ idx ].first; + output_stream.write( (char*)&birth, sizeof( int64_t ) ); + int64_t death = pairs[ idx ].second; + output_stream.write( (char*)&death, sizeof( int64_t ) ); + } + + output_stream.close(); + return true; + } + + bool operator==( persistence_pairs& other_pairs ) { + this->sort(); + other_pairs.sort(); + if( pairs.size() != (std::size_t)other_pairs.get_num_pairs() ) + return false; + + for( index idx = 0; idx < (index)pairs.size(); idx++ ) + if( get_pair( idx ) != other_pairs.get_pair( idx ) ) + return false; + + return true; + } + + bool operator!=( persistence_pairs& other_pairs ) { + return !( *this == other_pairs ); + } + }; + + + +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/abstract_pivot_column.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/abstract_pivot_column.h new file mode 100644 index 0000000000..e16d7a5d13 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/abstract_pivot_column.h @@ -0,0 +1,102 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + + // Note: We could even make the rep generic in the underlying Const representation + // But I cannot imagine that anything else than vector> would + // make sense + template< typename PivotColumn > + class abstract_pivot_column : public vector_vector { + + protected: + typedef vector_vector Base; + typedef PivotColumn pivot_col; + + // For parallization purposes, it could be more than one full column + mutable thread_local_storage< pivot_col > pivot_cols; + mutable thread_local_storage< index > idx_of_pivot_cols; + + pivot_col& get_pivot_col() const { + return pivot_cols(); + } + + bool is_pivot_col( index idx ) const { + return idx_of_pivot_cols() == idx; + } + + void release_pivot_col() { + index idx = idx_of_pivot_cols(); + if( idx != -1 ) { + this->matrix[ idx ].clear(); + pivot_cols().get_col_and_clear( this->matrix[ idx ] ); + } + idx_of_pivot_cols() = -1; + } + + void make_pivot_col( index idx ) { + release_pivot_col(); + idx_of_pivot_cols() = idx; + get_pivot_col().add_col( matrix[ idx ] ); + } + + public: + + void _set_num_cols( index nr_of_cols ) { + #pragma omp parallel for + for( int tid = 0; tid < omp_get_num_threads(); tid++ ) { + pivot_cols[ tid ].init( nr_of_cols ); + idx_of_pivot_cols[ tid ] = -1; + } + Base::_set_num_cols( nr_of_cols ); + } + + void _add_to( index source, index target ) { + if( !is_pivot_col( target ) ) + make_pivot_col( target ); + get_pivot_col().add_col( matrix[source] ); + } + + void _sync() { + #pragma omp parallel for + for( int tid = 0; tid < omp_get_num_threads(); tid++ ) + release_pivot_col(); + } + + void _get_col( index idx, column& col ) const { is_pivot_col( idx ) ? get_pivot_col().get_col( col ) : Base::_get_col( idx, col ); } + + bool _is_empty( index idx ) const { return is_pivot_col( idx ) ? get_pivot_col().is_empty() : Base::_is_empty( idx ); } + + index _get_max_index( index idx ) const { return is_pivot_col( idx ) ? get_pivot_col().get_max_index() : Base::_get_max_index( idx ); } + + void _clear( index idx ) { is_pivot_col( idx ) ? get_pivot_col().clear() : Base::_clear( idx ); } + + void _set_col( index idx, const column& col ) { is_pivot_col( idx ) ? get_pivot_col().set_col( col ) : Base::_set_col( idx, col ); } + + void _remove_max( index idx ) { is_pivot_col( idx ) ? get_pivot_col().remove_max() : Base::_remove_max( idx ); } + + void finalize( index idx ) { Base::_finalize( idx ); } + }; +} + + diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/bit_tree_pivot_column.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/bit_tree_pivot_column.h new file mode 100644 index 0000000000..4d48e8853d --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/bit_tree_pivot_column.h @@ -0,0 +1,165 @@ +/* Copyright 2013 IST Austria + Contributed by: Hubert Wagner + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + + // This is a bitset indexed with a 64-ary tree. Each node in the index + // has 64 bits; i-th bit says that the i-th subtree is non-empty. + // Supports practically O(1), inplace, zero-allocation: insert, remove, max_element + // and clear in O(number of ones in the bitset). + // 'add_index' is still the real bottleneck in practice. + class bit_tree_column + { + protected: + + size_t offset; // data[i + offset] = ith block of the data-bitset + typedef uint64_t block_type; + std::vector< block_type > data; + + + size_t debrujin_magic_table[ 64 ]; + + enum { block_size_in_bits = 64 }; + enum { block_shift = 6 }; + + // Some magic: http://graphics.stanford.edu/~seander/bithacks.html + // Gets the position of the rightmost bit of 'x'. 0 means the most significant bit. + // (-x)&x isolates the rightmost bit. + // The whole method is much faster than calling log2i, and very comparable to using ScanBitForward/Reverse intrinsic, + // which should be one CPU instruction, but is not portable. + size_t rightmost_pos( const block_type value ) const { + return 64 - 1 - debrujin_magic_table[ ( (value & (-(int64_t)value) ) * 0x07EDD5E59A4E28C2 ) >> 58 ]; + } + + public: + + void init( index num_cols ) { + int64_t n = 1; // in case of overflow + int64_t bottom_blocks_needed = ( num_cols + block_size_in_bits - 1 ) / block_size_in_bits; + int64_t upper_blocks = 1; + + // How many blocks/nodes of index needed to index the whole bitset? + while( n * block_size_in_bits < bottom_blocks_needed ) { + n *= block_size_in_bits; + upper_blocks += n; + } + + offset = upper_blocks; + data.resize( upper_blocks + bottom_blocks_needed, 0 ); + + std::size_t temp_array[ 64 ] = { + 63, 0, 58, 1, 59, 47, 53, 2, + 60, 39, 48, 27, 54, 33, 42, 3, + 61, 51, 37, 40, 49, 18, 28, 20, + 55, 30, 34, 11, 43, 14, 22, 4, + 62, 57, 46, 52, 38, 26, 32, 41, + 50, 36, 17, 19, 29, 10, 13, 21, + 56, 45, 25, 31, 35, 16, 9, 12, + 44, 24, 15, 8, 23, 7, 6, 5 }; + + std::copy( &temp_array[ 0 ], &temp_array[ 64 ], &debrujin_magic_table[ 0 ] ); + } + + index get_max_index() const { + if( !data[ 0 ] ) + return -1; + + size_t n = 0; + size_t newn = 0; + size_t index = 0; + while( newn < data.size() ) { + n = newn; + index = rightmost_pos( data[ n ] ); + newn = ( n << block_shift ) + index + 1; + } + + return ( ( n - offset ) << block_shift ) + index; + } + + bool is_empty() const { + return data[ 0 ] == 0; + } + + void add_index( const size_t entry ) { + const block_type ONE = 1; + const block_type block_modulo_mask = ( ONE << block_shift ) - 1; + size_t index_in_level = entry >> block_shift; + size_t address = index_in_level + offset; + size_t index_in_block = entry & block_modulo_mask; + + block_type mask = ( ONE << ( block_size_in_bits - index_in_block - 1 ) ); + + data[ address ] ^= mask; + + // Check if we reached the root. Also, if anyone else was in this block, we don't need to update the path up. + while( address && !( data[ address ] & ~mask ) ) { + index_in_block = index_in_level & block_modulo_mask; + index_in_level >>= block_shift; + --address; + address >>= block_shift; + mask = ( ONE << ( block_size_in_bits - index_in_block - 1 ) ); + data[ address ] ^= mask; + } + } + + void get_col_and_clear( column &out ) { + index mx = this->get_max_index(); + while( mx != -1 ) { + out.push_back( mx ); + add_index( mx ); + mx = this->get_max_index(); + } + + std::reverse( out.begin(), out.end() ); + } + + void add_col(const column &col) { + for( size_t i = 0; i < col.size(); ++i ) + add_index(col[i]); + } + + void clear() { + index mx = this->get_max_index(); + while( mx != -1 ) { + add_index( mx ); + mx = this->get_max_index(); + } + } + + void remove_max() { + add_index( get_max_index() ); + } + + void set_col( const column& col ) { + clear(); + add_col( col ); + } + + void get_col( column& col ) { + get_col_and_clear( col ); + add_col( col ); + } + }; + + typedef abstract_pivot_column bit_tree_pivot_column; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/full_pivot_column.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/full_pivot_column.h new file mode 100644 index 0000000000..c2e9e3c574 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/full_pivot_column.h @@ -0,0 +1,100 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + class full_column { + + protected: + std::priority_queue< index > history; + std::vector< char > is_in_history; + std::vector< char > col_bit_field; + + public: + void init( const index total_size ) { + col_bit_field.resize( total_size, false ); + is_in_history.resize( total_size, false ); + } + + void add_col( const column& col ) { + for( index idx = 0; idx < (index) col.size(); idx++ ) { + add_index( col[ idx ] ); + } + } + + void add_index( const index idx ) { + if( !is_in_history[ idx ] ) { + history.push( idx ); + is_in_history[ idx ] = true; + } + + col_bit_field[ idx ] = !col_bit_field[ idx ]; + } + + index get_max_index() { + while( history.size() > 0 ) { + index topIndex = history.top(); + if( col_bit_field[ topIndex ] ) { + return topIndex; + } else { + history.pop(); + is_in_history[ topIndex ] = false; + } + } + + return -1; + } + + void get_col_and_clear( column& col ) { + while( !is_empty() ) { + col.push_back( get_max_index() ); + add_index( get_max_index() ); + } + std::reverse( col.begin(), col.end() ); + } + + bool is_empty() { + return (get_max_index() == -1); + } + + void clear() { + while( !is_empty() ) + add_index( get_max_index() ); + } + + void remove_max() { + add_index( get_max_index() ); + } + + void set_col( const column& col ) { + clear(); + add_col( col ); + } + + void get_col( column& col ) { + get_col_and_clear( col ); + add_col( col ); + } + }; + + typedef abstract_pivot_column< full_column > full_pivot_column; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/heap_pivot_column.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/heap_pivot_column.h new file mode 100644 index 0000000000..33cd07b40d --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/heap_pivot_column.h @@ -0,0 +1,126 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + class heap_column { + + protected: + std::priority_queue< index > data; + + column temp_col; + index inserts_since_last_prune; + + void prune() + { + temp_col.clear( ); + index max_index = pop_max_index( ); + while( max_index != -1 ) { + temp_col.push_back( max_index ); + max_index = pop_max_index( ); + } + + for( index idx = 0; idx < (index)temp_col.size( ); idx++ ) + data.push( temp_col[ idx ] ); + + inserts_since_last_prune = 0; + } + + index pop_max_index() + { + if( data.empty( ) ) + return -1; + else { + index max_element = data.top( ); + data.pop(); + while( !data.empty( ) && data.top( ) == max_element ) { + data.pop( ); + if( data.empty( ) ) + return -1; + else { + max_element = data.top( ); + data.pop( ); + } + } + return max_element; + } + } + + public: + void init( const index total_size ) { + inserts_since_last_prune = 0; + clear(); + } + + void add_col( const column& col ) { + for( index idx = 0; idx < (index) col.size(); idx++ ) + data.push( col[ idx ] ); + inserts_since_last_prune += col.size( ); + if( 2 * inserts_since_last_prune >( index ) data.size( ) ) + prune(); + } + + index get_max_index() { + index max_element = pop_max_index( ); + if( max_element == -1 ) + return -1; + else { + data.push( max_element ); + return max_element; + } + } + + void get_col_and_clear( column& col ) { + col.clear(); + index max_index = pop_max_index( ); + while( max_index != -1 ) { + col.push_back( max_index ); + max_index = pop_max_index( ); + } + std::reverse( col.begin(), col.end() ); + } + + bool is_empty() { + return get_max_index() == -1; + } + + void clear() { + data = std::priority_queue< index >(); + } + + void remove_max() { + pop_max_index(); + } + + void set_col( const column& col ) { + clear(); + add_col( col ); + } + + void get_col( column& col ) { + get_col_and_clear( col ); + add_col( col ); + } + }; + + typedef abstract_pivot_column< heap_column > heap_pivot_column; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/sparse_pivot_column.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/sparse_pivot_column.h new file mode 100644 index 0000000000..390fd91a99 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/sparse_pivot_column.h @@ -0,0 +1,79 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + class sparse_column { + + protected: + std::set< index > data; + + void add_index( const index idx ) { + std::pair< std::set< index >::iterator, bool > result = data.insert( idx ); + if( result.second == false ) + data.erase( result.first ); + } + + public: + void init( const index total_size ) { + data.clear(); + } + + void add_col( const column& col ) { + for( index idx = 0; idx < (index) col.size(); idx++ ) + add_index( col[ idx ] ); + } + + index get_max_index() { + return data.empty() ? -1 : *data.rbegin(); + } + + void get_col_and_clear( column& col ) { + col.assign( data.begin(), data.end() ); + data.clear(); + } + + bool is_empty() { + return data.empty(); + } + + void clear() { + data.clear(); + } + + void remove_max() { + add_index( get_max_index() ); + } + + void set_col( const column& col ) { + clear(); + add_col( col ); + } + + void get_col( column& col ) { + get_col_and_clear( col ); + add_col( col ); + } + }; + + typedef abstract_pivot_column< sparse_column > sparse_pivot_column; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_heap.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_heap.h new file mode 100644 index 0000000000..db0420ff23 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_heap.h @@ -0,0 +1,170 @@ +/* Copyright 2013 IST Austria +Contributed by: Jan Reininghaus + +This file is part of PHAT. + +PHAT is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +PHAT 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with PHAT. If not, see . */ + +#pragma once + +#include + +namespace phat { + class vector_heap { + + protected: + std::vector< dimension > dims; + std::vector< column > matrix; + + std::vector< index > inserts_since_last_prune; + + mutable thread_local_storage< column > temp_column_buffer; + + protected: + void _prune( index idx ) + { + column& col = matrix[ idx ]; + column& temp_col = temp_column_buffer(); + temp_col.clear(); + index max_index = _pop_max_index( col ); + while( max_index != -1 ) { + temp_col.push_back( max_index ); + max_index = _pop_max_index( col ); + } + col = temp_col; + std::reverse( col.begin( ), col.end( ) ); + std::make_heap( col.begin( ), col.end( ) ); + inserts_since_last_prune[ idx ] = 0; + } + + index _pop_max_index( index idx ) + { + return _pop_max_index( matrix[ idx ] ); + } + + index _pop_max_index( column& col ) const + { + if( col.empty( ) ) + return -1; + else { + index max_element = col.front( ); + std::pop_heap( col.begin( ), col.end( ) ); + col.pop_back( ); + while( !col.empty( ) && col.front( ) == max_element ) { + std::pop_heap( col.begin( ), col.end( ) ); + col.pop_back( ); + if( col.empty( ) ) + return -1; + else { + max_element = col.front( ); + std::pop_heap( col.begin( ), col.end( ) ); + col.pop_back( ); + } + } + return max_element; + } + } + + public: + // overall number of cells in boundary_matrix + index _get_num_cols( ) const + { + return (index)matrix.size( ); + } + void _set_num_cols( index nr_of_columns ) + { + dims.resize( nr_of_columns ); + matrix.resize( nr_of_columns ); + inserts_since_last_prune.assign( nr_of_columns, 0 ); + } + + // dimension of given index + dimension _get_dim( index idx ) const + { + return dims[ idx ]; + } + void _set_dim( index idx, dimension dim ) + { + dims[ idx ] = dim; + } + + // replaces(!) content of 'col' with boundary of given index + void _get_col( index idx, column& col ) const + { + temp_column_buffer( ) = matrix[ idx ]; + + index max_index = _pop_max_index( temp_column_buffer() ); + while( max_index != -1 ) { + col.push_back( max_index ); + max_index = _pop_max_index( temp_column_buffer( ) ); + } + std::reverse( col.begin( ), col.end( ) ); + } + void _set_col( index idx, const column& col ) + { + matrix[ idx ] = col; + std::make_heap( matrix[ idx ].begin( ), matrix[ idx ].end( ) ); + } + + // true iff boundary of given idx is empty + bool _is_empty( index idx ) const + { + return _get_max_index( idx ) == -1; + } + + // largest row index of given column idx (new name for lowestOne()) + index _get_max_index( index idx ) const + { + column& col = const_cast< column& >( matrix[ idx ] ); + index max_element = _pop_max_index( col ); + col.push_back( max_element ); + std::push_heap( col.begin( ), col.end( ) ); + return max_element; + } + + // removes the maximal index of a column + void _remove_max( index idx ) + { + _pop_max_index( idx ); + } + + // clears given column + void _clear( index idx ) + { + matrix[ idx ].clear( ); + } + + // syncronizes all data structures (essential for openmp stuff) + void _sync( ) {} + + // adds column 'source' to column 'target' + void _add_to( index source, index target ) + { + for( index idx = 0; idx < (index)matrix[ source ].size( ); idx++ ) { + matrix[ target ].push_back( matrix[ source ][ idx ] ); + std::push_heap( matrix[ target ].begin(), matrix[ target ].end() ); + } + inserts_since_last_prune[ target ] += matrix[ source ].size(); + + if( 2 * inserts_since_last_prune[ target ] > ( index )matrix[ target ].size() ) + _prune( target ); + } + + // finalizes given column + void _finalize( index idx ) { + _prune( idx ); + } + + }; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_list.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_list.h new file mode 100644 index 0000000000..ca0b5b8e79 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_list.h @@ -0,0 +1,101 @@ +/* Copyright 2013 IST Austria + Contributed by: Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include + +namespace phat { + class vector_list { + + protected: + std::vector< dimension > dims; + std::vector< std::list< index > > matrix; + + public: + // overall number of cells in boundary_matrix + index _get_num_cols() const { + return (index)matrix.size(); + } + void _set_num_cols( index nr_of_columns ) { + dims.resize( nr_of_columns ); + matrix.resize( nr_of_columns ); + } + + // dimension of given index + dimension _get_dim( index idx ) const { + return dims[ idx ]; + } + void _set_dim( index idx, dimension dim ) { + dims[ idx ] = dim; + } + + // replaces(!) content of 'col' with boundary of given index + void _get_col( index idx, column& col ) const { + col.clear(); + col.reserve( matrix[idx].size() ); + std::copy (matrix[idx].begin(), matrix[idx].end(), std::back_inserter(col) ); + } + + void _set_col( index idx, const column& col ) { + matrix[ idx ].clear(); + matrix[ idx ].resize( col.size() ); + std::copy (col.begin(), col.end(), matrix[ idx ].begin() ); + } + + // true iff boundary of given idx is empty + bool _is_empty( index idx ) const { + return matrix[ idx ].empty(); + } + + // largest row index of given column idx (new name for lowestOne()) + index _get_max_index( index idx ) const { + return matrix[ idx ].empty() ? -1 : *matrix[ idx ].rbegin(); + } + + // removes the maximal index of a column + void _remove_max( index idx ) { + std::list< index >::iterator it = matrix[ idx ].end(); + it--; + matrix[ idx ].erase( it ); + } + + // clears given column + void _clear( index idx ) { + matrix[ idx ].clear(); + } + + // syncronizes all data structures (essential for openmp stuff) + void _sync() {} + + // adds column 'source' to column 'target' + void _add_to( index source, index target ) { + std::list< index >& source_col = matrix[ source ]; + std::list< index >& target_col = matrix[ target ]; + std::list< index > temp_col; + target_col.swap( temp_col ); + std::set_symmetric_difference( temp_col.begin(), temp_col.end(), + source_col.begin(), source_col.end(), + std::back_inserter( target_col ) ); + } + + // finalizes given column + void _finalize( index idx ) { + } + }; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_set.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_set.h new file mode 100644 index 0000000000..6878a270c0 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_set.h @@ -0,0 +1,99 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include + +namespace phat { + class vector_set { + + protected: + std::vector< dimension > dims; + std::vector< std::set< index > > matrix; + + public: + // overall number of cells in boundary_matrix + index _get_num_cols() const { + return (index)matrix.size(); + } + void _set_num_cols( index nr_of_columns ) { + dims.resize( nr_of_columns ); + matrix.resize( nr_of_columns ); + } + + // dimension of given index + dimension _get_dim( index idx ) const { + return dims[ idx ]; + } + void _set_dim( index idx, dimension dim ) { + dims[ idx ] = dim; + } + + // replaces(!) content of 'col' with boundary of given index + void _get_col( index idx, column& col ) const { + col.clear(); + col.reserve( matrix[idx].size() ); + std::copy (matrix[idx].begin(), matrix[idx].end(), std::back_inserter(col) ); + } + void _set_col( index idx, const column& col ) { + matrix[ idx ].clear(); + matrix[ idx ].insert( col.begin(), col.end() ); + } + + // true iff boundary of given idx is empty + bool _is_empty( index idx ) const { + return matrix[ idx ].empty(); + } + + // largest row index of given column idx (new name for lowestOne()) + index _get_max_index( index idx ) const { + return matrix[ idx ].empty() ? -1 : *matrix[ idx ].rbegin(); + } + + // removes the maximal index of a column + void _remove_max( index idx ) { + std::set< index >::iterator it = matrix[ idx ].end(); + it--; + matrix[ idx ].erase( it ); + } + + // clears given column + void _clear( index idx ) { + matrix[ idx ].clear(); + } + + // syncronizes all data structures (essential for openmp stuff) + void _sync() {} + + // adds column 'source' to column 'target' + void _add_to( index source, index target ) { + for( std::set< index >::iterator it = matrix[ source ].begin(); it != matrix[ source ].end(); it++ ) { + std::set< index >& col = matrix[ target ]; + std::pair< std::set< index >::iterator, bool > result = col.insert( *it ); + if( !result.second ) + col.erase( result.first ); + } + } + + // finalizes given column + void _finalize( index idx ) { + } + + }; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_vector.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_vector.h new file mode 100644 index 0000000000..f111d6b572 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_vector.h @@ -0,0 +1,107 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include + +namespace phat { + class vector_vector { + + protected: + std::vector< dimension > dims; + std::vector< column > matrix; + + thread_local_storage< column > temp_column_buffer; + + public: + // overall number of cells in boundary_matrix + index _get_num_cols() const { + return (index)matrix.size(); + } + void _set_num_cols( index nr_of_columns ) { + dims.resize( nr_of_columns ); + matrix.resize( nr_of_columns ); + } + + // dimension of given index + dimension _get_dim( index idx ) const { + return dims[ idx ]; + } + void _set_dim( index idx, dimension dim ) { + dims[ idx ] = dim; + } + + // replaces(!) content of 'col' with boundary of given index + void _get_col( index idx, column& col ) const { + col = matrix[ idx ]; + } + void _set_col( index idx, const column& col ) { + matrix[ idx ] = col; + } + + // true iff boundary of given idx is empty + bool _is_empty( index idx ) const { + return matrix[ idx ].empty(); + } + + // largest row index of given column idx (new name for lowestOne()) + index _get_max_index( index idx ) const { + return matrix[ idx ].empty() ? -1 : matrix[ idx ].back(); + } + + // removes the maximal index of a column + void _remove_max( index idx ) { + matrix[ idx ].pop_back(); + } + + // clears given column + void _clear( index idx ) { + matrix[ idx ].clear(); + } + + // syncronizes all data structures (essential for openmp stuff) + void _sync() {} + + // adds column 'source' to column 'target' + void _add_to( index source, index target ) { + column& source_col = matrix[ source ]; + column& target_col = matrix[ target ]; + column& temp_col = temp_column_buffer(); + + + size_t new_size = source_col.size() + target_col.size(); + + if (new_size > temp_col.size()) temp_col.resize(new_size); + + std::vector::iterator col_end = std::set_symmetric_difference( target_col.begin(), target_col.end(), + source_col.begin(), source_col.end(), + temp_col.begin() ); + temp_col.erase(col_end, temp_col.end()); + + + target_col.swap(temp_col); + } + + // finalizes given column + void _finalize( index idx ) { + column& col = matrix[ idx ]; + column(col.begin(), col.end()).swap(col); + } + }; +} diff --git a/src/Zigzag_persistence/example/CMakeLists.txt b/src/Zigzag_persistence/example/CMakeLists.txt index f0b424423d..446456e0e5 100644 --- a/src/Zigzag_persistence/example/CMakeLists.txt +++ b/src/Zigzag_persistence/example/CMakeLists.txt @@ -13,6 +13,12 @@ endif() file(COPY "zigzag_filtration_example.txt" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) add_test(NAME Zigzag_persistence_example_zzfiltration_from_file COMMAND $ "${CMAKE_CURRENT_BINARY_DIR}/zigzag_filtration_example.txt") +add_executable ( Zigzag_persistence_example_oscillating_rips_persistence example_oscillating_rips_persistence.cpp ) +if(TARGET TBB::tbb) + target_link_libraries(Zigzag_persistence_example_oscillating_rips_persistence TBB::tbb) +endif() +add_test(NAME Zigzag_persistence_example_oscillating_rips_persistence COMMAND $ "2" "3" "5" "10") + add_executable ( comp comparison_for_tests.cpp ) if(TARGET TBB::tbb) target_link_libraries(comp TBB::tbb) diff --git a/src/Zigzag_persistence/example/comparison_for_tests.cpp b/src/Zigzag_persistence/example/comparison_for_tests.cpp index bca280febf..2b55bdd648 100644 --- a/src/Zigzag_persistence/example/comparison_for_tests.cpp +++ b/src/Zigzag_persistence/example/comparison_for_tests.cpp @@ -23,13 +23,15 @@ #include #include #include +#include using ST = Gudhi::Simplex_tree; using Filtration_value = ST::Filtration_value; +using Simplex_handle = ST::Simplex_handle; using Square = Gudhi::zigzag_persistence::Square_root_edge_modifier; using ZE = Gudhi::zigzag_persistence::Zigzag_edge; using ORE = Gudhi::zigzag_persistence::Oscillating_rips_edge_range; -using ORi = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range; +using OR = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range; using ORv = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range::iterator>; using Point = std::vector; @@ -41,6 +43,11 @@ void print_points(const std::vector& points) { std::cout << "\n"; } +void print_res(const std::tuple& t, ST& st){ + for (auto v : st.simplex_vertex_range(std::get<0>(t))) std::cout << v << " "; + std::cout << " -- " << std::get<1>(t) << ", " << std::get<2>(t) << "\n"; +} + std::vector build_point_cloud(unsigned int numberOfPoints, int seed) { std::vector finalPoints; std::set points; @@ -220,9 +227,9 @@ void test_edges_timings(const std::vector& points, double nu, double mu, } void test_edges(const std::vector& points, double nu, double mu) { - // ORE::Order_policy p = ORE::Order_policy::FARTHEST_POINT_ORDERING; + ORE::Order_policy p = ORE::Order_policy::FARTHEST_POINT_ORDERING; // ORE::Order_policy p = ORE::Order_policy::ALREADY_ORDERED; - ORE::Order_policy p = ORE::Order_policy::RANDOM_POINT_ORDERING; +// ORE::Order_policy p = ORE::Order_policy::RANDOM_POINT_ORDERING; // test_edges_comp(points, nu, mu, p); // test_edges_canonical_sort(points, nu, mu, p); @@ -235,14 +242,14 @@ void test_simplices_print(const std::vector& points, double nu, double mu auto start = ORE::begin(nu, mu, points, Gudhi::Euclidean_distance(), p); auto end = ORE::end(); - for (auto& t : ORi::get_iterator_range(start, end, st, maxDim)) { - for (auto v : st.simplex_vertex_range(std::get<0>(t))) std::cout << v << " "; - std::cout << " -- " << std::get<1>(t) << ", " << std::get<2>(t) << "\n"; + for (auto& t : OR::get_iterator_range(start, end, st, maxDim)) { + print_res(t, st); } } -void test_simplices_comp(const std::vector& points, double nu, double mu, int maxDim, ORE::Order_policy p){ - ST st; +void test_simplices_comp(const std::vector& points, double nu, double mu, int maxDim, ORE::Order_policy p) { + ST stIt; + ST stVec; auto startEIt = ORE::begin(nu, mu, points, Gudhi::Euclidean_distance(), p); auto endEIt = ORE::end(); @@ -250,25 +257,109 @@ void test_simplices_comp(const std::vector& points, double nu, double mu, auto startEVec = vec.begin(); auto endEVec = vec.end(); - auto startIt = ORi::begin(startEIt, endEIt, st, maxDim); - auto endIt = ORi::end(); - auto rangeVec = ORv::get_iterator_range(startEVec, endEVec, st, maxDim); + auto startIt = OR::begin(startEIt, endEIt, stIt, maxDim); + auto endIt = OR::end(); + auto rangeVec = ORv::get_iterator_range(startEVec, endEVec, stVec, maxDim); auto startVec = rangeVec.begin(); auto endVec = rangeVec.end(); - for (; startIt != endIt && startVec != endVec; ++startIt,++startVec) { - if () - for (auto v : st.simplex_vertex_range(std::get<0>(t))) std::cout << v << " "; - std::cout << " -- " << std::get<1>(t) << ", " << std::get<2>(t) << "\n"; + unsigned int i = 0; + for (; startIt != endIt && startVec != endVec; ++startIt, ++startVec) { + const auto& tIt = *startIt; + const auto& tVec = *startVec; + if (std::get<1>(tIt) != std::get<1>(tVec) || std::get<2>(tIt) != std::get<2>(tVec)) { + std::cout << "[" << i << "] Different:\n"; + print_res(tIt, stIt); + print_res(tVec, stVec); + return; + } + auto verIt = stIt.simplex_vertex_range(std::get<0>(tIt)); + auto verVec = stVec.simplex_vertex_range(std::get<0>(tVec)); + auto itIt = verIt.begin(); + auto itVec = verVec.begin(); + while (itIt != verIt.end() && itVec != verVec.end()) { + if (*itIt != *itVec) { + std::cout << "[" << i << "] Different:\n"; + print_res(tIt, stIt); + print_res(tVec, stVec); + return; + } + ++itIt; + ++itVec; + } + if (itIt != verIt.end() || itVec != verVec.end()) { + std::cout << "[" << i << "] Different:\n"; + print_res(tIt, stIt); + print_res(tVec, stVec); + return; + } + ++i; + } +} + +void test_simplices_timings(const std::vector& points, double nu, double mu, int maxDim, ORE::Order_policy p) { +// bool dir = false; + { + Gudhi::Clock time1("Vector version"); + ST st; + unsigned int i = 0; + auto vec = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); + // std::cout << vec.size() << "\n"; + auto start = vec.begin(); + auto end = vec.end(); + for ([[maybe_unused]] const auto& t : ORv::get_iterator_range(start, end, st, maxDim)) { + // if (dir != std::get<2>(t)){ + // dir = !dir; + // std::cout << st.num_simplices() << "\n"; + // } + ++i; + } + std::cout << "Number of iterations: " << i << "\n"; + time1.end(); + std::cout << time1; } + + { + Gudhi::Clock time2("Iterator version"); + ST st; + unsigned int i = 0; + auto start = ORE::begin(nu, mu, points, Gudhi::Euclidean_distance(), p); + auto end = ORE::end(); + for ([[maybe_unused]] const auto& t : OR::get_iterator_range(start, end, st, maxDim)) { + // if (dir != std::get<2>(t)){ + // dir = !dir; + // std::cout << st.num_simplices() << "\n"; + // } + ++i; + } + std::cout << "Number of iterations: " << i << "\n"; + time2.end(); + std::cout << time2; + } + +// { +// Gudhi::Clock time1("Vector version"); +// ST st; +// unsigned int i = 0; +// auto vec = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); +// auto start = vec.begin(); +// auto end = vec.end(); +// for ([[maybe_unused]] const auto& t : ORv::get_iterator_range(start, end, st, maxDim)) { +// ++i; +// } +// std::cout << i << "\n"; +// time1.end(); +// std::cout << time1; +// } } void test_simplices(const std::vector& points, double nu, double mu, int maxDim) { ORE::Order_policy p = ORE::Order_policy::FARTHEST_POINT_ORDERING; // ORE::Order_policy p = ORE::Order_policy::ALREADY_ORDERED; - // ORE::Order_policy p = ORE::Order_policy::RANDOM_POINT_ORDERING; +// ORE::Order_policy p = ORE::Order_policy::RANDOM_POINT_ORDERING; // test_simplices_print(points, nu, mu, maxDim, p); - test_simplices_comp(points, nu, mu, maxDim, p); +// test_simplices_comp(points, nu, mu, maxDim, p); + test_simplices_timings(points, nu, mu, maxDim, p); } int main(int argc, char* const argv[]) { @@ -286,13 +377,16 @@ int main(int argc, char* const argv[]) { if (argc == 6) seed = std::stoi(argv[5]); std::cout << "nu, mu: " << nu << ", " << mu << "\n"; + std::cout << "max dimension: " << maxDim << "\n"; std::cout << "number of points: " << numberOfPoints << "\n"; std::cout << "seed: " << seed << "\n"; std::vector points = build_point_cloud(numberOfPoints, seed); // test_edges(points, nu, mu); - test_simplices(points, nu, mu, maxDim); +// test_simplices(points, nu, mu, maxDim); + + Gudhi::zigzag_persistence::compute_oscillating_rips_persistence(points, nu, mu, maxDim); return 0; } diff --git a/src/Zigzag_persistence/example/example_oscillating_rips_persistence.cpp b/src/Zigzag_persistence/example/example_oscillating_rips_persistence.cpp new file mode 100644 index 0000000000..4e027fdb43 --- /dev/null +++ b/src/Zigzag_persistence/example/example_oscillating_rips_persistence.cpp @@ -0,0 +1,95 @@ +/* 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): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#include +#include + +#include +#include +#include + +using ST = Gudhi::Simplex_tree; +using Filtration_value = ST::Filtration_value; +using Simplex_handle = ST::Simplex_handle; +using Point = std::vector; +using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; +using Barcode = std::vector; + +void print_barcode(const Barcode& bars) { + std::clog << "Resulting barcode:" << std::endl; + for (auto& bar : bars) { + std::clog << "[" << bar.dim() << "] " << bar.birth() << " - "; + if (bar.death() == std::numeric_limits::infinity()) { + std::clog << "inf"; + } else { + std::clog << bar.death(); + } + std::clog << std::endl; + } +} + +void print_points(const std::vector& points) { + std::clog << "Number of points: " << points.size() << std::endl; + for (const Point& p : points) { + std::clog << "(" << p[0] << ", " << p[1] << ")" << std::endl; + } + std::clog << std::endl; +} + +std::vector build_point_cloud(unsigned int numberOfPoints, int seed) { + std::vector finalPoints; + std::set points; + std::random_device dev; + std::mt19937 rng(dev()); + if (seed > -1) rng.seed(seed); + std::uniform_real_distribution dist(0, 10); + + for (unsigned int i = 0; i < numberOfPoints; ++i) { + auto res = points.insert({dist(rng), dist(rng)}); + while (!res.second) { + res = points.insert({dist(rng), dist(rng)}); + } + finalPoints.push_back(*res.first); + } + + print_points(finalPoints); + + return finalPoints; +} + +int main(int argc, char* const argv[]) { + if (argc != 5 && argc != 6) { + std::clog << "Usage: ./comp nu mu max_dim nomberOfPoints [seed]" << std::endl; + return 0; + } + + double nu = std::stod(argv[1]); + double mu = std::stod(argv[2]); + int maxDim = std::stoi(argv[3]); + unsigned int numberOfPoints = std::stoi(argv[4]); + int seed = -1; + + if (argc == 6) seed = std::stoi(argv[5]); + + std::clog << "nu, mu: " << nu << ", " << mu << std::endl; + std::clog << "max dimension: " << maxDim << std::endl; + std::clog << "number of points: " << numberOfPoints << std::endl; + std::clog << "seed: " << seed << std::endl; + + std::clog << "********** Computing " << numberOfPoints << " random points in a 10 x 10 square" << std::endl; + std::vector points = build_point_cloud(numberOfPoints, seed); + + std::clog << "********** Computing oscillating rips filtration and persistence" << std::endl; + //with default templates and parameters. See documention for more information. + Barcode res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence(points, nu, mu, maxDim); + print_barcode(res); + + return 0; +} diff --git a/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h b/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h new file mode 100644 index 0000000000..5382b1c68a --- /dev/null +++ b/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h @@ -0,0 +1,93 @@ +/* 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): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +/** + * @file Oscillating_rips_persistence.h + * @author Hannah Schreiber + * @brief Contains the implementation of the @ref Gudhi::zigzag_persistence::compute_oscillating_rips_persistence + * method. + */ + +#ifndef OSCILLATING_RIPS_PERSISTENCE_H_ +#define OSCILLATING_RIPS_PERSISTENCE_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace Gudhi { +namespace zigzag_persistence { + +enum Edge_range_type { VECTOR, BOOST_RANGE }; + +template , + typename std::conditional< + edge_range_type == Edge_range_type::BOOST_RANGE, + Oscillating_rips_edge_range::Filtration_value>::Oscillating_rips_edge_iterator, + std::vector::Filtration_value> >::iterator>::type>, + typename ZigzagPersistenceOptions = Gudhi::persistence_matrix::Zigzag_options<> > +std::vector, + ZigzagPersistenceOptions>::filtration_value_interval> +compute_oscillating_rips_persistence( + const PointRange& points, double nu, double mu, int maxDim, + typename Oscillating_rips_edge_range< + Gudhi::Simplex_tree::Filtration_value>::Order_policy p = + Oscillating_rips_edge_range::Filtration_value>::Order_policy::FARTHEST_POINT_ORDERING) +{ + using ST = Gudhi::Simplex_tree; + using Filtration_value = ST::Filtration_value; + using EdgeRange = Oscillating_rips_edge_range; + + Zigzag_persistence zp; + ST& st = zp.get_complex(); + + if constexpr (edge_range_type == Edge_range_type::BOOST_RANGE) { + auto start = EdgeRange::begin(nu, mu, points, Gudhi::Euclidean_distance(), p); + auto end = EdgeRange::end(); + for (const auto& t : OscillatingRipsSimplexRange::get_iterator_range(start, end, st, maxDim)) { + // std::cout << (std::get<2>(t) ? "in" : "out") << ": "; + // for (auto v : st.simplex_vertex_range(std::get<0>(t))) std::cout << v << " "; + // std::cout << " - "; + // for (auto sh : st.boundary_simplex_range(std::get<0>(t))) std::cout << st.key(sh) << " "; + // std::cout << "\n"; + if (std::get<2>(t)) + zp.insert_simplex_handle(std::get<0>(t)); + else + zp.remove_simplex_handle(std::get<0>(t), std::get<1>(t)); + } + } else { + auto vec = EdgeRange::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); + auto start = vec.begin(); + auto end = vec.end(); + for (const auto& t : OscillatingRipsSimplexRange::get_iterator_range(start, end, st, maxDim)) { + if (std::get<2>(t)) + zp.insert_simplex_handle(std::get<0>(t)); + else + zp.remove_simplex_handle(std::get<0>(t), std::get<1>(t)); + } + } + + return zp.get_persistence_diagram(); +} + +} // namespace zigzag_persistence + +} // namespace Gudhi + +#endif // OSCILLATING_RIPS_PERSISTENCE_H_ diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h index a263aede5a..42fa03e7a0 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h @@ -19,13 +19,6 @@ #ifndef ZIGZAG_PERSISTENCE_H_ #define ZIGZAG_PERSISTENCE_H_ -#include -#include -#include -#include -#include -#include - #include #include #include @@ -33,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -398,6 +390,62 @@ class Zigzag_persistence } } + /** + * @brief Updates the zigzag persistence diagram after the given simplex was ``manually'' inserted in the complex + * obtained with @ref get_complex. + * + * @param simplex Simplex handle of the simplex having been inserted. A filtration value and a key must be assigned + * to it. The key must correspond to the ``arrow number'' inserting the simplex, i.e., its position in the filtration. + * + * @warning Other simplices can have been inserted in the complex before calling this method, there will simply not + * be token into account as long as the method is not called for them. It it just important that a key was assigned + * to the given simplex handle which corresponds to the position of the simplex in the filtration and + * that this method is called in order. + * @warning No simplex given to this method should be removed before calling @ref remove_simplex_handle. + * @warning Do not modify the key even after calling this method, not before calling @ref remove_simplex_handle. + */ + void insert_simplex_handle(Simplex_handle simplex) { + if (dim_max_ != -1 && cpx_.dimension(simplex) > dim_max_) return; + + ++num_arrow_; + Filtration_value filtration_value = cpx_.filtration(simplex); + + if (filtration_value != previous_filtration_value_) // check whether the filt value has changed + { // consecutive pairs (i,f), (j,f') mean simplices of index k in [i,j-1] have + previous_filtration_value_ = filtration_value; // filtration value f + filtration_values_.emplace_back(num_arrow_, previous_filtration_value_); + } + + _process_forward_arrow(simplex); + } + + /** + * @brief Updates the zigzag persistence diagram with the intention that the given simplex will be removed + * by the caller afterwards from the complex obtained with @ref get_complex. + * + * @param simplex Simplex handle of the simplex that will be removed. + * @param filtration_value Filtration value corresponding to the removal of the given simplex. The filtration value + * stored inside the handle does not need to be updated. + * + * @warning Do not remove simplices before calling this method on them (at least if those were token into account + * with @ref insert_simplex_handle or @ref insert_simplex). + * @warning The associated key should not have been modified since calling @ref insert_simplex_handle + * or @ref insert_simplex. + */ + void remove_simplex_handle(Simplex_handle simplex, Filtration_value filtration_value) { + if (dim_max_ != -1 && cpx_.dimension(simplex) > dim_max_) return; + + ++num_arrow_; + + if (filtration_value != previous_filtration_value_) // check whether the filt value has changed + { // consecutive pairs (i,f), (j,f') mean simplices of index k in [i,j-1] have + previous_filtration_value_ = filtration_value; // filtration value f + filtration_values_.emplace_back(num_arrow_, previous_filtration_value_); + } + + _process_backward_arrow(simplex); + } + /** * @brief Returns the ``index persistence diagram'' of the current filtration, that is, the pairs of atomic arrow * numbers corresponding to a birth-death pair. Does not contain points at infinity, only the cycle classes which @@ -492,11 +540,12 @@ class Zigzag_persistence /** * @brief Returns a reference to the complex storing the simplices. * A simplex is added in a call of @ref insert_simplex and is removed in a call of @ref remove_simplex. - * @warning The complex is not const for now for technical reasons, but DO NOT modify it. + * The complex is allowed to be modified by the caller only under certain restrictions. + * See @ref insert_simplex_handle and @ref remove_simplex_handle. * * @return Reference to the complex. */ - ZigzagFilteredComplex& get_complex() const{ + ZigzagFilteredComplex& get_complex(){ return cpx_; } diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h index 446aeb54ca..973352a678 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h @@ -8,6 +8,16 @@ * - YYYY/MM Author: Description of the modification */ + /** + * @file oscillating_rips_iterators.h + * @author Clément Maria, Hannah Schreiber + * @brief Contains the implementation of the @ref Gudhi::zigzag_persistence::Zigzag_edge class, + * @ref Gudhi::zigzag_persistence::Identity_edge_modifier class, + * @ref Gudhi::zigzag_persistence::Square_root_edge_modifier class, + * @ref Gudhi::zigzag_persistence::Oscillating_rips_edge_range class and + * @ref Gudhi::zigzag_persistence::Oscillating_rips_simplex_range class. + */ + #ifndef ZIGZAG_OSCILLATING_RIPS_ITERATORS_H_ #define ZIGZAG_OSCILLATING_RIPS_ITERATORS_H_ @@ -331,12 +341,12 @@ class Oscillating_rips_edge_range { }; template - static std::vector > compute_vector_range(Filtration_value nu, - Filtration_value mu, - const PointRange& points, - DistanceFunction&& distance, - Order_policy orderPolicy) - { + static std::vector > compute_vector_range( + Filtration_value nu, + Filtration_value mu, + const PointRange& points, + DistanceFunction&& distance, + Order_policy orderPolicy = Order_policy::FARTHEST_POINT_ORDERING) { std::vector > edgeFiltration; std::vector epsilonValues; std::vector > > distanceMatrix; @@ -395,7 +405,7 @@ class Oscillating_rips_edge_range { Filtration_value mu, const PointRange& points, DistanceFunction&& distance, - Order_policy orderPolicy) + Order_policy orderPolicy = Order_policy::FARTHEST_POINT_ORDERING) { return boost::iterator_range( Oscillating_rips_edge_iterator(nu, mu, points, distance, orderPolicy), Oscillating_rips_edge_iterator()); @@ -408,7 +418,7 @@ class Oscillating_rips_edge_range { Filtration_value mu, const PointRange& points, DistanceFunction&& distance, - Order_policy orderPolicy) + Order_policy orderPolicy = Order_policy::FARTHEST_POINT_ORDERING) { return Oscillating_rips_edge_iterator(nu, mu, points, distance, orderPolicy); } @@ -858,9 +868,16 @@ class Oscillating_rips_simplex_range { std::get<0>(currentArrow_) = currentSimplices_[currentSimplexIndex_]; std::get<1>(currentArrow_) = complex_->filtration(currentSimplices_[currentSimplexIndex_]); std::get<2>(currentArrow_) = currentDirection_; + + complex_->assign_key(currentSimplices_[currentSimplexIndex_], 0); } - Oscillating_rips_iterator() : complex_(nullptr), currentSimplexIndex_(0), maxDimension_(0) {} + Oscillating_rips_iterator() + : complex_(nullptr), + currentSimplexIndex_(0), + currentDirection_(true), + maxDimension_(0), + currentArrowNumber_(0) {} private: friend class boost::iterator_core_access; @@ -909,6 +926,8 @@ class Oscillating_rips_simplex_range { } void increment() { + if (!currentDirection_) complex_->remove_maximal_simplex(currentSimplices_[currentSimplexIndex_]); + ++currentSimplexIndex_; ++currentArrowNumber_; @@ -918,14 +937,17 @@ class Oscillating_rips_simplex_range { return; } - if (!currentDirection_) { - for (auto& sh : currentSimplices_) complex_->remove_maximal_simplex(sh); - } + // if (!currentDirection_) { + // for (auto& sh : currentSimplices_) complex_->remove_maximal_simplex(sh); + // } currentSimplices_.clear(); auto fil = currentEdgeIt_->get_filtration_value(); currentDirection_ = currentEdgeIt_->get_direction(); + std::get<1>(currentArrow_) = fil; + std::get<2>(currentArrow_) = currentDirection_; + if (currentDirection_) { _update_positive_current_simplices(fil); } else { @@ -935,8 +957,6 @@ class Oscillating_rips_simplex_range { } std::get<0>(currentArrow_) = currentSimplices_[currentSimplexIndex_]; - std::get<1>(currentArrow_) = complex_->filtration(currentSimplices_[currentSimplexIndex_]); - std::get<2>(currentArrow_) = currentDirection_; if (currentDirection_) complex_->assign_key(currentSimplices_[currentSimplexIndex_], currentArrowNumber_); } From 63aad6a55f4ed44255e6b759babb061806f8fdbe Mon Sep 17 00:00:00 2001 From: hschreiber Date: Wed, 9 Aug 2023 18:21:10 +0200 Subject: [PATCH 11/51] doc --- .../Oscillating_rips_comp_benchmark.cpp | 67 ++ .../gudhi/Oscillating_rips_persistence.h | 49 +- .../oscillating_rips_iterators.h | 701 +++++++++++++++--- 3 files changed, 702 insertions(+), 115 deletions(-) diff --git a/src/Zigzag_persistence/benchmark/Oscillating_rips_comp_benchmark.cpp b/src/Zigzag_persistence/benchmark/Oscillating_rips_comp_benchmark.cpp index 5e8f512f61..6385fe8919 100644 --- a/src/Zigzag_persistence/benchmark/Oscillating_rips_comp_benchmark.cpp +++ b/src/Zigzag_persistence/benchmark/Oscillating_rips_comp_benchmark.cpp @@ -45,6 +45,23 @@ using DIndex = typename DZZ::Index; using DChain = dionysus::ChainEntry; using DIChain = dionysus::ChainEntry; +struct Simplex_tree_options_test { + typedef Gudhi::linear_indexing_tag Indexing_tag; + typedef int Vertex_handle; + typedef double Filtration_value; + typedef std::int32_t Simplex_key; + static const bool store_key = true; + static const bool store_filtration = true; + static const bool contiguous_vertices = true; + static const bool link_nodes_by_label = false; + static const bool simplex_handle_strong_validity = false; +}; + +// using ST_std = Gudhi::Simplex_tree; +using ST_std = Gudhi::Simplex_tree; +using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; +using Barcode = std::vector; + std::vector build_point_cloud(unsigned int numberOfPoints, int seed) { std::vector finalPoints; std::set points; @@ -148,6 +165,24 @@ std::vector > compute_with_ return persistence; } +Barcode compute_with_gudhi_v2(const std::vector& points, double nu, double mu, int maxDim) { + ZP zp; + ST st; + + auto start = ORE::begin(nu, mu, points, Gudhi::Euclidean_distance(), ORE::Order_policy::FARTHEST_POINT_ORDERING); + auto end = ORE::end(); + for (const auto& t : OR::get_iterator_range(start, end, st, maxDim)) { + auto r = st.simplex_vertex_range(std::get<0>(t)); + std::vector simplex(r.begin(), r.end()); + if (std::get<2>(t)) + zp.insert_simplex(simplex, std::get<1>(t)); + else + zp.remove_simplex(simplex, std::get<1>(t)); + } + + return zp.get_persistence_diagram(); +} + static void ORP_with_gudhi(benchmark::State& state) { double nu = state.range(0); double mu = state.range(1); @@ -180,6 +215,38 @@ BENCHMARK(ORP_with_gudhi) // benchmark::CreateRange(256, 500, 4)}) // ->Unit(benchmark::kMillisecond); +// static void ORP_with_gudhi_v2(benchmark::State& state) { +// double nu = state.range(0); +// double mu = state.range(1); +// int maxDim = state.range(2); +// unsigned int numberOfPoints = state.range(3); +// int seed = 0; + +// std::vector points = build_point_cloud(numberOfPoints, seed); + +// for (auto _ : state) { +// auto res = compute_with_gudhi_v2(points, nu, mu, maxDim); +// } +// } +// BENCHMARK(ORP_with_gudhi_v2) +// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), +// benchmark::CreateDenseRange(3, 5, 1), +// benchmark::CreateRange(2, 5, 4), +// benchmark::CreateRange(5, 5, 4)}) +// ->Unit(benchmark::kMicrosecond); +// BENCHMARK(ORP_with_gudhi_v2) +// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), +// benchmark::CreateDenseRange(3, 5, 1), +// benchmark::CreateRange(4, 50, 4), +// benchmark::CreateRange(20, 64, 4)}) +// ->Unit(benchmark::kMillisecond); +// // BENCHMARK(ORP_with_gudhi_v2) +// // ->ArgsProduct({benchmark::CreateDenseRange(1, 2, 1), +// // benchmark::CreateDenseRange(3, 4, 1), +// // benchmark::CreateRange(4, 50, 4), +// // benchmark::CreateRange(256, 500, 4)}) +// // ->Unit(benchmark::kMillisecond); + static void ORP_with_dionysus(benchmark::State& state) { double nu = state.range(0); double mu = state.range(1); diff --git a/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h b/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h index 5382b1c68a..93bab9be02 100644 --- a/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h @@ -18,19 +18,57 @@ #ifndef OSCILLATING_RIPS_PERSISTENCE_H_ #define OSCILLATING_RIPS_PERSISTENCE_H_ -#include #include #include #include #include #include -#include namespace Gudhi { namespace zigzag_persistence { -enum Edge_range_type { VECTOR, BOOST_RANGE }; +/** + * @brief Determines the type of the oscillanting Rips edge range. + * + * The two options are usually quite equivalent, as the base algorithm behind them is the same. + * They mostly differ by their memory managment, so it can be worth it to try both out. + */ +enum Edge_range_type { + VECTOR, /**< The edges are computed all at once and stored in a vector. */ + BOOST_RANGE /**< The edges are computed one by one at each incrementation of + * the range iterator and therefore not stored. */ +}; +/** + * @brief Computes the oscillating Rips filtration based on the given parameters and computes the + * corresponding zigzag barcode. + * + * @ingroup zigzag_persistence + * + * @details It is thought to be a helper function to easily compute oscillating Rips persistence with the + * possibility to switch out a few common options. But additional options exists for the iterators + * (e.g. replacing the Euclidian distance by another one, or using your own epsilon values; see + * the documention of @ref Oscillating_rips_edge_range and @ref Oscillating_rips_simplex_range for + * more information). You can easily create your own method based on this one. + * + * @tparam PointRange Range containing the point cloud. + * @tparam edge_range_type Either Edge_range_type::BOOST_RANGE or Edge_range_type::VECTOR. + * Default value: Edge_range_type::BOOST_RANGE. + * @tparam OscillatingRipsSimplexRange Type of the oscilliating Rips simplex range. + * Default value: Oscillating_rips_simplex_range with templates depending on @p edge_range_type. + * @tparam ZigzagPersistenceOptions Options for the matrix used by the zigzag persistence algorithm. + * Default value: @ref Gudhi::persistence_matrix::Zigzag_options<>. + * @param points Point cloud. + * @param nu Lower multiplicator. + * @param mu Upper multiplicator. + * @param maxDim Maximum dimension to which to expand the Rips complex. If set to -1, there is no limit. + * @param p Order policy for the points. + * Can be either @ref Oscillating_rips_edge_range::Order_policy::FARTHEST_POINT_ORDERING, + * @ref Oscillating_rips_edge_range::Order_policy::ALREADY_ORDERED or + * @ref Oscillating_rips_edge_range::Order_policy::RANDOM_POINT_ORDERING. + * Default value: @ref Oscillating_rips_edge_range::Order_policy::FARTHEST_POINT_ORDERING. + * @return The persistence diagram of the oscillating Rips filtration. + */ template , @@ -61,11 +99,6 @@ compute_oscillating_rips_persistence( auto start = EdgeRange::begin(nu, mu, points, Gudhi::Euclidean_distance(), p); auto end = EdgeRange::end(); for (const auto& t : OscillatingRipsSimplexRange::get_iterator_range(start, end, st, maxDim)) { - // std::cout << (std::get<2>(t) ? "in" : "out") << ": "; - // for (auto v : st.simplex_vertex_range(std::get<0>(t))) std::cout << v << " "; - // std::cout << " - "; - // for (auto sh : st.boundary_simplex_range(std::get<0>(t))) std::cout << st.key(sh) << " "; - // std::cout << "\n"; if (std::get<2>(t)) zp.insert_simplex_handle(std::get<0>(t)); else diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h index 973352a678..0f6d4f48ee 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h @@ -40,23 +40,62 @@ namespace Gudhi { namespace zigzag_persistence { +/** + * @class Zigzag_edge oscillating_rips_iterators.h gudhi/Zigzag_persistence/oscillating_rips_iterators.h + * @brief Edge structure for the oscilliating Rips filtration. + * + * @ingroup zigzag_persistence + * + * @details The edges of the filtration are computed first and the remaining simplices are deduced from them. + * Is also used to represent the vertices for technical reasons, by giving both vertices the same value. + * + * @tparam Filtration_value Type of the filtration value. + */ template class Zigzag_edge { public: + /** + * @brief Constructor. + * + * @param u First boundary vertex ID + * @param v Second boundary vertex ID. If same than @p u, the edge is considered a vertex. + * @param fil Filtration value + * @param direction If true, forwards. If false, backwards. + */ Zigzag_edge(int u, int v, Filtration_value fil, bool direction) : u_(u), v_(v), fil_(fil), direction_(direction) { if (u > v) std::swap(u_, v_); } + /** + * @brief Default constructor. Initialize everything to zero and the direction to true. + */ Zigzag_edge() : u_(0), v_(0), fil_(0), direction_(true) {} - /* Returns vertex with smaller label. */ + /** + * @brief Returns vertex with smaller ID. + * + * @return Smallest ID among the boundary vertices. + */ int get_smallest_vertex() const { return u_; } - /* Returns vertex with bigger label. */ + /** + * @brief Returns vertex with bigger ID. + * + * @return Biggest ID among the boundary vertices. + */ int get_biggest_vertex() const { return v_; } - /* Returns the filtration value of the edge. */ + /** + * @brief Returns the filtration value of the edge. + * + * @return Filtration value of the edge. + */ Filtration_value get_filtration_value() const { return fil_; } - /* Returns true if insertion of the edge, false if removal. */ + /** + * @brief Gives the direction of the arrow corresponding to the edge. + * + * @return True, if forward, i.e., an insertion. + * @return False, if backward, i.e., a removal. + */ bool get_direction() const { return direction_; } void set(int u, int v, Filtration_value fil, bool direction){ @@ -66,56 +105,139 @@ class Zigzag_edge { direction_ = direction; } + /** + * @brief Equality test + * + * @param e Edge to compare. + * @return True, if both edges are equal. + * @return False, if both edges are not equal. + */ bool operator==(const Zigzag_edge& e) const { return ((e.u_ == u_) && (e.v_ == v_) && (e.fil_ == fil_) && (e.direction_ == direction_)); } -// bool operator<(const Zigzag_edge& e) const { -// if (e.fil_ != fil_) return fil_ < e.fil_; -// if (e.direction_ != direction_) return direction_; -// if (e.u_ != u_) return u_ < e.u_; -// return v_ < e.v_; -// } - private: - int u_; - int v_; - Filtration_value fil_; - bool direction_; + int u_; /**< Smaller vertex. */ + int v_; /**< Bigger vertex. */ + Filtration_value fil_; /**< Filtration value. */ + bool direction_; /**< Direction. True = forward, false = backward. */ }; -template +/** + * @class Identity_edge_modifier oscillating_rips_iterators.h gudhi/Zigzag_persistence/oscillating_rips_iterators.h + * @brief Identity modifier, i.e., does nothing. + * + * @ingroup zigzag_persistence + */ class Identity_edge_modifier { public: - static constexpr bool isActive_ = false; + static constexpr bool isActive_ = false; /**< Indicates that the modifier should be ignored. */ private: + /** + * @brief Default constructor. Should not be called and therefore private. + */ Identity_edge_modifier() {} }; +/** + * @class Square_root_edge_modifier oscillating_rips_iterators.h gudhi/Zigzag_persistence/oscillating_rips_iterators.h + * @brief Modifier that square roots the filtration value of an edge. + * + * @ingroup zigzag_persistence + * + * @details Useful in particular when geometric computations (edge length, etc) are + * run with squared Euclidean distance for performance. + * + * @tparam Filtration_value Filtration value type. + */ template class Square_root_edge_modifier { public: + /** + * @brief Returns the square root of the given value. The parameter it-self is not modified. + * + * @param f Value to modify. + * @return The modified value of @p f. + */ static Filtration_value apply_modifier(Filtration_value f) { return std::sqrt(f); } + /** + * @brief Returns the square of the given value. The parameter it-self is not modified. + * + * @param f Value to modify. + * @return The modified value of @p f. + */ static Filtration_value apply_inverse_modifier(Filtration_value f) { return f * f; } - static constexpr bool isActive_ = true; + static constexpr bool isActive_ = true; /**< Indicates that the modifier should not be ignored. */ private: + /** + * @brief Default constructor. Should not be called and therefore private. + */ Square_root_edge_modifier() {} }; -//assumes that eps_n-1 == 0 -template > +/** + * @class Oscillating_rips_edge_range oscillating_rips_iterators.h gudhi/Zigzag_persistence/oscillating_rips_iterators.h + * @brief Gives access and computes different edge range types for an oscilliating Rips filtration. + * + * @ingroup zigzag_persistence + * + * @details There are two different kind of ranges: a vector range containing all computed edges and + * a Boost range made from a custom iterator computing an edge on the fly at each incrementation. + * The custom iterator is therefore only a forward iterator and can only be incremented. + * + * @tparam Filtration_value Filtration value type + * @tparam EdgeModifier Modifier for the edge filtration values. If no modifications are wanted, + * use @ref Identity_edge_modifier. Default value: @ref Identity_edge_modifier. + * + * @warning As the custom iterator used for the Boost ranges stores possibly large ranges, avoid copying it. + * Use @ref get_iterator_range, @ref begin and @ref end wisely. If one needs to do something else than to simply + * iterate over the edges in order, then the vector range is for sure the better option. + */ +template class Oscillating_rips_edge_range { public: - enum Order_policy { ALREADY_ORDERED, FARTHEST_POINT_ORDERING, RANDOM_POINT_ORDERING }; + /** + * @brief Order policy for the points. + */ + enum Order_policy { + ALREADY_ORDERED, /**< The given range of points is considered ordered. */ + FARTHEST_POINT_ORDERING, /**< The points are reordered using @ref Gudhi::subsampling::choose_n_farthest_points.*/ + RANDOM_POINT_ORDERING /**< The points are shuffled randomly. */ + }; + /** + * @class Oscillating_rips_edge_iterator oscillating_rips_iterators.h gudhi/Zigzag_persistence/oscillating_rips_iterators.h + * @brief Custom iterator over the edges of an oscillating rips filtration. + * + * It inherits from boost::iterator_facade. + * + * The iterator stores the distance matrix and epsilon values to compute the current edge on the fly + * at each incrementation. + * + * @warning As the iterator stores possibly large ranges, avoid copying it. + */ class Oscillating_rips_edge_iterator : public boost::iterator_facade&, boost::forward_traversal_tag> { public: + /** + * @brief Constructor. Computes the distance matrix and the epsilon values it-self from the given parameters. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplicator. + * @param mu Upper multiplicator. + * @param points Point cloud as a range.The format of a point has to correspond to the input format of the + * distance function. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param orderPolicy Order policy for the points. Can be either @ref Order_policy::FARTHEST_POINT_ORDERING, + * @ref Order_policy::ALREADY_ORDERED or @ref Order_policy::RANDOM_POINT_ORDERING. + */ template Oscillating_rips_edge_iterator(Filtration_value nu, Filtration_value mu, @@ -138,6 +260,22 @@ class Oscillating_rips_edge_range { columnIndex_ = it - distanceMatrix_[1].begin(); } + /** + * @brief Constructor. Takes already computed epsilon values as input, but assumes that the points are + * already ordered accordingly. Assumes also that the last epsilon value is 0. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplicator. + * @param mu Upper multiplicator. + * @param orderedPoints Point cloud as an ordered range. The format of a point has to correspond to the input + * format of the distance function. The order of the points should be in correspondence with the order of + * the epsilon values. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param epsilonValues Epsilon values for the oscillating rips filtration. Should be in decreasing order. + * And the last value should be 0. + */ template Oscillating_rips_edge_iterator(Filtration_value nu, Filtration_value mu, @@ -172,6 +310,9 @@ class Oscillating_rips_edge_range { columnIndex_ = it - distanceMatrix_[1].begin(); } + /** + * @brief Default constructor. Corresponds to the end iterator. + */ Oscillating_rips_edge_iterator() : nu_(0), mu_(0), @@ -183,22 +324,39 @@ class Oscillating_rips_edge_range { insertVertex_(true) {} private: + //mandatory for the boost::iterator_facade inheritance. friend class boost::iterator_core_access; - std::vector epsilonValues_; - std::vector > > distanceMatrix_; - Filtration_value nu_; - Filtration_value mu_; - Zigzag_edge currentEdge_; - size_t epsilonIndex_, rowIndex_, columnIndex_; - bool inPositiveDirection_, insertVertex_; - + std::vector epsilonValues_; /**< Epsilon values. */ + std::vector > > distanceMatrix_; /**< Distance matrix. */ + Filtration_value nu_; /**< Lower multiplicator. */ + Filtration_value mu_; /**< Upper multiplicator. */ + Zigzag_edge currentEdge_; /**< Stores the current edge in the range. */ + size_t epsilonIndex_, rowIndex_, columnIndex_; /**< Indices indicating the next position in the range. */ + bool inPositiveDirection_, insertVertex_; /**< Next direction and indicates if next ``edge'' is a vertex. */ + + /** + * @brief Mandatory for the boost::iterator_facade inheritance. Indicates if to iterators are equal. + * + * @param other Iterator to compare. + * @return True, the iterators are pointing to the same position. + * @return False, otherwise. + */ bool equal(Oscillating_rips_edge_iterator const& other) const { return rowIndex_ == other.rowIndex_ && currentEdge_ == other.currentEdge_; } + /** + * @brief Mandatory for the boost::iterator_facade inheritance. Returns the value of the dereferenced iterator. + * + * @return Current edge. + */ const Zigzag_edge& dereference() const { return currentEdge_; } + /** + * @brief Mandatory for the boost::iterator_facade inheritance. Increments the iterator. + * + */ void increment() { if (epsilonIndex_ < distanceMatrix_.size() - 1) { if (insertVertex_) { @@ -286,11 +444,17 @@ class Oscillating_rips_edge_range { _set_end(); } + /** + * @brief Set the iterator as the end iterator. + */ void _set_end(){ rowIndex_ = 0; currentEdge_ = Zigzag_edge(); } + /** + * @brief Initialize the column index for a new sequence of positive edges. + */ void _initialize_positive_col_index() { auto it = std::upper_bound(distanceMatrix_[rowIndex_].begin(), distanceMatrix_[rowIndex_].end(), @@ -299,6 +463,9 @@ class Oscillating_rips_edge_range { columnIndex_ = it - distanceMatrix_[rowIndex_].begin(); } + /** + * @brief Initialize the column index for a new sequence of negative edges. + */ void _initialize_negative_col_index() { auto it = std::lower_bound(distanceMatrix_[rowIndex_].begin(), distanceMatrix_[rowIndex_].end(), @@ -308,17 +475,29 @@ class Oscillating_rips_edge_range { columnIndex_ = it - distanceMatrix_[rowIndex_].begin(); } + /** + * @brief Indicates if the column index needs to be initialized or not. + */ bool _positive_col_index_is_not_valid() { return columnIndex_ == 0 || (rowIndex_ != (epsilonIndex_ + 1) && distanceMatrix_[rowIndex_][columnIndex_ - 1].second <= nu_ * epsilonValues_[epsilonIndex_]); } + /** + * @brief Indicates if the column index needs to be initialized or not. + */ bool _negative_col_index_is_not_valid() { return columnIndex_ == distanceMatrix_[rowIndex_].size() || distanceMatrix_[rowIndex_][columnIndex_].second > mu_ * epsilonValues_[epsilonIndex_]; } + /** + * @brief Updates the current edge. + * + * @param i Epsilon value index. + * @param direction Direction. + */ void _update_edge(size_t i, bool direction) { if constexpr (EdgeModifier::isActive_) currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, rowIndex_, @@ -327,6 +506,9 @@ class Oscillating_rips_edge_range { currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, rowIndex_, epsilonValues_[i], direction); } + /** + * @brief Update the current edge as a vertex to insert. + */ void _update_edge_as_positive_vertex() { if constexpr (EdgeModifier::isActive_) currentEdge_.set(epsilonIndex_ + 1, epsilonIndex_ + 1, @@ -335,11 +517,30 @@ class Oscillating_rips_edge_range { currentEdge_.set(epsilonIndex_ + 1, epsilonIndex_ + 1, epsilonValues_[epsilonIndex_], true); } + /** + * @brief Update the current edge as a vertex to remove. + */ void _update_edge_as_negative_vertex() { currentEdge_.set(rowIndex_ - 1, rowIndex_ - 1, -std::numeric_limits::infinity(), false); } }; + /** + * @brief Computes and return a vector with all edges in order of the oscillating Rips filtration. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplicator. + * @param mu Upper multiplicator. + * @param points Point cloud as a range.The format of a point has to correspond to the input format of the + * distance function. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param orderPolicy Order policy for the points. Can be either @ref Order_policy::FARTHEST_POINT_ORDERING, + * @ref Order_policy::ALREADY_ORDERED or @ref Order_policy::RANDOM_POINT_ORDERING. + * + * @return Vector of @ref Zigzag_edge. + */ template static std::vector > compute_vector_range( Filtration_value nu, @@ -399,6 +600,26 @@ class Oscillating_rips_edge_range { return edgeFiltration; } + /** + * @brief Returns a boost::iterator_range from @ref Oscillating_rips_edge_iterator. The edges are computed + * on the fly at each incrementation. The iterator is a forward iterator only. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplicator. + * @param mu Upper multiplicator. + * @param points Point cloud as a range.The format of a point has to correspond to the input format of the + * distance function. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param orderPolicy Order policy for the points. Can be either @ref Order_policy::FARTHEST_POINT_ORDERING, + * @ref Order_policy::ALREADY_ORDERED or @ref Order_policy::RANDOM_POINT_ORDERING. + * + * @return boost::iterator_range of @ref Oscillating_rips_edge_iterator. + * + * @warning Avoid copying the iterators as they are heavier than usual iterators. If begin and end iterators + * are needed but not the structure of the range, use @ref begin and @ref end instead. + */ template static boost::iterator_range get_iterator_range( Filtration_value nu, @@ -411,7 +632,59 @@ class Oscillating_rips_edge_range { Oscillating_rips_edge_iterator(nu, mu, points, distance, orderPolicy), Oscillating_rips_edge_iterator()); } - //as Oscillating_rips_edge_iterator is a heavy iterator to copy, it should not be used as a usual iterator + /** + * @brief Returns a boost::iterator_range from @ref Oscillating_rips_edge_iterator. The edges are computed + * on the fly at each incrementation. The iterator is a forward iterator only. + * Takes already computed epsilon values as input, but assumes that the points are already ordered accordingly. + * Assumes also that the last epsilon value is 0. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplicator. + * @param mu Upper multiplicator. + * @param orderedPoints Point cloud as an ordered range. The format of a point has to correspond to the input + * format of the distance function. The order of the points should be in correspondence with the order of + * the epsilon values. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param epsilonValues Epsilon values for the oscillating rips filtration. Should be in decreasing order. + * And the last value should be 0. + * + * @return boost::iterator_range of @ref Oscillating_rips_edge_iterator. + * + * @warning Avoid copying the iterators as they are heavier than usual iterators. If begin and end iterators + * are needed but not the structure of the range, use @ref begin and @ref end instead. + */ + template + static boost::iterator_range get_iterator_range( + Filtration_value nu, + Filtration_value mu, + const PointRange& orderedPoints, + DistanceFunction&& distance, + const std::vector& epsilonValues) + { + return boost::iterator_range( + Oscillating_rips_edge_iterator(nu, mu, orderedPoints, distance, epsilonValues), Oscillating_rips_edge_iterator()); + } + + /** + * @brief Returns the begin iterator of a the range of edges based on @ref Oscillating_rips_edge_iterator. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplicator. + * @param mu Upper multiplicator. + * @param points Point cloud as a range.The format of a point has to correspond to the input format of the + * distance function. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param orderPolicy Order policy for the points. Can be either @ref Order_policy::FARTHEST_POINT_ORDERING, + * @ref Order_policy::ALREADY_ORDERED or @ref Order_policy::RANDOM_POINT_ORDERING. + * + * @return Instianciation of @ref Oscillating_rips_edge_iterator. + * + * @warning Avoid copying the iterator as it is heavier than usual iterators. + */ template static Oscillating_rips_edge_iterator begin( Filtration_value nu, @@ -423,19 +696,60 @@ class Oscillating_rips_edge_range { return Oscillating_rips_edge_iterator(nu, mu, points, distance, orderPolicy); } + /** + * @brief Returns the begin iterator of a the range of edges based on @ref Oscillating_rips_edge_iterator. + * Takes already computed epsilon values as input, but assumes that the points are already ordered accordingly. + * Assumes also that the last epsilon value is 0. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplicator. + * @param mu Upper multiplicator. + * @param orderedPoints Point cloud as an ordered range. The format of a point has to correspond to the input + * format of the distance function. The order of the points should be in correspondence with the order of + * the epsilon values. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param epsilonValues Epsilon values for the oscillating rips filtration. Should be in decreasing order. + * And the last value should be 0. + * + * @return Instianciation of @ref Oscillating_rips_edge_iterator. + * + * @warning Avoid copying the iterator as it is heavier than usual iterators. + */ + template + static Oscillating_rips_edge_iterator begin( + Filtration_value nu, + Filtration_value mu, + const PointRange& orderedPoints, + DistanceFunction&& distance, + const std::vector& epsilonValues) + { + return Oscillating_rips_edge_iterator(nu, mu, orderedPoints, distance, epsilonValues); + } + + /** + * @brief Returns the end iterator of a the range of edges based on @ref Oscillating_rips_edge_iterator. + * + * @return Default instianciation of @ref Oscillating_rips_edge_iterator. + */ static Oscillating_rips_edge_iterator end() { return Oscillating_rips_edge_iterator(); } private: + /** + * @brief Default constructor. Should not be called and therfore private. Use as a ``static'' class only. + */ Oscillating_rips_edge_range(){}; - /* The two input types std::pair encode pairs - * (j, d(p_i,p_j)) and (k, d(p_i,p_k)) for some fixed point p_i. + /** + * @brief The two input types std::pair encode pairs + * @f$(j, d(p_i,p_j))@f$ and @f$(k, d(p_i,p_k))@f$ for some fixed point @f$p_i@f$. * The operator() orders edges by length. By convention, if lengths are equal, - * it orders pairs by taking the smaller vertex label between j and k. - */ + * it orders pairs by taking the smaller vertex label between @f$j@f$ and @f$k@f$. + */ struct Point_distance_comp { bool operator()(const std::pair& p1, const std::pair& p2) const { { @@ -447,6 +761,23 @@ class Oscillating_rips_edge_range { } }; + /** + * @brief Initialize the distance function and epsilon values. Updates also the multiplicators if + * the edge modifier is active. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param[out] nu Lower multiplicator. + * @param[out] mu Upper multiplicator. + * @param[out] epsilonValues Container for the epsilon values. + * @param[out] distanceMatrix Container for the distance matrices. + * @param[in] points Point cloud as a range.The format of a point has to correspond to the input format of the + * distance function. + * @param[in] distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param[in] orderPolicy Order policy for the points. Can be either @ref Order_policy::FARTHEST_POINT_ORDERING, + * @ref Order_policy::ALREADY_ORDERED or @ref Order_policy::RANDOM_POINT_ORDERING. + */ template static void _initialize(Filtration_value& nu, Filtration_value& mu, std::vector& epsilonValues, std::vector > >& distanceMatrix, @@ -487,26 +818,28 @@ class Oscillating_rips_edge_range { distanceMatrix = _compute_distance_matrix(sortedPoints, distance); } - /** \brief Compute the epsilon values for an ordered set of points, measuring the + /** + * @brief Compute the epsilon values for an ordered set of points, measuring the * sparsity of the ordering. * - * \details Let \f$P = \{p_0, \ldots, p_{n-1}\f$ be the ordered set of points. Then + * @details Let \f$P = \{p_0, \ldots, p_{n-1}\f$ be the ordered set of points. Then * the method sets eps_range[i]<\CODE> with the value \$f\varepsilon_i\$f, * defined as \f$\varpesilon_i = d_H(P_i,P)\f$, the Hausdorff between the points * \f$P_i= \{p_0, \ldots, p_{i}\}\f$ and the entire point cloud * \f$P = \{p_0, \ldots, p_{n-1}\}\f$. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param sortedPoints Point cloud as an ordered range. The format of a point has to correspond to the input + * format of the distance function. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. * - * @param[in] points Range of points. - * @param[in] distance Distance function that can be called on two points. - * @param[in] eps_range Vector in which the epsilon values are written, - * eps_range[i]<\CODE> is \$f\varepsilon_i\$f. - * Satisfyies epsilonValues[i] >= - * epsilonValues[j] >= 0<\CODE> whenever i>=j<\CODE>. - * The range must be of same size as the number of points. + * @return Vector of decreasing epsilon values ending with 0. */ template - static std::vector _compute_epsilon_values(const PointRange& points, DistanceFunction&& distance) { - size_t n = points.size(); + static std::vector _compute_epsilon_values(const PointRange& sortedPoints, DistanceFunction&& distance) { + size_t n = sortedPoints.size(); std::vector eps_range(n, std::numeric_limits::infinity()); // compute all \f$\varepsilon_i\f$ values, such that eps_range[i] == @@ -518,7 +851,7 @@ class Oscillating_rips_edge_range { tbb::parallel_for(size_t(i + 1), n, [&](size_t k) { // set eps_range[k] <- d(p_k, P_i) == // min{ d(p_k, P_{i-1}), d(p_k, p_i) } for k >= i. - double dist_i_k = distance(points[i], points[k]); + double dist_i_k = distance(sortedPoints[i], sortedPoints[k]); if (dist_i_k < eps_range[k]) { eps_range[k] = dist_i_k; } @@ -548,6 +881,20 @@ class Oscillating_rips_edge_range { return eps_range; } + /** + * @brief Returns the sparse distance matrix. A cell is represented as a pair of its row index and its value, + * i.e., @f$(j, d(p_i,p_j))@f$ is stored in column @f$i@f$ to indicate that the distance of the @f$i^{th}@f$ + * point has distance @f$d(p_i,p_j)@f$ from the @f$j^{th}@f$ point. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param sortedPoints Point cloud as an ordered range. The format of a point has to correspond to the input + * format of the distance function. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * + * @return A vector of vectors of pairs of row indices and distances. + */ template static std::vector > > _compute_distance_matrix( const PointRange& sortedPoints, DistanceFunction&& distance) { @@ -577,6 +924,19 @@ class Oscillating_rips_edge_range { return distanceMatrix; } + /** + * @brief Computes the edges that are added and the edges that are removed and stores them in two separate containers. + * The edge container @f$e@f$ stores the @f$i^{th}@f$ edge induced by vertex @f$j@f$ at @f$e[j][i]@f$. + * + * @param[in] nu Lower multiplicator. + * @param[in] mu Upper multiplicator. + * @param[in] epsilonValues Epsilon values in decreasing order finishing with 0. + * @param[in] distanceMatrix Spare distance matrix. + * @param[out] edgesAdded Container for positive edges. + * @param[out] edgesRemoved Container for negative edges. + * + * @return Total number of edges. + */ static size_t _compute_edges(Filtration_value nu, Filtration_value mu, const std::vector& epsilonValues, std::vector > >& distanceMatrix, @@ -759,46 +1119,50 @@ class Oscillating_rips_edge_range { return number_of_arrows; } + /** + * @brief Sorts canonically the edges: as much as possible, edges should be removed in + * the reverse order of their insertion. We decide to insert shorted edges first, + * with increasing lexicographical order, and remove larger edges first, with + * decreasing lexicographic order. + * + * @param edges Edges to sort. + */ static void _canonically_sort_edges(std::vector >& edges) { - // canonical sort of the edges: as much as possible, edges should be removed in - // the reverse order of their insertion. We decide to insert shorted edges first, - // with increasing lexicographical order, and remove larger edges first, with - // decreasing lexicographic order. - // filtration then dimension, then lex order for insertion auto edge_cmp = [](const Zigzag_edge& e1, const Zigzag_edge& e2) { if (e1.get_filtration_value() != e2.get_filtration_value()) { - return e1.get_filtration_value() < e2.get_filtration_value(); - } // lower fil first + return e1.get_filtration_value() < e2.get_filtration_value(); // lower fil first + } - if (e1.get_smallest_vertex() == e1.get_biggest_vertex()) { // e1 is a vertex, -> put vertices first + if (e1.get_smallest_vertex() == e1.get_biggest_vertex()) { // e1 is a vertex, -> put vertices first if (e2.get_smallest_vertex() == e2.get_biggest_vertex()) { - return e1.get_smallest_vertex() < e2.get_smallest_vertex(); - } //-> vertex of lower label - else { - return true; - } //-> always vertices before edges + return e1.get_smallest_vertex() < e2.get_smallest_vertex(); //-> vertex of lower label + } else { + return true; //-> always vertices before edges + } } // e1 is an edge if (e2.get_smallest_vertex() == e2.get_biggest_vertex()) { - return false; - } // e2 vertex, -> put it first + return false; // e2 vertex, -> put it first + } // both are edges, lexigraphic compare if (e1.get_smallest_vertex() != e2.get_smallest_vertex()) { - return e1.get_smallest_vertex() < e2.get_smallest_vertex(); - } // lex order + return e1.get_smallest_vertex() < e2.get_smallest_vertex(); // lex order + } if (e1.get_biggest_vertex() != e2.get_biggest_vertex()) { return e1.get_biggest_vertex() < e2.get_biggest_vertex(); } - return false; // equality + return false; // equality }; + // the inverse ordering for deletions auto inv_edge_cmp = [&](const Zigzag_edge& e1, const Zigzag_edge& e2) { if (e1.get_smallest_vertex() == e2.get_smallest_vertex() && e1.get_biggest_vertex() == e2.get_biggest_vertex()) { - return false; - } //== => false - return !(edge_cmp(e1, e2)); // reverse order + return false; //equality => false + } + return !(edge_cmp(e1, e2)); // reverse order }; + // sort sequences of inclusions of same filtration with edge_cmp // sort sequences of removals of same filtration with inv_edge_cmp auto beg = edges.begin(); @@ -809,20 +1173,19 @@ class Oscillating_rips_edge_range { while (end != edges.end() && end->get_filtration_value() == curr_fil && end->get_direction() == curr_type) { ++end; } - if (curr_type) { + if (curr_type) { // sequence of insertions #ifdef GUDHI_USE_TBB tbb::parallel_sort(beg, end, edge_cmp); #else std::sort(beg, end, edge_cmp); #endif - } // sequence of insertions - else { + } else { // sequence of removals #ifdef GUDHI_USE_TBB tbb::parallel_sort(beg, end, inv_edge_cmp); #else std::sort(beg, end, inv_edge_cmp); #endif - } // sequence of removals + } beg = end; curr_fil = beg->get_filtration_value(); curr_type = beg->get_direction(); @@ -830,26 +1193,81 @@ class Oscillating_rips_edge_range { } }; -//needs Filtered_complex to have stable simplex handles -template +/** + * @class Oscillating_rips_simplex_range oscillating_rips_iterators.h gudhi/Zigzag_persistence/oscillating_rips_iterators.h + * @brief Gives access to a range of the ordered simplices in an oscilliating Rips filtration. + * + * @ingroup zigzag_persistence + * + * @details The simplices are returned as tuples of three elements: + * the first is the simplex handle of the simplex in the given complex, + * the second is the filtration value of the corresponding arrow, + * the third is the direction of the arrow, i.e., indicates if the simplex is inserted or removed. + * For more information, see @ref Oscillating_rips_iterator. + * + * @tparam StableFilteredComplex Filtered complex structure that has stable simplex handles, + * that is, they do not invalidates after an insertion or a removal (except for the removed simplices). + * @tparam EdgeRangeIterator Type of the edge range iterator. + * + * @warning As the custom iterator stores a possibly large range, avoid copying it. + * Use @ref get_iterator_range, @ref begin and @ref end wisely. + */ +template class Oscillating_rips_simplex_range { public: + /** + * @class Oscillating_rips_iterator oscillating_rips_iterators.h gudhi/Zigzag_persistence/oscillating_rips_iterators.h + * @brief Custom iterator over the simplices of an oscillating rips filtration. Is a forward iterator only. + * + * It inherits from boost::iterator_facade. + * + * For each positive edge given by the edge range iterator, the given complex computes and adds all possible + * cofaces and they respective simplex handle are stored. For each negative edge, the given complex computes + * the simplex handles of all cofaces and they are stored. + * The simplex handles induced by an edge are stored only the time needed and are removed when reaching the next edge. + * That is, we start by storing the possible cofaces of an edge, then at each incrementation, + * the next element within the stored simplex handles is retrieved and once the end is reached, + * the simplex handles are erased and replaced by the possible cofaces of the next edge and so on... + * If the edge is negative, then a simplex is removed from the complex at the next incrementation. + * So, the maximum size of the complex corresponds to the maximum size of a complex in the zigzag filtration. + * + * The simplices are returned by the iterator as tuples of three elements: + * the first is the simplex handle of the simplex in the given complex, + * the second is the filtration value of the corresponding arrow, + * the third is the direction of the arrow, i.e., indicates if the simplex is inserted or removed. + * + * @warning As the iterator stores possibly a large range, avoid copying it. + */ class Oscillating_rips_iterator : public boost::iterator_facade&, - boost::forward_traversal_tag> { + const std::tuple&, + boost::forward_traversal_tag> + { public: - using Filtration_value = typename Filtered_complex::Filtration_value; - using Simplex_handle = typename Filtered_complex::Simplex_handle; - using Simplex_key = typename Filtered_complex::Simplex_key; - - // edges and complex are not copied, so do not modifiy outside as long as iterator != end. - // TODO: move constructor for iterator? + using Filtration_value = typename StableFilteredComplex::Filtration_value; /**< Filtration value type. */ + using Simplex_handle = typename StableFilteredComplex::Simplex_handle; /**< Simplex handle type. */ + using Simplex_key = typename StableFilteredComplex::Simplex_key; /**< Key type. */ + + /** + * @brief Constructor. The edge iterators and the complex are not copied, so do not modifiy or invalidate + * them outside as long as this iterator is different from the end iterator. + * + * @param edgeStartIterator Begin iterator of the oscilliating Rips edge range. + * Is moved, so the original iterator will probably be invalidated. + * @param edgeEndIterator End iterator of the oscilliating Rips edge range. + * Is moved, so the original iterator will probably be invalidated. + * @param complex Empty complex which will be used to store current simplices. + * Only the address of the complex is stored, so the state of the complex can be + * consulted between each incrementation. + * @param maxDimension Maximal dimension to which to expand the complex. If set to -1, there is no limit. + * Default value: -1. + */ Oscillating_rips_iterator(EdgeRangeIterator& edgeStartIterator, EdgeRangeIterator& edgeEndIterator, - Filtered_complex& complex, int maxDimension = -1) + StableFilteredComplex& complex, int maxDimension = -1) : complex_(&complex), currentSimplexIndex_(0), - currentEdgeIt_(std::move(edgeStartIterator)), + currentEdgeIt_(std::move(edgeStartIterator)), // TODO: move constructor for custom iterator? endEdgeIt_(std::move(edgeEndIterator)), currentDirection_(true), maxDimension_(maxDimension), @@ -872,6 +1290,9 @@ class Oscillating_rips_simplex_range { complex_->assign_key(currentSimplices_[currentSimplexIndex_], 0); } + /** + * @brief Default constructor. Corresponds to the end iterator. + */ Oscillating_rips_iterator() : complex_(nullptr), currentSimplexIndex_(0), @@ -880,10 +1301,14 @@ class Oscillating_rips_simplex_range { currentArrowNumber_(0) {} private: + //mandatory for the boost::iterator_facade inheritance. friend class boost::iterator_core_access; + /** + * @brief Reverse lexicographical order for the simplex handles. + */ struct reverse_lexicographic_order { - explicit reverse_lexicographic_order(Filtered_complex* st) : st_(st) {} + explicit reverse_lexicographic_order(StableFilteredComplex* st) : st_(st) {} bool operator()(const Simplex_handle sh1, const Simplex_handle sh2) const { auto rg1 = st_->simplex_vertex_range(sh1); @@ -900,20 +1325,26 @@ class Oscillating_rips_simplex_range { } return ((it1 == rg1.end()) && (it2 != rg2.end())); } - Filtered_complex* st_; + StableFilteredComplex* st_; }; - std::vector currentSimplices_; - Filtered_complex* complex_; - size_t currentSimplexIndex_; - EdgeRangeIterator currentEdgeIt_; - EdgeRangeIterator endEdgeIt_; - bool currentDirection_; - const int maxDimension_; - // bool isEnd_; - std::tuple currentArrow_; - Simplex_key currentArrowNumber_; - + std::vector currentSimplices_; /**< Stores current simplex handles. */ + StableFilteredComplex* complex_; /**< Pointer to the complex. */ + size_t currentSimplexIndex_; /**< Index to current position in currentSimplices_. */ + EdgeRangeIterator currentEdgeIt_; /**< Iterator pointing to the next edge. */ + EdgeRangeIterator endEdgeIt_; /**< End edge iterator. */ + bool currentDirection_; /**< Current direction. */ + const int maxDimension_; /**< Maximal dimension of expansion. */ + std::tuple currentArrow_; /**< Current return element. */ + Simplex_key currentArrowNumber_; /**< Number of incrementations. */ + + /** + * @brief Mandatory for the boost::iterator_facade inheritance. Indicates if two iterators are equal. + * + * @param other Iterator to compare. + * @return True, if the two iterators point to the same position. + * @return False, otherwise. + */ bool equal(Oscillating_rips_iterator const& other) const { if (complex_ == nullptr) return other.complex_ == nullptr; @@ -921,10 +1352,18 @@ class Oscillating_rips_simplex_range { currentSimplexIndex_ == other.currentSimplexIndex_; } + /** + * @brief Mandatory for the boost::iterator_facade inheritance. Dereference the iterator. + * + * @return Value of the current return element. + */ const std::tuple& dereference() const { return currentArrow_; } + /** + * @brief Mandatory for the boost::iterator_facade inheritance. Increments the iterator. + */ void increment() { if (!currentDirection_) complex_->remove_maximal_simplex(currentSimplices_[currentSimplexIndex_]); @@ -937,9 +1376,6 @@ class Oscillating_rips_simplex_range { return; } - // if (!currentDirection_) { - // for (auto& sh : currentSimplices_) complex_->remove_maximal_simplex(sh); - // } currentSimplices_.clear(); auto fil = currentEdgeIt_->get_filtration_value(); @@ -960,20 +1396,22 @@ class Oscillating_rips_simplex_range { if (currentDirection_) complex_->assign_key(currentSimplices_[currentSimplexIndex_], currentArrowNumber_); } + /** + * @brief Sets the iterator as the end iterator. + */ void _set_end() { complex_ = nullptr; } + /** + * @brief Updates @p currentSimplices_ for insertions. + * + * @param fil Current filtration value. + */ void _update_positive_current_simplices(Filtration_value fil) { while (currentEdgeIt_ != endEdgeIt_ && currentEdgeIt_->get_direction() && currentEdgeIt_->get_filtration_value() == fil) { complex_->insert_edge_as_flag(currentEdgeIt_->get_smallest_vertex(), currentEdgeIt_->get_biggest_vertex(), currentEdgeIt_->get_filtration_value(), maxDimension_, currentSimplices_); - // std::cout << "add edge: " << currentEdgeIt_->get_smallest_vertex() << " " << currentEdgeIt_->get_biggest_vertex() << " - " << currentEdgeIt_->get_filtration_value() << "\n"; - // std::cout << "current:\n"; - // for (auto sh : currentSimplices_){ - // for (auto v : complex_->simplex_vertex_range(sh)) std::cout << v << " "; - // std::cout << "\n"; - // } ++currentEdgeIt_; } #ifdef GUDHI_USE_TBB @@ -985,6 +1423,11 @@ class Oscillating_rips_simplex_range { #endif } + /** + * @brief Updates @p currentSimplices_ for removals. + * + * @param fil Current filtration value. + */ void _update_negative_current_simplices(Filtration_value fil) { unsigned int count = 0; while (currentEdgeIt_ != endEdgeIt_ && !currentEdgeIt_->get_direction() && @@ -1018,23 +1461,67 @@ class Oscillating_rips_simplex_range { } }; + /** + * @brief Returns a boost::iterator_range from @ref Oscillating_rips_iterator. + * The iterator is a forward iterator only. + * + * @param edgeStartIterator Begin iterator of the oscilliating Rips edge range. + * Is moved, so the original iterator will probably be invalidated. + * @param edgeEndIterator End iterator of the oscilliating Rips edge range. + * Is moved, so the original iterator will probably be invalidated. + * @param complex Empty complex which will be used to store current simplices. + * Only the address of the complex is stored, so the state of the complex can be + * consulted between each incrementation. + * @param maxDimension Maximal dimension to which to expand the complex. If set to -1, there is no limit. + * Default value: -1. + * + * @return boost::iterator_range of @ref Oscillating_rips_iterator. + * + * @warning Avoid copying the iterators as they are heavier than usual iterators. If begin and end iterators + * are needed but not the structure of the range, use @ref begin and @ref end instead. + */ static boost::iterator_range get_iterator_range(EdgeRangeIterator& edgeStartIterator, EdgeRangeIterator& edgeEndIterator, - Filtered_complex& complex, + StableFilteredComplex& complex, int maxDimension = -1) { return boost::iterator_range( Oscillating_rips_iterator(edgeStartIterator, edgeEndIterator, complex, maxDimension), Oscillating_rips_iterator()); } + /** + * @brief Returns the begin iterator of a the range of simplices based on @ref Oscillating_rips_iterator. + * + * @param edgeStartIterator Begin iterator of the oscilliating Rips edge range. + * Is moved, so the original iterator will probably be invalidated. + * @param edgeEndIterator End iterator of the oscilliating Rips edge range. + * Is moved, so the original iterator will probably be invalidated. + * @param complex Empty complex which will be used to store current simplices. + * Only the address of the complex is stored, so the state of the complex can be + * consulted between each incrementation. + * @param maxDimension Maximal dimension to which to expand the complex. If set to -1, there is no limit. + * Default value: -1. + * + * @return Instianciation of @ref Oscillating_rips_iterator. + * + * @warning Avoid copying the iterator as it is heavier than usual iterators. + */ static Oscillating_rips_iterator begin(EdgeRangeIterator& edgeStartIterator, EdgeRangeIterator& edgeEndIterator, - Filtered_complex& complex, int maxDimension = -1) { + StableFilteredComplex& complex, int maxDimension = -1) { return Oscillating_rips_iterator(edgeStartIterator, edgeEndIterator, complex, maxDimension); } + /** + * @brief Returns the end iterator of a the range of simplices based on @ref Oscillating_rips_iterator. + * + * @return Default instianciation of @ref Oscillating_rips_iterator. + */ static Oscillating_rips_iterator end() { return Oscillating_rips_iterator(); } private: + /** + * @brief Default constructor. Should not be called and therfore private. Use as a ``static'' class only. + */ Oscillating_rips_simplex_range(){}; }; From d8d07c5402f3b61c29db91416e843592733ea47b Mon Sep 17 00:00:00 2001 From: hschreiber Date: Mon, 21 Aug 2023 11:05:35 +0200 Subject: [PATCH 12/51] simplex tree options correction --- .../include/gudhi/column_types/cell.h | 6 +- .../include/gudhi/column_types/row_access.h | 2 +- src/Simplex_tree/include/gudhi/Simplex_tree.h | 62 +++++++++---------- ...igzagFilteredComplex.h => ZigzagComplex.h} | 11 ++-- .../example_simple_zigzag_filtration.cpp | 2 +- .../example_zzfiltration_from_file.cpp | 2 +- .../include/gudhi/Zigzag_persistence.h | 20 +++--- .../test/zigzag_persistence_unit_test.cpp | 2 +- 8 files changed, 49 insertions(+), 58 deletions(-) rename src/Zigzag_persistence/concept/{ZigzagFilteredComplex.h => ZigzagComplex.h} (92%) diff --git a/src/Persistence_matrix/include/gudhi/column_types/cell.h b/src/Persistence_matrix/include/gudhi/column_types/cell.h index 5f605eda6b..52c4ae468b 100644 --- a/src/Persistence_matrix/include/gudhi/column_types/cell.h +++ b/src/Persistence_matrix/include/gudhi/column_types/cell.h @@ -14,9 +14,7 @@ #include #include -//#include "../options.h" #include "../utilities/utilities.h" -//#include "../utilities/Zp_field.h" namespace Gudhi { namespace persistence_matrix { @@ -128,7 +126,7 @@ struct Z2_intrusive_row_cell : public Z2_row_cell, public base_hook_matrix_row return *this; }; - using base_hook_matrix_row = base_hook_matrix_row; //why ????? + // using base_hook_matrix_row = base_hook_matrix_row; //why ????? }; template @@ -228,7 +226,7 @@ struct Intrusive_row_cell : public Row_cell, public base_hoo return *this; }; - using base_hook_matrix_row = base_hook_matrix_row; //why ????? + // using base_hook_matrix_row = base_hook_matrix_row; //why ????? }; struct Z2_intrusive_list_cell : public Z2_base_cell, public base_hook_matrix_list_column diff --git a/src/Persistence_matrix/include/gudhi/column_types/row_access.h b/src/Persistence_matrix/include/gudhi/column_types/row_access.h index 80b9bfa7f2..8d6b645b1f 100644 --- a/src/Persistence_matrix/include/gudhi/column_types/row_access.h +++ b/src/Persistence_matrix/include/gudhi/column_types/row_access.h @@ -15,7 +15,7 @@ #include #include "../utilities/utilities.h" -//#include "cell.h" //why?? +#include "cell.h" namespace Gudhi { namespace persistence_matrix { diff --git a/src/Simplex_tree/include/gudhi/Simplex_tree.h b/src/Simplex_tree/include/gudhi/Simplex_tree.h index d724c8f53d..0704b435ea 100644 --- a/src/Simplex_tree/include/gudhi/Simplex_tree.h +++ b/src/Simplex_tree/include/gudhi/Simplex_tree.h @@ -2458,38 +2458,6 @@ struct Simplex_tree_options_full_featured { static const bool link_nodes_by_label = false; }; -/** Model of SimplexTreeOptions, same as `Simplex_tree_options_full_featured` - * but the possibility of much bigger keys and in particular of negative keys. - * - * Maximum number of simplices to compute persistence is std::numeric_limits::max() - * (way enough simplices). */ -struct Simplex_tree_options_wide_indexation { - typedef linear_indexing_tag Indexing_tag; - typedef int Vertex_handle; - typedef double Filtration_value; - typedef std::int64_t Simplex_key; - static const bool store_key = true; - static const bool store_filtration = true; - static const bool contiguous_vertices = false; - static const bool link_nodes_by_label = false; -}; - -/** Model of SimplexTreeOptions, same as `Simplex_tree_options_full_featured` - * but with the possibility of negative keys. - * - * Maximum number of simplices to compute persistence is std::numeric_limits::max() - * (about 2 billions of simplices). */ -struct Simplex_tree_options_negative_indexation { - typedef linear_indexing_tag Indexing_tag; - typedef int Vertex_handle; - typedef double Filtration_value; - typedef std::int32_t Simplex_key; - static const bool store_key = true; - static const bool store_filtration = true; - static const bool contiguous_vertices = false; - static const bool link_nodes_by_label = false; -}; - /** Model of SimplexTreeOptions, faster than `Simplex_tree_options_full_featured` but note the unsafe * `contiguous_vertices` option. * @@ -2523,6 +2491,36 @@ struct Simplex_tree_options_fast_cofaces { static const bool link_nodes_by_label = true; }; +/** Model of SimplexTreeOptions, fitting the requirement of the @ref ZigzagComplex concept. + * + * Maximum number of arrows in the filtration is std::numeric_limits::max() + * (about 2 billions of arrows). */ +struct Simplex_tree_options_zigzag_persistence { + typedef linear_indexing_tag Indexing_tag; + typedef int Vertex_handle; + typedef double Filtration_value; + typedef std::int32_t Simplex_key; + static const bool store_key = true; + static const bool store_filtration = false; + static const bool contiguous_vertices = false; + static const bool link_nodes_by_label = false; +}; + +/** Model of SimplexTreeOptions, same as `Simplex_tree_options_zigzag_persistence` + * but with much bigger keys for particularly long filtrations. + * + * Maximum number of arrows is std::numeric_limits::max(). */ +struct Simplex_tree_options_zigzag_persistence_long { + typedef linear_indexing_tag Indexing_tag; + typedef int Vertex_handle; + typedef double Filtration_value; + typedef std::int64_t Simplex_key; + static const bool store_key = true; + static const bool store_filtration = false; + static const bool contiguous_vertices = false; + static const bool link_nodes_by_label = false; +}; + /** @}*/ // end addtogroup simplex_tree } // namespace Gudhi diff --git a/src/Zigzag_persistence/concept/ZigzagFilteredComplex.h b/src/Zigzag_persistence/concept/ZigzagComplex.h similarity index 92% rename from src/Zigzag_persistence/concept/ZigzagFilteredComplex.h rename to src/Zigzag_persistence/concept/ZigzagComplex.h index 7a6e416f1b..a8b03a209a 100644 --- a/src/Zigzag_persistence/concept/ZigzagFilteredComplex.h +++ b/src/Zigzag_persistence/concept/ZigzagComplex.h @@ -11,8 +11,8 @@ #ifndef CONCEPT_ZZ_COMPLEX_TYPE_H_ #define CONCEPT_ZZ_COMPLEX_TYPE_H_ -/** @file ZigzagFilteredComplex.h - * @brief Contains @ref Gudhi::zigzag_persistence::ZigzagFilteredComplex concept. +/** @file ZigzagComplex.h + * @brief Contains @ref Gudhi::zigzag_persistence::ZigzagComplex concept. */ namespace Gudhi { @@ -21,7 +21,7 @@ namespace zigzag_persistence { /** * @brief Data structure storing the simplices in the current complex. */ -class ZigzagFilteredComplex { +class ZigzagComplex { public: /** * @brief Signed integer type that needs to be long enough to store the numbers of arrows in the zigzag filtration. @@ -51,20 +51,19 @@ class ZigzagFilteredComplex { /** * @brief Constructor */ - ZigzagFilteredComplex(); + ZigzagComplex(); /** * @brief Inserts the given simplex in the complex. * * @tparam VertexRange Range over the vertices of a simplex. * @param simplex Simplex to insert represented by its vertices. - * @param filtration Filtration value at the insertion. * @return A pair of a simplex handle and a boolean. * The simplex handle represents the inserted simplex and * the boolean if simplex was already contained in the complex or not. */ template - std::pair insert_simplex(const VertexRange& simplex, Filtration_value filtration); + std::pair insert_simplex(const VertexRange& simplex); /** * @brief Removes the given simplex. Assumes that the simplex is maximal and can be safely removed. diff --git a/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp b/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp index 82e20bba63..0493952c1b 100644 --- a/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp +++ b/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp @@ -15,7 +15,7 @@ #include #include -using ST = Gudhi::Simplex_tree; +using ST = Gudhi::Simplex_tree; using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; using Vertex_handle = ST::Vertex_handle; using Filtration_value = ST::Filtration_value; diff --git a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp index 21bcfc1fde..3cdd5d4023 100644 --- a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp +++ b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp @@ -15,7 +15,7 @@ #include #include -using ST = Gudhi::Simplex_tree; +using ST = Gudhi::Simplex_tree; using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; using Vertex_handle = ST::Vertex_handle; using Filtration_value = ST::Filtration_value; diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h index a263aede5a..539331170f 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h @@ -52,24 +52,20 @@ namespace zigzag_persistence { * * \ingroup zigzag_persistence * - * \details The type ZigzagFilteredComplex::Simplex_key counts the number of - * insertions and - * deletions of simplices, which may be large in zigzag persistence and require + * \details The type ZigzagComplex::Simplex_key counts the number of + * insertions and deletions of simplices, which may be large in zigzag persistence and require * more than 32 bits of storage. The type used (int, long, etc) should be chosen in * consequence. Simplex_key must be signed. * - * Over all insertions, the Simplex_key must be positive and strictly increasing - * when forward iterating along the zigzag filtration. - * - * \tparam ZigzagFilteredComplex Complex storing the current simplices. + * \tparam ZigzagComplex Complex storing the current simplices. * \tparam ZigzagPersistenceOptions Options for the matrix used to compute the persistence. */ -template > class Zigzag_persistence { public: - using Complex = ZigzagFilteredComplex; /**< Complex type. */ + using Complex = ZigzagComplex; /**< Complex type. */ using Options = ZigzagPersistenceOptions; /**< Matrix options */ /*** Types defined in the complex ***/ using Simplex_key = typename Complex::Simplex_key; /**< Key type, must be signed. */ @@ -276,7 +272,7 @@ class Zigzag_persistence filtration_values_.emplace_back(num_arrow_, previous_filtration_value_); } - std::pair res = cpx_.insert_simplex(simplex, filtration_value); + std::pair res = cpx_.insert_simplex(simplex); GUDHI_CHECK(res.second, "Zigzag_persistence::insert_simplex - insertion of a simplex already in the complex"); cpx_.assign_key(res.first, num_arrow_); _process_forward_arrow(res.first); @@ -485,7 +481,7 @@ class Zigzag_persistence * * @return Const reference to the complex. */ -// const ZigzagFilteredComplex& get_complex() const{ +// const ZigzagComplex& get_complex() const{ // return cpx_; // } @@ -496,7 +492,7 @@ class Zigzag_persistence * * @return Reference to the complex. */ - ZigzagFilteredComplex& get_complex() const{ + ZigzagComplex& get_complex() const{ return cpx_; } diff --git a/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp b/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp index 050bcd93b7..d4443b043f 100644 --- a/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp +++ b/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp @@ -23,7 +23,7 @@ using namespace Gudhi; using namespace boost::unit_test; -using ST = Gudhi::Simplex_tree; +using ST = Gudhi::Simplex_tree; using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; using Vertex_handle = ST::Vertex_handle; using Filtration_value = ST::Filtration_value; From 394e33e444cfa7e7d10992e20921871edb9cefd8 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Mon, 21 Aug 2023 14:05:05 +0200 Subject: [PATCH 13/51] doc --- src/Simplex_tree/include/gudhi/Simplex_tree.h | 2 +- .../test/simplex_tree_edge_expansion_unit_test.cpp | 4 ++-- src/Zigzag_persistence/concept/ZigzagComplex.h | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Simplex_tree/include/gudhi/Simplex_tree.h b/src/Simplex_tree/include/gudhi/Simplex_tree.h index 3176397199..4e8d291944 100644 --- a/src/Simplex_tree/include/gudhi/Simplex_tree.h +++ b/src/Simplex_tree/include/gudhi/Simplex_tree.h @@ -2497,7 +2497,7 @@ struct Simplex_tree_options_fast_cofaces { static const bool link_nodes_by_label = true; }; -/** Model of SimplexTreeOptions, fitting the requirement of the @ref ZigzagComplex concept. +/** Model of SimplexTreeOptions, fitting the requirement of the @ref Gudhi::zigzag_persistence::ZigzagComplex concept. * * Maximum number of arrows in the filtration is std::numeric_limits::max() * (about 2 billions of arrows). */ diff --git a/src/Simplex_tree/test/simplex_tree_edge_expansion_unit_test.cpp b/src/Simplex_tree/test/simplex_tree_edge_expansion_unit_test.cpp index 1b43930909..70fb6f6254 100644 --- a/src/Simplex_tree/test/simplex_tree_edge_expansion_unit_test.cpp +++ b/src/Simplex_tree/test/simplex_tree_edge_expansion_unit_test.cpp @@ -1,8 +1,8 @@ /* 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 + * Author(s): Hannah Schreiber * - * Copyright (C) 2014 Inria + * Copyright (C) 2023 Inria * * Modification(s): * - YYYY/MM Author: Description of the modification diff --git a/src/Zigzag_persistence/concept/ZigzagComplex.h b/src/Zigzag_persistence/concept/ZigzagComplex.h index a8b03a209a..6b0faa757b 100644 --- a/src/Zigzag_persistence/concept/ZigzagComplex.h +++ b/src/Zigzag_persistence/concept/ZigzagComplex.h @@ -19,7 +19,9 @@ namespace Gudhi { namespace zigzag_persistence { /** - * @brief Data structure storing the simplices in the current complex. + * @brief Data structure storing the simplices in the current complex. + * The concept is realized for example by @ref Gudhi::Simplex_tree < Gudhi::Simplex_tree_options_zigzag_persistence > + * or @ref Gudhi::Simplex_tree < Gudhi::Simplex_tree_options_zigzag_persistence_long >. */ class ZigzagComplex { public: From 951cbb89f574eb860bb8b576020acfecd2c81806 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Mon, 21 Aug 2023 14:10:10 +0200 Subject: [PATCH 14/51] doc --- .../include/gudhi/Oscillating_rips_persistence.h | 2 +- src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h | 4 ++-- .../gudhi/Zigzag_persistence/oscillating_rips_iterators.h | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h b/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h index 93bab9be02..a701d8cabd 100644 --- a/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h @@ -49,7 +49,7 @@ enum Edge_range_type { * possibility to switch out a few common options. But additional options exists for the iterators * (e.g. replacing the Euclidian distance by another one, or using your own epsilon values; see * the documention of @ref Oscillating_rips_edge_range and @ref Oscillating_rips_simplex_range for - * more information). You can easily create your own method based on this one. + * more information). One can easily create their own method based on this one. * * @tparam PointRange Range containing the point cloud. * @tparam edge_range_type Either Edge_range_type::BOOST_RANGE or Edge_range_type::VECTOR. diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h index 42fa03e7a0..0b89ef3457 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h @@ -256,7 +256,7 @@ class Zigzag_persistence * @param filtration_value Filtration value associated to the simplex. * Assumed to be larger or equal to previously used filtration values. */ - template > + template > void insert_simplex(const VertexRange& simplex, Filtration_value filtration_value) { if (dim_max_ != -1 && simplex.size() > static_cast(dim_max_) + 1) return; @@ -333,7 +333,7 @@ class Zigzag_persistence * are removed in the filtration and ``contiguous'' in the filtration, that is, no other simplex * which is not in the range is inserted or removed between two simplices in the range. * @param filtration_values Filtration values associated to the removal of the given simplices. Has therefore the - * same size as @a simplices. The order has to correspond to the order in @a simplices. Their values have to + * same size as @p simplices. The order has to correspond to the order in @p simplices. Their values have to * ascending in this order and they are assumed to be larger or equal to previously used filtration values. */ template >, diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h index 0f6d4f48ee..3703fdafd2 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h @@ -1207,7 +1207,8 @@ class Oscillating_rips_edge_range { * * @tparam StableFilteredComplex Filtered complex structure that has stable simplex handles, * that is, they do not invalidates after an insertion or a removal (except for the removed simplices). - * @tparam EdgeRangeIterator Type of the edge range iterator. + * @tparam EdgeRangeIterator Type of the edge range iterator. Each element of the range is assumed to have + * type @ref Zigzag_edge or at least to be a class with the same public methods than @ref Zigzag_edge. * * @warning As the custom iterator stores a possibly large range, avoid copying it. * Use @ref get_iterator_range, @ref begin and @ref end wisely. From 2c79bc2bed36d79d2a35d6c4e543cdd2d8fde90f Mon Sep 17 00:00:00 2001 From: hschreiber Date: Mon, 21 Aug 2023 14:55:18 +0200 Subject: [PATCH 15/51] merge error fix --- src/Simplex_tree/include/gudhi/Simplex_tree.h | 101 ++++++++++++------ 1 file changed, 67 insertions(+), 34 deletions(-) diff --git a/src/Simplex_tree/include/gudhi/Simplex_tree.h b/src/Simplex_tree/include/gudhi/Simplex_tree.h index c26ab24efe..128f117381 100644 --- a/src/Simplex_tree/include/gudhi/Simplex_tree.h +++ b/src/Simplex_tree/include/gudhi/Simplex_tree.h @@ -5,8 +5,10 @@ * Copyright (C) 2014 Inria * * Modification(s): - * - 2020/09 Clément Maria: option to link all simplex tree nodes with same label in an intrusive list. + * - 2020/09 Clément Maria: Option to link all simplex tree nodes with same label in an intrusive list + * - 2020/09 Clément Maria: Edge insertion method for flag complexes * - 2023/02 Vincent Rouvreau: Add de/serialize methods for pickle feature + * - 2023/05 Hannah Schreiber: Factorization of expansion methods * - YYYY/MM Author: Description of the modification */ @@ -534,32 +536,37 @@ class Simplex_tree { template friend class Simplex_tree; /** \brief Checks if two simplex trees are equal. */ - bool operator==(Simplex_tree& st2) { + template + bool operator==(Simplex_tree& st2) { if ((null_vertex_ != st2.null_vertex_) || (dimension_ != st2.dimension_ && !dimension_to_be_lowered_ && !st2.dimension_to_be_lowered_)) return false; - return rec_equal(&root_, &st2.root_); + return rec_equal(&root_, &st2.root_); } /** \brief Checks if two simplex trees are different. */ - bool operator!=(Simplex_tree& st2) { + template + bool operator!=(Simplex_tree& st2) { return (!(*this == st2)); } private: /** rec_equal: Checks recursively whether or not two simplex trees are equal, using depth first search. */ - bool rec_equal(Siblings* s1, Siblings* s2) { + template + bool rec_equal(Siblings* s1, typename Simplex_tree::Siblings* s2) { if (s1->members().size() != s2->members().size()) return false; - for (auto sh1 = s1->members().begin(), sh2 = s2->members().begin(); - (sh1 != s1->members().end() && sh2 != s2->members().end()); ++sh1, ++sh2) { + auto sh2 = s2->members().begin(); + for (auto sh1 = s1->members().begin(); + (sh1 != s1->members().end() && sh2 != s2->members().end()); + ++sh1, ++sh2) { if (sh1->first != sh2->first || sh1->second.filtration() != sh2->second.filtration()) return false; if (has_children(sh1) != has_children(sh2)) return false; // Recursivity on children only if both have children else if (has_children(sh1)) - if (!rec_equal(sh1->second.children(), sh2->second.children())) + if (!rec_equal(sh1->second.children(), sh2->second.children())) return false; } return true; @@ -1539,38 +1546,60 @@ class Simplex_tree { thread_local std::vector > inter; for (Dictionary_it s_h = siblings->members().begin(); - s_h != siblings->members().end(); ++s_h, ++next) { - Simplex_handle root_sh = find_vertex(s_h->first); - if (has_children(root_sh)) { - intersection( - inter, // output intersection - next, // begin - siblings->members().end(), // end - root_sh->second.children()->members().begin(), - root_sh->second.children()->members().end(), - s_h->second.filtration()); - if (inter.size() != 0) { - Siblings * new_sib = new Siblings(siblings, // oncles - s_h->first, // parent - inter); // boost::container::ordered_unique_range_t - for (auto it = new_sib->members().begin(); it != new_sib->members().end(); ++it) { - update_simplex_tree_after_node_insertion(it); - } + s_h != siblings->members().end(); ++s_h, ++next) + { + create_expansion(siblings, s_h, inter, next, s_h->second.filtration(), k); + } + } - inter.clear(); - s_h->second.assign_children(new_sib); - siblings_expansion(new_sib, k - 1); - } else { - // ensure the children property - s_h->second.assign_children(siblings); - inter.clear(); + /** \brief Recursive expansion of the simplex tree.*/ + template + void create_expansion(Siblings * siblings, + Dictionary_it& s_h, + std::vector >& inter, + Dictionary_it& next, + Filtration_value fil, + int k, + std::vector* added_simplices = nullptr) + { + Simplex_handle root_sh = find_vertex(s_h->first); + + if (!has_children(root_sh)) return; + + intersection( + inter, // output intersection + next, // begin + siblings->members().end(), // end + root_sh->second.children()->members().begin(), + root_sh->second.children()->members().end(), + fil); + if (inter.size() != 0) { + Siblings * new_sib = new Siblings(siblings, // oncles + s_h->first, // parent + inter); // boost::container::ordered_unique_range_t + for (auto it = new_sib->members().begin(); it != new_sib->members().end(); ++it) { + update_simplex_tree_after_node_insertion(it); + if constexpr (force_filtration_value){ + added_simplices->push_back(it); } } + inter.clear(); + s_h->second.assign_children(new_sib); + if constexpr (force_filtration_value){ + siblings_expansion(new_sib, fil, k - 1, *added_simplices); + } else { + siblings_expansion(new_sib, k - 1); + } + } else { + // ensure the children property + s_h->second.assign_children(siblings); + inter.clear(); } } /** \brief Intersects Dictionary 1 [begin1;end1) with Dictionary 2 [begin2,end2) * and assigns the maximal possible Filtration_value to the Nodes. */ + template static void intersection(std::vector >& intersection, Dictionary_it begin1, Dictionary_it end1, Dictionary_it begin2, Dictionary_it end2, @@ -1579,8 +1608,12 @@ class Simplex_tree { return; // ----->> while (true) { if (begin1->first == begin2->first) { - Filtration_value filt = (std::max)({begin1->second.filtration(), begin2->second.filtration(), filtration_}); - intersection.emplace_back(begin1->first, Node(nullptr, filt)); + if constexpr (force_filtration_value){ + intersection.emplace_back(begin1->first, Node(nullptr, filtration_)); + } else { + Filtration_value filt = (std::max)({begin1->second.filtration(), begin2->second.filtration(), filtration_}); + intersection.emplace_back(begin1->first, Node(nullptr, filt)); + } if (++begin1 == end1 || ++begin2 == end2) return; // ----->> } else if (begin1->first < begin2->first) { From f85ffb92bc1271c5a1727165ac2cef392994415d Mon Sep 17 00:00:00 2001 From: hschreiber Date: Mon, 21 Aug 2023 15:34:36 +0200 Subject: [PATCH 16/51] removal of useless simplex tree options --- src/Simplex_tree/include/gudhi/Simplex_tree.h | 18 ++---------------- src/Zigzag_persistence/concept/ZigzagComplex.h | 3 +-- .../example_simple_zigzag_filtration.cpp | 2 +- .../example/example_zzfiltration_from_file.cpp | 2 +- .../include/gudhi/Zigzag_persistence.h | 12 ++++++------ .../test/zigzag_persistence_unit_test.cpp | 2 +- 6 files changed, 12 insertions(+), 27 deletions(-) diff --git a/src/Simplex_tree/include/gudhi/Simplex_tree.h b/src/Simplex_tree/include/gudhi/Simplex_tree.h index 128f117381..d5efcb4cb8 100644 --- a/src/Simplex_tree/include/gudhi/Simplex_tree.h +++ b/src/Simplex_tree/include/gudhi/Simplex_tree.h @@ -2476,7 +2476,8 @@ struct Simplex_tree_options_fast_cofaces { /** Model of SimplexTreeOptions, fitting the requirement of the @ref Gudhi::zigzag_persistence::ZigzagComplex concept. * * Maximum number of arrows in the filtration is std::numeric_limits::max() - * (about 2 billions of arrows). */ + * (about 2 billions of arrows). If more arrows are needed, just inherit from this structure and redefine + * the type `Simplex_key`, for example as `std::int64_t`.*/ struct Simplex_tree_options_zigzag_persistence { typedef linear_indexing_tag Indexing_tag; typedef int Vertex_handle; @@ -2488,21 +2489,6 @@ struct Simplex_tree_options_zigzag_persistence { static const bool link_nodes_by_label = false; }; -/** Model of SimplexTreeOptions, same as `Simplex_tree_options_zigzag_persistence` - * but with much bigger keys for particularly long filtrations. - * - * Maximum number of arrows is std::numeric_limits::max(). */ -struct Simplex_tree_options_zigzag_persistence_long { - typedef linear_indexing_tag Indexing_tag; - typedef int Vertex_handle; - typedef double Filtration_value; - typedef std::int64_t Simplex_key; - static const bool store_key = true; - static const bool store_filtration = false; - static const bool contiguous_vertices = false; - static const bool link_nodes_by_label = false; -}; - /** @}*/ // end addtogroup simplex_tree } // namespace Gudhi diff --git a/src/Zigzag_persistence/concept/ZigzagComplex.h b/src/Zigzag_persistence/concept/ZigzagComplex.h index 6b0faa757b..0a76441a4b 100644 --- a/src/Zigzag_persistence/concept/ZigzagComplex.h +++ b/src/Zigzag_persistence/concept/ZigzagComplex.h @@ -20,8 +20,7 @@ namespace zigzag_persistence { /** * @brief Data structure storing the simplices in the current complex. - * The concept is realized for example by @ref Gudhi::Simplex_tree < Gudhi::Simplex_tree_options_zigzag_persistence > - * or @ref Gudhi::Simplex_tree < Gudhi::Simplex_tree_options_zigzag_persistence_long >. + * The concept is realized for example by @ref Gudhi::Simplex_tree < Gudhi::Simplex_tree_options_zigzag_persistence >. */ class ZigzagComplex { public: diff --git a/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp b/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp index 0493952c1b..0b804b0547 100644 --- a/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp +++ b/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp @@ -15,7 +15,7 @@ #include #include -using ST = Gudhi::Simplex_tree; +using ST = Gudhi::Simplex_tree; using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; using Vertex_handle = ST::Vertex_handle; using Filtration_value = ST::Filtration_value; diff --git a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp index 3cdd5d4023..29d4f67d3b 100644 --- a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp +++ b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp @@ -15,7 +15,7 @@ #include #include -using ST = Gudhi::Simplex_tree; +using ST = Gudhi::Simplex_tree; using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; using Vertex_handle = ST::Vertex_handle; using Filtration_value = ST::Filtration_value; diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h index 539331170f..8c1e66d1ee 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h @@ -65,13 +65,13 @@ template ; +using ST = Gudhi::Simplex_tree; using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; using Vertex_handle = ST::Vertex_handle; using Filtration_value = ST::Filtration_value; From 8f2dc9cd6b0e6b9a613c306a94411eb2c2b8b808 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Thu, 31 Aug 2023 16:54:57 +0200 Subject: [PATCH 17/51] doc --- biblio/bibliography.bib | 12 ++ .../concept/DistanceFunction.h | 31 +++++ src/Zigzag_persistence/concept/EdgeModifier.h | 54 ++++++++ .../concept/OscillatingRipsSimplexRange.h | 69 ++++++++++ src/Zigzag_persistence/concept/PointRange.h | 67 ++++++++++ .../concept/StableFilteredComplex.h | 121 ++++++++++++++++++ .../concept/ZigzagComplex.h | 3 +- .../doc/Intro_zigzag_persistence.h | 9 ++ .../include/gudhi/Zigzag_persistence.h | 14 +- .../oscillating_rips_iterators.h | 30 ++--- src/common/doc/examples.h | 1 + 11 files changed, 382 insertions(+), 29 deletions(-) create mode 100644 src/Zigzag_persistence/concept/DistanceFunction.h create mode 100644 src/Zigzag_persistence/concept/EdgeModifier.h create mode 100644 src/Zigzag_persistence/concept/OscillatingRipsSimplexRange.h create mode 100644 src/Zigzag_persistence/concept/PointRange.h create mode 100644 src/Zigzag_persistence/concept/StableFilteredComplex.h diff --git a/biblio/bibliography.bib b/biblio/bibliography.bib index 907391c607..d5a7f07d5c 100644 --- a/biblio/bibliography.bib +++ b/biblio/bibliography.bib @@ -70,6 +70,18 @@ @inproceedings{zigzag_morse doi = {10.1007/978-3-030-24766-9\_39} } +@inproceedings{osc_zz, +author = {Oudot, Steve and Sheehy, Donald}, +year = {2013}, +month = {06}, +pages = {387-396}, +title = {{Zigzag Zoology: Rips Zigzags for Homology Inference}}, +volume = {15}, + booktitle = {Foundations of Computational Mathematics}, +url = {https://doi.org/10.1145/2462356.2462371}, +doi = {10.1145/2462356.2462371} +} + @article{Cohen-Steiner2009, author = {Cohen-Steiner, David and Edelsbrunner, Herbert and Harer, John}, journal = {Foundations of Computational Mathematics}, diff --git a/src/Zigzag_persistence/concept/DistanceFunction.h b/src/Zigzag_persistence/concept/DistanceFunction.h new file mode 100644 index 0000000000..031da7069d --- /dev/null +++ b/src/Zigzag_persistence/concept/DistanceFunction.h @@ -0,0 +1,31 @@ +/* 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): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#ifndef CONCEPT_ZZ_DISTANCE_FUNCTION_H_ +#define CONCEPT_ZZ_DISTANCE_FUNCTION_H_ + +/** @file DistanceFunction.h + * @brief Contains @ref Gudhi::zigzag_persistence::DistanceFunction concept. + */ + +#include "PointRange.h" + +namespace Gudhi { +namespace zigzag_persistence { + +/** + * @brief Distance function taking two points as input and returning the distance between them. + */ +using DistanceFunction = double (*)(const Point&, const Point&); + +} // namespace zigzag_persistence +} // namespace Gudhi + +#endif // CONCEPT_ZZ_DISTANCE_FUNCTION_H_ diff --git a/src/Zigzag_persistence/concept/EdgeModifier.h b/src/Zigzag_persistence/concept/EdgeModifier.h new file mode 100644 index 0000000000..180460d3d6 --- /dev/null +++ b/src/Zigzag_persistence/concept/EdgeModifier.h @@ -0,0 +1,54 @@ +/* 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): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#ifndef CONCEPT_ZZ_EDGE_MODIFIER_H_ +#define CONCEPT_ZZ_EDGE_MODIFIER_H_ + +/** @file EdgeModifier.h + * @brief Contains @ref Gudhi::zigzag_persistence::EdgeModifier concept. + */ + +namespace Gudhi { +namespace zigzag_persistence { + +/** + * @brief Methods whose purposes are to modify the filtration value of a given edge following a rule. + * The concept is for example realized by @ref Square_root_edge_modifier. + */ + template +class EdgeModifier { + public: + /** + * @brief Indicates that the modifier should not be ignored. + */ + static constexpr bool isActive_ = true; + + /** + * @brief Applies the modifier to the given value and returns it. + * + * @param f Value to modify. + * @return The modified value of @p f. + */ + static Filtration_value apply_modifier(Filtration_value f); + + /** + * @brief Applies the inverse modifier to the given value and returns it. + * So, apply_inverse_modifier(apply_modifier(f)) == f (modulo some possible precision errors.). + * + * @param f Value to modify. + * @return The modified value of @p f. + */ + static Filtration_value apply_inverse_modifier(Filtration_value f); +}; + +} // namespace zigzag_persistence +} // namespace Gudhi + +#endif // CONCEPT_ZZ_EDGE_MODIFIER_H_ diff --git a/src/Zigzag_persistence/concept/OscillatingRipsSimplexRange.h b/src/Zigzag_persistence/concept/OscillatingRipsSimplexRange.h new file mode 100644 index 0000000000..899d96276a --- /dev/null +++ b/src/Zigzag_persistence/concept/OscillatingRipsSimplexRange.h @@ -0,0 +1,69 @@ +/* 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): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#ifndef CONCEPT_ZZ_OSCI_RIPS_RANGE_H_ +#define CONCEPT_ZZ_OSCI_RIPS_RANGE_H_ + +/** @file OscillatingRipsSimplexRange.h + * @brief Contains @ref Gudhi::zigzag_persistence::OscillatingRipsSimplexRange concept. + */ + +#include +#include + +namespace Gudhi { +namespace zigzag_persistence { + +/** + * @brief Class giving access to a range over the simplices in an oscilliating Rips filtration + * in order of the filtration. + * + * A simplex has to be represented by a tuple of three elements: + * the first is the simplex handle of the simplex in the given complex, + * the second is the filtration value of the corresponding arrow, + * the third is the direction of the arrow, i.e., indicates if the simplex is inserted or removed. + */ +class OscillatingRipsSimplexRange { + public: + /** + * @brief Returns a range over the simplices in an oscilliating Rips filtration + * in order of the filtration. + * + * @param edgeStartIterator Begin iterator of the edge range. + * @param edgeEndIterator End iterator of the edge range. + * @param complex Structure storing the complex at each step. + * @param maxDimension Maximal dimension of the expansion. + * @return A range with begin() and end() methods. + */ + static auto get_iterator_range(Oscillating_rips_edge_range::Oscillating_rips_edge_iterator& edgeStartIterator, + Oscillating_rips_edge_range::Oscillating_rips_edge_iterator& edgeEndIterator, + Gudhi::Simplex_tree& complex, + int maxDimension); + + /** + * @brief Returns a range over the simplices in an oscilliating Rips filtration + * in order of the filtration. + * + * @param edgeStartIterator Begin iterator of the edge range. + * @param edgeEndIterator End iterator of the edge range. + * @param complex Structure storing the complex at each step. + * @param maxDimension Maximal dimension of the expansion. + * @return A range with begin() and end() methods. + */ + static auto get_iterator_range(std::vector >::iterator& edgeStartIterator, + std::vector >::iterator& edgeEndIterator, + Gudhi::Simplex_tree& complex, + int maxDimension); +}; + +} // namespace zigzag_persistence +} // namespace Gudhi + +#endif // CONCEPT_ZZ_OSCI_RIPS_RANGE_H_ diff --git a/src/Zigzag_persistence/concept/PointRange.h b/src/Zigzag_persistence/concept/PointRange.h new file mode 100644 index 0000000000..b09a9123e7 --- /dev/null +++ b/src/Zigzag_persistence/concept/PointRange.h @@ -0,0 +1,67 @@ +/* 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): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#ifndef CONCEPT_ZZ_POINT_RANGE_H_ +#define CONCEPT_ZZ_POINT_RANGE_H_ + +/** @file PointRange.h + * @brief Contains @ref Gudhi::zigzag_persistence::Point and @ref Gudhi::zigzag_persistence::PointRange concept. + */ + +namespace Gudhi { +namespace zigzag_persistence { + +/** + * @brief Data structure representing a point of fixed dimension. The structure of the point does not matter + * it-self as long as it corresponds to the input type of the @ref DistanceFunction concept. + */ +class Point{}; + +/** + * @brief Range of @ref Point. Used with @ref Oscillating_rips_edge_range::Order_policy::FARTHEST_POINT_ORDERING order policy, + * it has to be a random access range. + */ +class PointRange { + public: + /** + * @brief Returns begin iterator. + * + * @return Begin iterator. + */ + auto begin(); + + /** + * @brief Returns end iterator. + * + * @return End iterator. + */ + auto end(); + + /** + * @brief Returns size of the range. + * + * @return Size of the range. + */ + std::size_t size(); + + /** + * @brief Necessary only if used with @ref Oscillating_rips_edge_range::Order_policy::FARTHEST_POINT_ORDERING. Returns the element + * at the given index. + * + * @param index Index of the element to return. + * @return Point at index @p index. + */ + Point operator[](std::size_t index); +}; + +} // namespace zigzag_persistence +} // namespace Gudhi + +#endif // CONCEPT_ZZ_POINT_RANGE_H_ diff --git a/src/Zigzag_persistence/concept/StableFilteredComplex.h b/src/Zigzag_persistence/concept/StableFilteredComplex.h new file mode 100644 index 0000000000..6d430ec52a --- /dev/null +++ b/src/Zigzag_persistence/concept/StableFilteredComplex.h @@ -0,0 +1,121 @@ +/* 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): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#ifndef CONCEPT_ZZ_STABLE_COMPLEX_TYPE_H_ +#define CONCEPT_ZZ_STABLE_COMPLEX_TYPE_H_ + +/** @file StableFilteredComplex.h + * @brief Contains @ref Gudhi::zigzag_persistence::StableFilteredComplex concept. + */ + +namespace Gudhi { +namespace zigzag_persistence { + +/** + * @brief Data structure storing the simplices and their filtration values in the current complex. + * The concept is realized for example by @ref Gudhi::Simplex_tree < Gudhi::Simplex_tree_options_oscillating_rips >. + * + * This concept is not incompatible with the @ref ZigzagComplex concept, so it is possible to use the same complex + * for the @ref Oscillating_rips_iterator class and the @ref ZigzagPersistence class, as long as it realizes the two + * concepts (which is the case for @ref Gudhi::Simplex_tree < Gudhi::Simplex_tree_options_oscillating_rips >). + * This avoids having two complexes in memory and give the possibility to directly use the simplex handle instead + * of the vector of vertices to communicate between the two classes. + */ +class StableFilteredComplex { + public: + /** + * @brief Integer type that needs to be long enough to store the numbers of arrows in the zigzag filtration. + */ + typename Simplex_key; + + /** + * @brief Handle to specify a simplex. Different from the @ref ZigzagComplex concept, + * the simplex handles have to be stable, that is, they do not invalidate when a simplex is + * added or removed from the complex (except for the removed simplices them selves of course). + */ + typename Simplex_handle; + + /** + * @brief Type for filtration values. Usually 'double'. + */ + typename Filtration_value; + + /** + * @brief Removes the given simplex. Assumes that the simplex is maximal and can be safely removed. + * + * @param sh Simplex handle representing the simplex to remove. + */ + void remove_maximal_simplex(Simplex_handle sh); + + /** + * @brief Adds a vertex or an edge in a flag complex, as well as all + * simplices of its star, defined to maintain the property + * of the complex to be a flag complex, truncated at dimension dim_max. + * + * @param u ID of one end of the edge. + * @param v ID of the other end of the edge. If @p u == @p v, then the input is considered as a vertex. + * @param fil Filtration value of the edge. + * @param dim_max Maximal dimension of the expansion. If set to -1, the expansion goes as far as possible. + * @param added_simplices Container for all new simplices induced by the insertion of the edge. + * If not empty at start, the content of the container should @b not be erased by the method. + */ + void insert_edge_as_flag(int u, + int v, + Filtration_value fil, + int dim_max, + std::vector& added_simplices); + + /** + * @brief Returns the key associated to the given simplex. + * + * @param sh Simplex handle representing the simplex. + * @return The key. + */ + Simplex_key key(Simplex_handle sh); + + /** + * @brief Assignes the given value to the given simplex as a key. + * + * @param sh Simplex handle representing the simplex. + * @param key Values to associate as key. + */ + void assign_key(Simplex_handle sh, Simplex_key key); + + /** + * @brief Finds the given simplex in the complex and returns the associated simplex handle. + * + * @tparam VertexRange Range over the vertices of a simplex. + * @param simplex Simplex to find represented by its vertices. + * @return The simplex handle associated to @p simplex if the simplex is found, @ref null_simplex() otherwise. + */ + template + Simplex_handle find(const VertexRange& simplex); + + /** + * @brief Returns the filtration value of the given simplex. + * + * @param sh + * @return Filtration_value + */ + Filtration_value filtration(Simplex_handle sh); + + /** + * @brief Returns a range (with begin() and end() methods) over the star of the given simplex, including the simplex. + * + * @param simplex Simplex to compute the star from. + * @return A iterable range over the star of @p simplex. + */ + auto star_simplex_range(const Simplex_handle simplex); +}; + +} // namespace zigzag_persistence +} // namespace Gudhi + +#endif // CONCEPT_ZZ_STABLE_COMPLEX_TYPE_H_ diff --git a/src/Zigzag_persistence/concept/ZigzagComplex.h b/src/Zigzag_persistence/concept/ZigzagComplex.h index 6b0faa757b..0a76441a4b 100644 --- a/src/Zigzag_persistence/concept/ZigzagComplex.h +++ b/src/Zigzag_persistence/concept/ZigzagComplex.h @@ -20,8 +20,7 @@ namespace zigzag_persistence { /** * @brief Data structure storing the simplices in the current complex. - * The concept is realized for example by @ref Gudhi::Simplex_tree < Gudhi::Simplex_tree_options_zigzag_persistence > - * or @ref Gudhi::Simplex_tree < Gudhi::Simplex_tree_options_zigzag_persistence_long >. + * The concept is realized for example by @ref Gudhi::Simplex_tree < Gudhi::Simplex_tree_options_zigzag_persistence >. */ class ZigzagComplex { public: diff --git a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h index 5fe392a659..779ad4cecf 100644 --- a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h +++ b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h @@ -34,6 +34,12 @@ namespace zigzag_persistence { * which can be processed without overreaching memory space. For this purpose, it is possible to feed the module with * information about the filtration "on the fly" to avoid loading the whole filtration at once. Information about the * current complex and current barcode can be retrieved between any steps. + * + * \subsection zigzagrips Oscillating Rips + * + * A typical example of zigzag filtrations are oscillating rips filtrations. Similar to standart Rips filtrations, they + * completely depend on their edges. But here we look at neighborhoods ''oscillating'' in size around the points, so + * edges are added but also removed. We refer for example to \cite osc_zz. * * \subsection zigzagexamples Examples * @@ -43,6 +49,9 @@ namespace zigzag_persistence { * * \li \gudhi_example_link{Zigzag_persistence,example_zzfiltration_from_file.cpp} - An example of a "stream-like" usage * by reading of the filtration from a file. + * + * \li \gudhi_example_link{Zigzag_persistence,example_oscillating_rips_persistence.cpp} - An example of a how to + * compute the persistence of an oscillating rips filtration. * * @} */ diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h index 5892f6e2d8..a5d75e7d3c 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h @@ -304,7 +304,7 @@ class Zigzag_persistence * @tparam SimplexRange Range type needing begin and end members. * @tparam FiltrationRange Range type needing begin and end members. * @param simplices Simplices which are inserted, represented by their vertices. They have to be in the order they - * are inserted in the filtration and ``contiguous'' in the filtration, that is, no other simplex + * are inserted in the filtration and ''contiguous'' in the filtration, that is, no other simplex * which is not in the range is inserted or removed between two simplices in the range. * @param filtration_values Filtration values associated to the insertion of the given simplices. * The order has to correspond to the order in @a simplices. Their values have to ascending in this order and @@ -326,7 +326,7 @@ class Zigzag_persistence * @tparam SimplexRange Range type needing begin and end members. * @tparam FiltrationRange Range type needing begin and end members. * @param simplices Simplices which are removed, represented by their vertices. They have to be in the order they - * are removed in the filtration and ``contiguous'' in the filtration, that is, no other simplex + * are removed in the filtration and ''contiguous'' in the filtration, that is, no other simplex * which is not in the range is inserted or removed between two simplices in the range. * @param filtration_values Filtration values associated to the removal of the given simplices. Has therefore the * same size as @p simplices. The order has to correspond to the order in @p simplices. Their values have to @@ -349,7 +349,7 @@ class Zigzag_persistence * @tparam FiltrationRangeIterators Forward iterator of a range. * @param simplex_range_start Iterator pointing to the begining of the range of simplices to insert. * The simplices should be represented by their vertices. They have to be in the order they - * are inserted in the filtration and ``contiguous'' in the filtration, that is, no other simplex + * are inserted in the filtration and ''contiguous'' in the filtration, that is, no other simplex * which is not in the range is inserted or removed between two simplices in the range. * @param simplex_range_end Iterator pointing to the end of the range of simplices to insert. * @param filtration_range_start Iterator pointing to the begining of the range of filtration values. The range is @@ -371,7 +371,7 @@ class Zigzag_persistence * @tparam FiltrationRangeIterators Forward iterator of a range. * @param simplex_range_start Iterator pointing to the begining of the range of simplices to remove. * The simplices should be represented by their vertices. They have to be in the order they - * are removed in the filtration and ``contiguous'' in the filtration, that is, no other simplex + * are removed in the filtration and ''contiguous'' in the filtration, that is, no other simplex * which is not in the range is inserted or removed between two simplices in the range. * @param simplex_range_end Iterator pointing to the end of the range of simplices to remove. * @param filtration_range_start Iterator pointing to the begining of the range of filtration values. The range is @@ -387,11 +387,11 @@ class Zigzag_persistence } /** - * @brief Updates the zigzag persistence diagram after the given simplex was ``manually'' inserted in the complex + * @brief Updates the zigzag persistence diagram after the given simplex was ''manually'' inserted in the complex * obtained with @ref get_complex. * * @param simplex Simplex handle of the simplex having been inserted. A filtration value and a key must be assigned - * to it. The key must correspond to the ``arrow number'' inserting the simplex, i.e., its position in the filtration. + * to it. The key must correspond to the ''arrow number'' inserting the simplex, i.e., its position in the filtration. * * @warning Other simplices can have been inserted in the complex before calling this method, there will simply not * be token into account as long as the method is not called for them. It it just important that a key was assigned @@ -443,7 +443,7 @@ class Zigzag_persistence } /** - * @brief Returns the ``index persistence diagram'' of the current filtration, that is, the pairs of atomic arrow + * @brief Returns the ''index persistence diagram'' of the current filtration, that is, the pairs of atomic arrow * numbers corresponding to a birth-death pair. Does not contain points at infinity, only the cycle classes which * already died are represented. * diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h index 3703fdafd2..4f05d1fc42 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h @@ -333,7 +333,7 @@ class Oscillating_rips_edge_range { Filtration_value mu_; /**< Upper multiplicator. */ Zigzag_edge currentEdge_; /**< Stores the current edge in the range. */ size_t epsilonIndex_, rowIndex_, columnIndex_; /**< Indices indicating the next position in the range. */ - bool inPositiveDirection_, insertVertex_; /**< Next direction and indicates if next ``edge'' is a vertex. */ + bool inPositiveDirection_, insertVertex_; /**< Next direction and indicates if next ''edge'' is a vertex. */ /** * @brief Mandatory for the boost::iterator_facade inheritance. Indicates if to iterators are equal. @@ -547,7 +547,8 @@ class Oscillating_rips_edge_range { Filtration_value mu, const PointRange& points, DistanceFunction&& distance, - Order_policy orderPolicy = Order_policy::FARTHEST_POINT_ORDERING) { + Order_policy orderPolicy = Order_policy::FARTHEST_POINT_ORDERING) + { std::vector > edgeFiltration; std::vector epsilonValues; std::vector > > distanceMatrix; @@ -560,10 +561,6 @@ class Oscillating_rips_edge_range { // only at the very last step of the oRzz filtration. std::vector > > edgesAdded, edgesRemoved; - // auto it = std::upper_bound(distanceMatrix[1].begin(), distanceMatrix[1].end(), - // std::pair(distanceMatrix.size(), mu * epsilonValues[0]), - // Point_distance_comp()); - // std::cout << "start vect colind: " << (it - distanceMatrix[1].begin()) << ", (" << it->first << ", " << it->second << "), (" << distanceMatrix[1].begin()->first << ", " << distanceMatrix[1].begin()->second << ")\n"; size_t number_of_arrows = _compute_edges(nu, mu, epsilonValues, distanceMatrix, edgesAdded, edgesRemoved); // Now, sort edges according to lengths, and put everything in edgeFiltration @@ -740,7 +737,7 @@ class Oscillating_rips_edge_range { private: /** - * @brief Default constructor. Should not be called and therfore private. Use as a ``static'' class only. + * @brief Default constructor. Should not be called and therfore private. Use as a ''static'' class only. */ Oscillating_rips_edge_range(){}; @@ -838,7 +835,8 @@ class Oscillating_rips_edge_range { * @return Vector of decreasing epsilon values ending with 0. */ template - static std::vector _compute_epsilon_values(const PointRange& sortedPoints, DistanceFunction&& distance) { + static std::vector _compute_epsilon_values(const PointRange& sortedPoints, + DistanceFunction&& distance) { size_t n = sortedPoints.size(); std::vector eps_range(n, std::numeric_limits::infinity()); @@ -897,7 +895,8 @@ class Oscillating_rips_edge_range { */ template static std::vector > > _compute_distance_matrix( - const PointRange& sortedPoints, DistanceFunction&& distance) { + const PointRange& sortedPoints, DistanceFunction&& distance) + { std::vector > > distanceMatrix(sortedPoints.size()); #ifdef GUDHI_USE_TBB tbb::parallel_for(size_t(0), sortedPoints.size(), [&](size_t i) { @@ -982,7 +981,6 @@ class Oscillating_rips_edge_range { } else { edgesAdded[i].emplace_back(it->first, j, epsilonValues[i], true); } - // std::cout << j << ", " << (it - distanceMatrix[j].begin()) << ", " << it->first << "\n"; ++number_of_arrows; } } @@ -1001,7 +999,6 @@ class Oscillating_rips_edge_range { } else { edgesAdded[i].emplace_back(it->first, i + 1, epsilonValues[i], true); } - // std::cout << (i + 1) << ", " << (it - distanceMatrix[i+1].begin()) << ", " << it->first << "\n"; ++number_of_arrows; } @@ -1023,13 +1020,11 @@ class Oscillating_rips_edge_range { if (it->second <= nu * epsilonValues[i + 1]) { break; } - // edgesRemoved[i].emplace_back(it->first, j, epsilonValues[i+1], false); if constexpr (EdgeModifier::isActive_){ edgesRemoved[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), false); } else { edgesRemoved[i].emplace_back(it->first, j, epsilonValues[i], false); } - // std::cout << j << ", " << (it - distanceMatrix[j].begin()) << ", " << it->first << "\n"; ++number_of_arrows; } } @@ -1072,16 +1067,12 @@ class Oscillating_rips_edge_range { // first striclty longer edge it = std::upper_bound(distanceMatrix[i + 1].begin(), distanceMatrix[i + 1].end(), std::pair(n, mu * epsilonValues[i]), Point_distance_comp()); -// if (i == 0){ -// std::cout << "start vect colind2: " << (it - distanceMatrix[1].begin()) << ", (" << it->first << ", " << it->second << "), (" << distanceMatrix[1].begin()->first << ", " << distanceMatrix[1].begin()->second << ")\n"; -// } while (it != distanceMatrix[i + 1].begin()) { --it; if constexpr (EdgeModifier::isActive_) { edgesAdded[i].emplace_back(it->first, i + 1, EdgeModifier::apply_modifier(epsilonValues[i]), true); } else { edgesAdded[i].emplace_back(it->first, i + 1, epsilonValues[i], true); - // if (i==0) std::cout << "added: " << it->first << ", " << (i + 1) << ", " << epsilonValues[i] << "\n"; } ++number_of_arrows; } @@ -1104,7 +1095,6 @@ class Oscillating_rips_edge_range { if (it->second <= nu * epsilonValues[i + 1]) { break; } - // edgesRemoved[i].emplace_back(it->first, j, epsilonValues[i+1], false); if constexpr (EdgeModifier::isActive_){ edgesRemoved[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), false); } else { @@ -1121,7 +1111,7 @@ class Oscillating_rips_edge_range { /** * @brief Sorts canonically the edges: as much as possible, edges should be removed in - * the reverse order of their insertion. We decide to insert shorted edges first, + * the reverse order of their insertion. We decide to insert shortest edges first, * with increasing lexicographical order, and remove larger edges first, with * decreasing lexicographic order. * @@ -1521,7 +1511,7 @@ class Oscillating_rips_simplex_range { private: /** - * @brief Default constructor. Should not be called and therfore private. Use as a ``static'' class only. + * @brief Default constructor. Should not be called and therfore private. Use as a ''static'' class only. */ Oscillating_rips_simplex_range(){}; }; diff --git a/src/common/doc/examples.h b/src/common/doc/examples.h index 913d95b0f8..cef69f9b7c 100644 --- a/src/common/doc/examples.h +++ b/src/common/doc/examples.h @@ -132,5 +132,6 @@ * \section Zigzag_persistence_example_section Zigzag_persistence * @example example_simple_zigzag_filtration.cpp * @example example_zzfiltration_from_file.cpp + * @example example_oscillating_rips_persistence.cpp */ From 67637b95b63eb39ac0a97c3c7898e2f64e5833b7 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Thu, 31 Aug 2023 16:59:02 +0200 Subject: [PATCH 18/51] cleanup --- .../benchmark/CMakeLists.txt | 19 - .../benchmark/Oscillating_rips_benchmark.cpp | 213 -- .../Oscillating_rips_comp_benchmark.cpp | 317 -- .../benchmark/ext_zz/dionysus/chain.h | 153 - .../benchmark/ext_zz/dionysus/chain.hpp | 188 -- .../ext_zz/dionysus/clearing-reduction.h | 45 - .../ext_zz/dionysus/clearing-reduction.hpp | 60 - .../benchmark/ext_zz/dionysus/cnpy.h | 241 -- .../ext_zz/dionysus/cohomology-persistence.h | 116 - .../dionysus/cohomology-persistence.hpp | 61 - .../benchmark/ext_zz/dionysus/common.h | 25 - .../benchmark/ext_zz/dionysus/diagram.h | 114 - .../benchmark/ext_zz/dionysus/distances.h | 93 - .../benchmark/ext_zz/dionysus/distances.hpp | 30 - .../benchmark/ext_zz/dionysus/dlog/progress.h | 57 - .../benchmark/ext_zz/dionysus/fields/q.h | 63 - .../benchmark/ext_zz/dionysus/fields/z2.h | 31 - .../benchmark/ext_zz/dionysus/fields/zp.h | 55 - .../benchmark/ext_zz/dionysus/filtration.h | 124 - .../benchmark/ext_zz/dionysus/format.h | 8 - .../ext_zz/dionysus/format/format.cc | 1156 -------- .../benchmark/ext_zz/dionysus/format/format.h | 2546 ----------------- .../benchmark/ext_zz/dionysus/grid/box.h | 136 - .../benchmark/ext_zz/dionysus/grid/box.hpp | 141 - .../benchmark/ext_zz/dionysus/grid/grid.h | 143 - .../benchmark/ext_zz/dionysus/grid/point.h | 132 - .../benchmark/ext_zz/dionysus/grid/vertices.h | 86 - .../ext_zz/dionysus/matrix-filtration.h | 120 - .../ext_zz/dionysus/omni-field-persistence.h | 145 - .../dionysus/omni-field-persistence.hpp | 250 -- .../benchmark/ext_zz/dionysus/opts/opts.h | 499 ---- .../ext_zz/dionysus/ordinary-persistence.h | 64 - .../benchmark/ext_zz/dionysus/pair-recorder.h | 78 - .../ext_zz/dionysus/reduced-matrix.h | 170 -- .../ext_zz/dionysus/reduced-matrix.hpp | 78 - .../benchmark/ext_zz/dionysus/reduction.h | 109 - .../dionysus/relative-homology-zigzag.h | 84 - .../dionysus/relative-homology-zigzag.hpp | 122 - .../benchmark/ext_zz/dionysus/rips.h | 147 - .../benchmark/ext_zz/dionysus/rips.hpp | 162 -- .../benchmark/ext_zz/dionysus/row-reduction.h | 54 - .../ext_zz/dionysus/row-reduction.hpp | 103 - .../benchmark/ext_zz/dionysus/simplex.h | 280 -- .../ext_zz/dionysus/sparse-row-matrix.h | 184 -- .../ext_zz/dionysus/sparse-row-matrix.hpp | 103 - .../ext_zz/dionysus/standard-reduction.h | 44 - .../ext_zz/dionysus/standard-reduction.hpp | 47 - .../benchmark/ext_zz/dionysus/trails-chains.h | 17 - .../ext_zz/dionysus/zigzag-persistence.h | 142 - .../ext_zz/dionysus/zigzag-persistence.hpp | 541 ---- .../benchmark/ext_zz/fzz/fzz.cpp | 206 -- .../benchmark/ext_zz/fzz/fzz.h | 87 - .../ext_zz/phat/algorithms/chunk_reduction.h | 223 -- .../ext_zz/phat/algorithms/row_reduction.h | 56 - .../algorithms/spectral_sequence_reduction.h | 80 - .../phat/algorithms/standard_reduction.h | 47 - .../ext_zz/phat/algorithms/twist_reduction.h | 51 - .../benchmark/ext_zz/phat/boundary_matrix.h | 343 --- .../ext_zz/phat/compute_persistence_pairs.h | 128 - .../benchmark/ext_zz/phat/helpers/dualize.h | 74 - .../benchmark/ext_zz/phat/helpers/misc.h | 75 - .../phat/helpers/thread_local_storage.h | 52 - .../benchmark/ext_zz/phat/persistence_pairs.h | 155 - .../representations/abstract_pivot_column.h | 102 - .../representations/bit_tree_pivot_column.h | 165 -- .../phat/representations/full_pivot_column.h | 100 - .../phat/representations/heap_pivot_column.h | 126 - .../representations/sparse_pivot_column.h | 79 - .../ext_zz/phat/representations/vector_heap.h | 170 -- .../ext_zz/phat/representations/vector_list.h | 101 - .../ext_zz/phat/representations/vector_set.h | 99 - .../phat/representations/vector_vector.h | 107 - src/Zigzag_persistence/example/CMakeLists.txt | 4 - .../example/comparison_for_tests.cpp | 392 --- 74 files changed, 12918 deletions(-) delete mode 100644 src/Zigzag_persistence/benchmark/CMakeLists.txt delete mode 100644 src/Zigzag_persistence/benchmark/Oscillating_rips_benchmark.cpp delete mode 100644 src/Zigzag_persistence/benchmark/Oscillating_rips_comp_benchmark.cpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.hpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.hpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/cnpy.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.hpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/common.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/diagram.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.hpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/dlog/progress.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/q.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/z2.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/zp.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/filtration.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/format.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.cc delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.hpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/grid.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/point.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/vertices.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/matrix-filtration.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.hpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/opts/opts.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/ordinary-persistence.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/pair-recorder.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.hpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduction.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.hpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.hpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.hpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/simplex.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.hpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.hpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/trails-chains.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.hpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.cpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/chunk_reduction.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/row_reduction.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/spectral_sequence_reduction.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/standard_reduction.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/twist_reduction.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/boundary_matrix.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/compute_persistence_pairs.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/dualize.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/misc.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/thread_local_storage.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/persistence_pairs.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/abstract_pivot_column.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/bit_tree_pivot_column.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/full_pivot_column.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/heap_pivot_column.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/sparse_pivot_column.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_heap.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_list.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_set.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_vector.h delete mode 100644 src/Zigzag_persistence/example/comparison_for_tests.cpp diff --git a/src/Zigzag_persistence/benchmark/CMakeLists.txt b/src/Zigzag_persistence/benchmark/CMakeLists.txt deleted file mode 100644 index 3d21cbe72a..0000000000 --- a/src/Zigzag_persistence/benchmark/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -project(Zigzag_benchmark) - -find_package(benchmark REQUIRED) - -add_executable(Oscillating_rips_benchmark Oscillating_rips_benchmark.cpp) -target_link_libraries(Oscillating_rips_benchmark benchmark::benchmark) -if(TARGET TBB::tbb) - target_link_libraries(Oscillating_rips_benchmark TBB::tbb) -endif() - -add_executable(Comp_benchmark Oscillating_rips_comp_benchmark.cpp ./ext_zz/fzz/fzz.cpp) -target_link_libraries(Comp_benchmark benchmark::benchmark) -target_include_directories(Comp_benchmark PUBLIC . ./ext_zz) -if(TARGET TBB::tbb) - target_link_libraries(Comp_benchmark TBB::tbb) -endif() -target_compile_options(Comp_benchmark PUBLIC "-fopenmp") -target_link_options(Comp_benchmark PUBLIC "-fopenmp") - diff --git a/src/Zigzag_persistence/benchmark/Oscillating_rips_benchmark.cpp b/src/Zigzag_persistence/benchmark/Oscillating_rips_benchmark.cpp deleted file mode 100644 index 179eecaebb..0000000000 --- a/src/Zigzag_persistence/benchmark/Oscillating_rips_benchmark.cpp +++ /dev/null @@ -1,213 +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): Hannah Schreiber - * - * Copyright (C) 2023 Inria - * - * Modification(s): - * - YYYY/MM Author: Description of the modification - */ - -#include -#include -#include -#include // for pair -#include - -#include -#include -#include -#include -#include -#include - -using ST = Gudhi::Simplex_tree; -using Filtration_value = ST::Filtration_value; -using Simplex_handle = ST::Simplex_handle; -using Square = Gudhi::zigzag_persistence::Square_root_edge_modifier; -using ZE = Gudhi::zigzag_persistence::Zigzag_edge; -using ORE = Gudhi::zigzag_persistence::Oscillating_rips_edge_range; -using OR = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range; -using ORv = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range::iterator>; -using Point = std::vector; - -std::vector build_point_cloud(unsigned int numberOfPoints, int seed) { - std::vector finalPoints; - std::set points; - std::random_device dev; - std::mt19937 rng(dev()); - if (seed > -1) rng.seed(seed); - std::uniform_real_distribution dist(0, 10); - - for (unsigned int i = 0; i < numberOfPoints; ++i) { - auto res = points.insert({dist(rng), dist(rng)}); - while (!res.second) { - res = points.insert({dist(rng), dist(rng)}); - } - finalPoints.push_back(*res.first); - } - - return finalPoints; -} - -// static void ORP_with_custom_iterator(benchmark::State& state) { -// double nu = state.range(0); -// double mu = state.range(1); -// int maxDim = state.range(2); -// unsigned int numberOfPoints = state.range(3); -// int seed = 0; - -// std::vector points = build_point_cloud(numberOfPoints, seed); - -// for (auto _ : state) { -// auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence(points, nu, mu, maxDim, ORE::Order_policy::FARTHEST_POINT_ORDERING); -// } -// } -// BENCHMARK(ORP_with_custom_iterator) -// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), -// benchmark::CreateDenseRange(3, 5, 1), -// benchmark::CreateRange(2, 5, 4), -// benchmark::CreateRange(5, 5, 4)}) -// ->Unit(benchmark::kMicrosecond); -// BENCHMARK(ORP_with_custom_iterator) -// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), -// benchmark::CreateDenseRange(3, 5, 1), -// benchmark::CreateRange(4, 50, 4), -// benchmark::CreateRange(20, 64, 4)}) -// ->Unit(benchmark::kMillisecond); -// BENCHMARK(ORP_with_custom_iterator) -// ->ArgsProduct({benchmark::CreateDenseRange(1, 2, 1), -// benchmark::CreateDenseRange(3, 4, 1), -// benchmark::CreateRange(4, 50, 4), -// benchmark::CreateRange(256, 500, 4)}) -// ->Unit(benchmark::kMillisecond); - -// static void ORP_with_vector_iterator(benchmark::State& state) { -// double nu = state.range(0); -// double mu = state.range(1); -// int maxDim = state.range(2); -// unsigned int numberOfPoints = state.range(3); -// int seed = 0; - -// std::vector points = build_point_cloud(numberOfPoints, seed); - -// for (auto _ : state) { -// auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence< -// std::vector, Gudhi::zigzag_persistence::Edge_range_type::VECTOR>(points, nu, mu, maxDim, ORE::Order_policy::FARTHEST_POINT_ORDERING); -// } -// } -// BENCHMARK(ORP_with_vector_iterator) -// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), -// benchmark::CreateDenseRange(3, 5, 1), -// benchmark::CreateRange(2, 5, 4), -// benchmark::CreateRange(5, 5, 4)}) -// ->Unit(benchmark::kMicrosecond); -// BENCHMARK(ORP_with_vector_iterator) -// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), -// benchmark::CreateDenseRange(3, 5, 1), -// benchmark::CreateRange(4, 50, 4), -// benchmark::CreateRange(20, 64, 4)}) -// ->Unit(benchmark::kMillisecond); -// BENCHMARK(ORP_with_vector_iterator) -// ->ArgsProduct({benchmark::CreateDenseRange(1, 2, 1), -// benchmark::CreateDenseRange(3, 4, 1), -// benchmark::CreateRange(4, 50, 4), -// benchmark::CreateRange(256, 500, 4)}) -// ->Unit(benchmark::kMillisecond); - -// static void ORP_with_custom_iterator_random(benchmark::State& state) { -// double nu = state.range(0); -// double mu = state.range(1); -// int maxDim = state.range(2); -// unsigned int numberOfPoints = state.range(3); -// int seed = 0; - -// std::vector points = build_point_cloud(numberOfPoints, seed); - -// for (auto _ : state) { -// auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence(points, nu, mu, maxDim, ORE::Order_policy::RANDOM_POINT_ORDERING); -// } -// } -// BENCHMARK(ORP_with_custom_iterator_random) -// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), -// benchmark::CreateDenseRange(3, 5, 1), -// benchmark::CreateRange(4, 50, 4), -// benchmark::CreateDenseRange(5, 15, 5)}) -// ->Unit(benchmark::kMillisecond); -// BENCHMARK(ORP_with_custom_iterator_random) -// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), -// benchmark::CreateDenseRange(3, 4, 1), -// benchmark::CreateRange(4, 50, 4), -// benchmark::CreateDenseRange(20, 50, 30)}) -// ->Unit(benchmark::kMillisecond); - -static void ORP_with_vector_iterator_random(benchmark::State& state) { - double nu = state.range(0); - double mu = state.range(1); - int maxDim = state.range(2); - unsigned int numberOfPoints = state.range(3); - int seed = 0; - - std::vector points = build_point_cloud(numberOfPoints, seed); - - for (auto _ : state) { - auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence< - std::vector, Gudhi::zigzag_persistence::Edge_range_type::VECTOR>(points, nu, mu, maxDim, ORE::Order_policy::RANDOM_POINT_ORDERING); - } -} -BENCHMARK(ORP_with_vector_iterator_random) - ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), - benchmark::CreateDenseRange(3, 5, 1), - benchmark::CreateRange(4, 50, 4), - benchmark::CreateDenseRange(5, 15, 5)}) - ->Unit(benchmark::kMillisecond); -BENCHMARK(ORP_with_vector_iterator_random) - ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), - benchmark::CreateDenseRange(3, 4, 1), - benchmark::CreateRange(4, 50, 4), - benchmark::CreateDenseRange(20, 50, 30)}) - ->Unit(benchmark::kMillisecond); - -// static void ORP_with_custom_iterator_ordered(benchmark::State& state) { -// double nu = state.range(0); -// double mu = state.range(1); -// int maxDim = state.range(2); -// unsigned int numberOfPoints = state.range(3); -// int seed = 0; - -// std::vector points = build_point_cloud(numberOfPoints, seed); - -// for (auto _ : state) { -// auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence(points, nu, mu, maxDim, ORE::Order_policy::ALREADY_ORDERED); -// } -// } -// BENCHMARK(ORP_with_custom_iterator_ordered) -// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), -// benchmark::CreateDenseRange(3, 5, 1), -// benchmark::CreateRange(4, 50, 4), -// benchmark::CreateRange(5, 50, 4)}) -// ->Unit(benchmark::kMillisecond); - -// static void ORP_with_vector_iterator_ordered(benchmark::State& state) { -// double nu = state.range(0); -// double mu = state.range(1); -// int maxDim = state.range(2); -// unsigned int numberOfPoints = state.range(3); -// int seed = 0; - -// std::vector points = build_point_cloud(numberOfPoints, seed); - -// for (auto _ : state) { -// auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence< -// std::vector, Gudhi::zigzag_persistence::Edge_range_type::VECTOR>(points, nu, mu, maxDim, ORE::Order_policy::ALREADY_ORDERED); -// } -// } -// BENCHMARK(ORP_with_vector_iterator_ordered) -// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), -// benchmark::CreateDenseRange(3, 5, 1), -// benchmark::CreateRange(4, 50, 4), -// benchmark::CreateRange(5, 50, 4)}) -// ->Unit(benchmark::kMillisecond); - -BENCHMARK_MAIN(); - diff --git a/src/Zigzag_persistence/benchmark/Oscillating_rips_comp_benchmark.cpp b/src/Zigzag_persistence/benchmark/Oscillating_rips_comp_benchmark.cpp deleted file mode 100644 index 6385fe8919..0000000000 --- a/src/Zigzag_persistence/benchmark/Oscillating_rips_comp_benchmark.cpp +++ /dev/null @@ -1,317 +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): Hannah Schreiber - * - * Copyright (C) 2023 Inria - * - * Modification(s): - * - YYYY/MM Author: Description of the modification - */ - -#include -#include -#include -#include // for pair -#include - -#include -#include -#include -#include -#include -#include - -#include "ext_zz/fzz/fzz.h" -#include "ext_zz/dionysus/simplex.h" -#include "ext_zz/dionysus/filtration.h" -#include "ext_zz/dionysus/zigzag-persistence.h" - -using ST = Gudhi::Simplex_tree; -using Vertex_handle = ST::Vertex_handle; -using Filtration_value = ST::Filtration_value; -using Simplex_handle = ST::Simplex_handle; -using Square = Gudhi::zigzag_persistence::Square_root_edge_modifier; -using ZE = Gudhi::zigzag_persistence::Zigzag_edge; -using ORE = Gudhi::zigzag_persistence::Oscillating_rips_edge_range; -using OR = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range; -using ORv = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range::const_iterator>; -using Point = std::vector; - -using DField = dionysus::Z2Field; -using Simplex = dionysus::Simplex<>; -using DFiltration = dionysus::Filtration; -using DZZ = dionysus::ZigzagPersistence; -using DIndex = typename DZZ::Index; -using DChain = dionysus::ChainEntry; -using DIChain = dionysus::ChainEntry; - -struct Simplex_tree_options_test { - typedef Gudhi::linear_indexing_tag Indexing_tag; - typedef int Vertex_handle; - typedef double Filtration_value; - typedef std::int32_t Simplex_key; - static const bool store_key = true; - static const bool store_filtration = true; - static const bool contiguous_vertices = true; - static const bool link_nodes_by_label = false; - static const bool simplex_handle_strong_validity = false; -}; - -// using ST_std = Gudhi::Simplex_tree; -using ST_std = Gudhi::Simplex_tree; -using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; -using Barcode = std::vector; - -std::vector build_point_cloud(unsigned int numberOfPoints, int seed) { - std::vector finalPoints; - std::set points; - std::random_device dev; - std::mt19937 rng(dev()); - if (seed > -1) rng.seed(seed); - std::uniform_real_distribution dist(0, 10); - - for (unsigned int i = 0; i < numberOfPoints; ++i) { - auto res = points.insert({dist(rng), dist(rng)}); - while (!res.second) { - res = points.insert({dist(rng), dist(rng)}); - } - finalPoints.push_back(*res.first); - } - - return finalPoints; -} - -std::vector > compute_with_dionysus(const std::vector& edges, int maxDim) { - ST st; - DField k; - std::unordered_map indices; - DZZ persistence(k); - std::vector > res; - - std::set essentials; - - unsigned int op = 0; - unsigned int idx = 0; - - auto start = edges.begin(); - auto end = edges.end(); - for (const auto& t : ORv::get_iterator_range(start, end, st, maxDim)) { - auto r = st.simplex_vertex_range(std::get<0>(t)); - std::vector simplex(r.begin(), r.end()); - Simplex c(simplex); - DIndex pair; - if (std::get<2>(t)) { - indices.try_emplace(c, idx++); - // int dim = boost::distance(c.boundary(persistence.field())); - // dim = dim == 0 ? 0 : dim -1; - // fmt::print("[{}] Adding: {} : {}\n", op, c, dim); - pair = - persistence.add(c.boundary(persistence.field()) | boost::adaptors::transformed([&indices](const DChain& e) { - return DIChain(e.element(), indices.find(e.index())->second); - })); - } else { - // fmt::print("[{}] Removing: {} : {}\n", op, c, boost::distance(c.boundary(persistence.field())) - 1); - auto idxIt = indices.find(c); - pair = persistence.remove(idxIt->second); - indices.erase(idxIt); - } - - if (pair != DZZ::unpaired()) { - // fmt::print("{} - {}\n", pair, op); - res.emplace_back(pair, op); - essentials.erase(pair); - } else { - essentials.insert(essentials.end(), op); - } - op++; - } - - for (unsigned int v : essentials) { - // fmt::print("{} - inf\n", v); - res.emplace_back(v, op); - } - - return res; -} - -std::vector > compute_with_fzz(const std::vector& edges, - int maxDim) { - std::vector > persistence; - FZZ::FastZigzag fzz; - std::vector > simplices; - std::vector dirs; - ST st; - - auto start = edges.begin(); - auto end = edges.end(); - for (const auto& t : ORv::get_iterator_range(start, end, st, maxDim)){ - auto r = st.simplex_vertex_range(std::get<0>(t)); - simplices.emplace_back(r.begin(), r.end()); - dirs.push_back(std::get<2>(t)); - } - - fzz.compute(simplices, dirs, &persistence); - - std::sort(persistence.begin(), persistence.end(), - [](const std::tuple& p1, - const std::tuple& p2) { - if (std::get<1>(p1) == std::get<1>(p2)) { - return std::get<0>(p1) < std::get<0>(p2); - } - - return std::get<1>(p1) < std::get<1>(p2); - }); - - return persistence; -} - -Barcode compute_with_gudhi_v2(const std::vector& points, double nu, double mu, int maxDim) { - ZP zp; - ST st; - - auto start = ORE::begin(nu, mu, points, Gudhi::Euclidean_distance(), ORE::Order_policy::FARTHEST_POINT_ORDERING); - auto end = ORE::end(); - for (const auto& t : OR::get_iterator_range(start, end, st, maxDim)) { - auto r = st.simplex_vertex_range(std::get<0>(t)); - std::vector simplex(r.begin(), r.end()); - if (std::get<2>(t)) - zp.insert_simplex(simplex, std::get<1>(t)); - else - zp.remove_simplex(simplex, std::get<1>(t)); - } - - return zp.get_persistence_diagram(); -} - -static void ORP_with_gudhi(benchmark::State& state) { - double nu = state.range(0); - double mu = state.range(1); - int maxDim = state.range(2); - unsigned int numberOfPoints = state.range(3); - int seed = 0; - - std::vector points = build_point_cloud(numberOfPoints, seed); - - for (auto _ : state) { - auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence(points, nu, mu, maxDim); - } -} -BENCHMARK(ORP_with_gudhi) - ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), - benchmark::CreateDenseRange(3, 5, 1), - benchmark::CreateRange(2, 5, 4), - benchmark::CreateRange(5, 5, 4)}) - ->Unit(benchmark::kMicrosecond); -BENCHMARK(ORP_with_gudhi) - ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), - benchmark::CreateDenseRange(3, 5, 1), - benchmark::CreateRange(4, 50, 4), - benchmark::CreateRange(20, 64, 4)}) - ->Unit(benchmark::kMillisecond); -// BENCHMARK(ORP_with_gudhi) -// ->ArgsProduct({benchmark::CreateDenseRange(1, 2, 1), -// benchmark::CreateDenseRange(3, 4, 1), -// benchmark::CreateRange(4, 50, 4), -// benchmark::CreateRange(256, 500, 4)}) -// ->Unit(benchmark::kMillisecond); - -// static void ORP_with_gudhi_v2(benchmark::State& state) { -// double nu = state.range(0); -// double mu = state.range(1); -// int maxDim = state.range(2); -// unsigned int numberOfPoints = state.range(3); -// int seed = 0; - -// std::vector points = build_point_cloud(numberOfPoints, seed); - -// for (auto _ : state) { -// auto res = compute_with_gudhi_v2(points, nu, mu, maxDim); -// } -// } -// BENCHMARK(ORP_with_gudhi_v2) -// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), -// benchmark::CreateDenseRange(3, 5, 1), -// benchmark::CreateRange(2, 5, 4), -// benchmark::CreateRange(5, 5, 4)}) -// ->Unit(benchmark::kMicrosecond); -// BENCHMARK(ORP_with_gudhi_v2) -// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), -// benchmark::CreateDenseRange(3, 5, 1), -// benchmark::CreateRange(4, 50, 4), -// benchmark::CreateRange(20, 64, 4)}) -// ->Unit(benchmark::kMillisecond); -// // BENCHMARK(ORP_with_gudhi_v2) -// // ->ArgsProduct({benchmark::CreateDenseRange(1, 2, 1), -// // benchmark::CreateDenseRange(3, 4, 1), -// // benchmark::CreateRange(4, 50, 4), -// // benchmark::CreateRange(256, 500, 4)}) -// // ->Unit(benchmark::kMillisecond); - -static void ORP_with_dionysus(benchmark::State& state) { - double nu = state.range(0); - double mu = state.range(1); - int maxDim = state.range(2); - unsigned int numberOfPoints = state.range(3); - int seed = 0; - - std::vector points = build_point_cloud(numberOfPoints, seed); - - for (auto _ : state) { - auto vec = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance()); - auto res = compute_with_dionysus(vec, maxDim); - } -} -BENCHMARK(ORP_with_dionysus) - ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), - benchmark::CreateDenseRange(3, 5, 1), - benchmark::CreateRange(2, 5, 4), - benchmark::CreateRange(5, 5, 4)}) - ->Unit(benchmark::kMicrosecond); -BENCHMARK(ORP_with_dionysus) - ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), - benchmark::CreateDenseRange(3, 5, 1), - benchmark::CreateRange(4, 50, 4), - benchmark::CreateRange(20, 64, 4)}) - ->Unit(benchmark::kMillisecond); -// BENCHMARK(ORP_with_dionysus) -// ->ArgsProduct({benchmark::CreateDenseRange(1, 2, 1), -// benchmark::CreateDenseRange(3, 4, 1), -// benchmark::CreateRange(4, 50, 4), -// benchmark::CreateRange(256, 500, 4)}) -// ->Unit(benchmark::kMillisecond); - -static void ORP_with_fzz(benchmark::State& state) { - double nu = state.range(0); - double mu = state.range(1); - int maxDim = state.range(2); - unsigned int numberOfPoints = state.range(3); - int seed = 0; - - std::vector points = build_point_cloud(numberOfPoints, seed); - - for (auto _ : state) { - auto vec = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance()); - auto res = compute_with_fzz(vec, maxDim); - } -} -BENCHMARK(ORP_with_fzz) - ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), - benchmark::CreateDenseRange(3, 5, 1), - benchmark::CreateRange(2, 5, 4), - benchmark::CreateRange(5, 5, 4)}) - ->Unit(benchmark::kMicrosecond); -BENCHMARK(ORP_with_fzz) - ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), - benchmark::CreateDenseRange(3, 5, 1), - benchmark::CreateRange(4, 50, 4), - benchmark::CreateRange(20, 64, 4)}) - ->Unit(benchmark::kMillisecond); -// BENCHMARK(ORP_with_fzz) -// ->ArgsProduct({benchmark::CreateDenseRange(1, 2, 1), -// benchmark::CreateDenseRange(3, 4, 1), -// benchmark::CreateRange(4, 50, 4), -// benchmark::CreateRange(256, 500, 4)}) -// ->Unit(benchmark::kMillisecond); - -BENCHMARK_MAIN(); - diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.h deleted file mode 100644 index 00c983623a..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.h +++ /dev/null @@ -1,153 +0,0 @@ -#ifndef DIONYSUS_CHAIN_H -#define DIONYSUS_CHAIN_H - -#include -#include -#include - -#include "fields/z2.h" - -namespace dionysus -{ - -template -struct FieldElement -{ - typedef typename Field::Element Element; - FieldElement(Element e_): - e(e_) {} - Element element() const { return e; } - void set_element(Element e_) { e = e_; } - Element e; -}; - -template<> -struct FieldElement -{ - typedef Z2Field::Element Element; - FieldElement(Element) {} - Element element() const { return Z2Field::id(); } - void set_element(Element) {} -}; - -template -struct ChainEntry: public FieldElement, public Extra... -{ - typedef Field_ Field; - typedef Index_ Index; - - typedef FieldElement Parent; - typedef typename Parent::Element Element; - - ChainEntry(): Parent(Element()), i(Index()) {} // need for serialization - - ChainEntry(ChainEntry&& other) = default; - ChainEntry(const ChainEntry& other) = default; - ChainEntry& operator=(ChainEntry&& other) = default; - - ChainEntry(Element e_, const Index& i_): - Parent(e_), i(i_) {} - - ChainEntry(Element e_, Index&& i_): - Parent(e_), i(std::move(i_)) {} - - const Index& index() const { return i; } - Index& index() { return i; } - - // debug - bool operator==(const ChainEntry& other) const { return i == other.i; } - - Index i; -}; - -template -struct Chain -{ - struct Visitor - { - template - void first(Iter it) const {} - - template - void second(Iter it) const {} - - template - void equal_keep(Iter it) const {} - - template - void equal_drop(Iter it) const {} - }; - - // x += a*y - template - static void addto(C1& x, typename Field::Element a, const C2& y, const Field& field, const Cmp& cmp, const Visitor_& = Visitor_()); -}; - -template -struct Chain> -{ - struct Visitor - { - template - void first(Iter it) const {} - - template - void second(Iter it) const {} - - template - void equal_keep(Iter it) const {} - - template - void equal_drop(Iter it) const {} - }; - - // x += a*y - template - static void addto(std::list& x, typename Field::Element a, const C2& y, - const Field& field, const Cmp& cmp, const Visitor_& visitor = Visitor_()); -}; - - -template -struct Chain> -{ - struct Visitor - { - template - void first(Iter it) const {} - - template - void second(Iter it) const {} - - template - void equal_keep(Iter it) const {} - - template - void equal_drop(Iter it) const {} - }; - - // x += a*y - template - static void addto(std::set& x, typename Field::Element a, const C2& y, - const Field& field, const Cmp& cmp, const Visitor_& = Visitor_()); - - template - static void addto(std::set& x, typename Field::Element a, T&& y, - const Field& field, const Cmp& cmp, const Visitor_& = Visitor_()); -}; - -} - -//namespace std -//{ -// template -// void swap(::dionysus::ChainEntry& x, ::dionysus::ChainEntry& y) -// { -// std::swap(x.e, y.e); -// std::swap(x.i, y.i); -// } -//} - -#include "chain.hpp" - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.hpp deleted file mode 100644 index 4da9f44615..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.hpp +++ /dev/null @@ -1,188 +0,0 @@ -template -template -void -dionysus::Chain>:: -addto(std::list& x, typename Field::Element a, const C2& y, const Field& field, const Cmp& cmp, const Visitor_& visitor) -{ - typedef typename Field::Element Element; - - auto cur_x = std::begin(x), - end_x = std::end(x); - auto cur_y = std::begin(y), - end_y = std::end(y); - - while (cur_x != end_x && cur_y != end_y) - { - if (cmp(cur_x->index(), cur_y->index())) - { - visitor.first(cur_x++); - } else if (cmp(cur_y->index(), cur_x->index())) - { - // multiply and add - Element ay = field.mul(a, cur_y->element()); - auto nw_x = x.insert(cur_x, *cur_y); - nw_x->set_element(ay); - ++cur_y; - visitor.second(nw_x); - } else - { - Element ay = field.mul(a, cur_y->element()); - Element r = field.add(cur_x->element(), ay); - if (field.is_zero(r)) - { - visitor.equal_drop(cur_x); - x.erase(cur_x++); - } - else - { - cur_x->set_element(r); - visitor.equal_keep(cur_x); - ++cur_x; - } - ++cur_y; - } - } - - for (auto it = cur_y; it != end_y; ++it) - { - Element ay = field.mul(a, it->element()); - x.push_back(*it); - x.back().set_element(ay); - visitor.second(--x.end()); - } -} - -template -template -void -dionysus::Chain>:: -addto(std::set& x, typename Field::Element a, const C2& y, const Field& field, const Cmp&, const Visitor_& visitor) -{ - typedef typename Field::Element Element; - - auto cur_y = std::begin(y), - end_y = std::end(y); - - while (cur_y != end_y) - { - auto cur_x = x.find(*cur_y); - if (cur_x == x.end()) - { - auto nw = x.insert(*cur_y).first; - Element ay = field.mul(a, nw->element()); - const_cast(*nw).set_element(ay); - visitor.second(nw); - } else - { - Element ay = field.mul(a, cur_y->element()); - Element r = field.add(cur_x->element(), ay); - if (field.is_zero(r)) - { - visitor.equal_drop(cur_x); - x.erase(cur_x); - } - else - { - const_cast(*cur_x).set_element(r); - visitor.equal_keep(cur_x); - } - } - ++cur_y; - } -} - -template -template -void -dionysus::Chain>:: -addto(std::set& x, typename Field::Element a, T&& y, const Field& field, const Cmp&, const Visitor_& visitor) -{ - typedef typename Field::Element Element; - - auto cur_x = x.find(y); - if (cur_x == x.end()) - { - auto nw = x.insert(std::move(y)).first; - Element ay = field.mul(a, nw->element()); - const_cast(*nw).set_element(ay); - visitor.second(nw); - } else - { - Element ay = field.mul(a, y.element()); - Element r = field.add(cur_x->element(), ay); - if (field.is_zero(r)) - { - visitor.equal_drop(cur_x); - x.erase(cur_x); - } - else - { - const_cast(*cur_x).set_element(r); - visitor.equal_keep(cur_x); - } - } -} - -template -template -void -dionysus::Chain:: -addto(C1& x, typename Field::Element a, const C2& y, const Field& field, const Cmp& cmp, const Visitor_& visitor) -{ - typedef typename Field::Element Element; - - C1 res; - - auto cur_x = std::begin(x), - end_x = std::end(x); - auto cur_y = std::begin(y), - end_y = std::end(y); - - while (cur_x != end_x && cur_y != end_y) - { - if (cmp(*cur_x, *cur_y)) - { - res.emplace_back(std::move(*cur_x)); - visitor.first(--res.end()); - ++cur_x; - } else if (cmp(*cur_y, *cur_x)) - { - // multiply and add - Element ay = field.mul(a, cur_y->element()); - res.emplace_back(ay, cur_y->index()); - visitor.second(--res.end()); - ++cur_y; - } else - { - Element ay = field.mul(a, cur_y->element()); - Element r = field.add(cur_x->element(), ay); - if (field.is_zero(r)) - visitor.equal_drop(cur_x); - else - { - res.emplace_back(std::move(*cur_x)); - res.back().set_element(r); - visitor.equal_keep(--res.end()); - } - ++cur_x; - ++cur_y; - } - } - - while (cur_y != end_y) - { - Element ay = field.mul(a, cur_y->element()); - res.emplace_back(ay, cur_y->index()); - visitor.second(--res.end()); - ++cur_y; - } - - while (cur_x != end_x) - { - res.emplace_back(std::move(*cur_x)); - visitor.first(--res.end()); - ++cur_x; - } - - x.swap(res); -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.h deleted file mode 100644 index 8651e9a69a..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef DIONYSUS_CLEARING_REDUCTION_H -#define DIONYSUS_CLEARING_REDUCTION_H - -namespace dionysus -{ - -// Mid-level interface -template -class ClearingReduction -{ - public: - using Persistence = Persistence_; - using Field = typename Persistence::Field; - using Index = typename Persistence::Index; - - public: - ClearingReduction(Persistence& persistence): - persistence_(persistence) {} - - template - void operator()(const Filtration& f, const Relative& relative, const ReportPair& report_pair, const Progress& progress); - - template - void operator()(const Filtration& f, const ReportPair& report_pair); - - template - void operator()(const Filtration& f) { return (*this)(f, &no_report_pair); } - - static void no_report_pair(int, Index, Index) {} - static void no_progress() {} - - const Persistence& - persistence() const { return persistence_; } - Persistence& persistence() { return persistence_; } - - private: - Persistence& persistence_; -}; - -} - -#include "clearing-reduction.hpp" - -#endif - diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.hpp deleted file mode 100644 index ceac11879d..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.hpp +++ /dev/null @@ -1,60 +0,0 @@ -#include -#include - -#include -namespace ba = boost::adaptors; - -template -template -void -dionysus::ClearingReduction

:: -operator()(const Filtration& filtration, const ReportPair& report_pair) -{ - using Cell = typename Filtration::Cell; - (*this)(filtration, [](const Cell&) { return false; }, report_pair, &no_progress); -} - -template -template -void -dionysus::ClearingReduction

:: -operator()(const Filtration& filtration, const Relative& relative, const ReportPair& report_pair, const Progress& progress) -{ - persistence_.resize(filtration.size()); - - // sort indices by decreasing dimension - std::vector indices(filtration.size()); - std::iota(indices.begin(), indices.end(), 0); - std::stable_sort(indices.begin(), indices.end(), - [&filtration](size_t x, size_t y) - { return filtration[x].dimension() > filtration[y].dimension(); }); - - typedef typename Filtration::Cell Cell; - typedef ChainEntry CellChainEntry; - typedef ChainEntry ChainEntry; - - for(size_t i : indices) - { - progress(); - const auto& c = filtration[i]; - - if (relative(c)) - { - persistence_.set_skip(i); - continue; - } - - if (persistence_.pair(i) != persistence_.unpaired()) - continue; - - persistence_.set(i, c.boundary(persistence_.field()) | - ba::filtered([relative](const CellChainEntry& e) { return !relative(e.index()); }) | - ba::transformed([this,&filtration](const CellChainEntry& e) - { return ChainEntry(e.element(), filtration.index(e.index())); })); - - Index pair = persistence_.reduce(i); - if (pair != persistence_.unpaired()) - report_pair(c.dimension(), pair, i); - } -} - diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cnpy.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cnpy.h deleted file mode 100644 index b11013b9d7..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cnpy.h +++ /dev/null @@ -1,241 +0,0 @@ -//Copyright (C) 2011 Carl Rogers -//Released under MIT License -//license available in LICENSE file, or at http://www.opensource.org/licenses/mit-license.php - -#ifndef LIBCNPY_H_ -#define LIBCNPY_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cnpy { - - struct NpyArray { - char* data; - std::vector shape; - unsigned int word_size; - bool fortran_order; - void destruct() {delete[] data;} - }; - - struct npz_t : public std::map - { - void destruct() - { - npz_t::iterator it = this->begin(); - for(; it != this->end(); ++it) (*it).second.destruct(); - } - }; - - char BigEndianTest(); - char map_type(const std::type_info& t); - template std::vector create_npy_header(const T* data, const unsigned int* shape, const unsigned int ndims); - void parse_npy_header(FILE* fp,unsigned int& word_size, unsigned int*& shape, unsigned int& ndims, bool& fortran_order); - void parse_zip_footer(FILE* fp, unsigned short& nrecs, unsigned int& global_header_size, unsigned int& global_header_offset); - npz_t npz_load(std::string fname); - NpyArray npz_load(std::string fname, std::string varname); - NpyArray npy_load(std::string fname); - - template std::vector& operator+=(std::vector& lhs, const T rhs) { - //write in little endian - for(char byte = 0; byte < sizeof(T); byte++) { - char val = *((char*)&rhs+byte); - lhs.push_back(val); - } - return lhs; - } - - template<> std::vector& operator+=(std::vector& lhs, const std::string rhs); - template<> std::vector& operator+=(std::vector& lhs, const char* rhs); - - - template std::string tostring(T i, int pad = 0, char padval = ' ') { - std::stringstream s; - s << i; - return s.str(); - } - - template void npy_save(std::string fname, const T* data, const unsigned int* shape, const unsigned int ndims, std::string mode = "w") { - FILE* fp = NULL; - - if(mode == "a") fp = fopen(fname.c_str(),"r+b"); - - if(fp) { - //file exists. we need to append to it. read the header, modify the array size - unsigned int word_size, tmp_dims; - unsigned int* tmp_shape = 0; - bool fortran_order; - parse_npy_header(fp,word_size,tmp_shape,tmp_dims,fortran_order); - assert(!fortran_order); - - if(word_size != sizeof(T)) { - std::cout<<"libnpy error: "< header = create_npy_header(data,tmp_shape,ndims); - fwrite(&header[0],sizeof(char),header.size(),fp); - fseek(fp,0,SEEK_END); - - delete[] tmp_shape; - } - else { - fp = fopen(fname.c_str(),"wb"); - std::vector header = create_npy_header(data,shape,ndims); - fwrite(&header[0],sizeof(char),header.size(),fp); - } - - unsigned int nels = 1; - for(int i = 0;i < ndims;i++) nels *= shape[i]; - - fwrite(data,sizeof(T),nels,fp); - fclose(fp); - } - - template void npz_save(std::string zipname, std::string fname, const T* data, const unsigned int* shape, const unsigned int ndims, std::string mode = "w") - { - //first, append a .npy to the fname - fname += ".npy"; - - //now, on with the show - FILE* fp = NULL; - unsigned short nrecs = 0; - unsigned int global_header_offset = 0; - std::vector global_header; - - if(mode == "a") fp = fopen(zipname.c_str(),"r+b"); - - if(fp) { - //zip file exists. we need to add a new npy file to it. - //first read the footer. this gives us the offset and size of the global header - //then read and store the global header. - //below, we will write the the new data at the start of the global header then append the global header and footer below it - unsigned int global_header_size; - parse_zip_footer(fp,nrecs,global_header_size,global_header_offset); - fseek(fp,global_header_offset,SEEK_SET); - global_header.resize(global_header_size); - size_t res = fread(&global_header[0],sizeof(char),global_header_size,fp); - if(res != global_header_size){ - throw std::runtime_error("npz_save: header read error while adding to existing zip"); - } - fseek(fp,global_header_offset,SEEK_SET); - } - else { - fp = fopen(zipname.c_str(),"wb"); - } - - std::vector npy_header = create_npy_header(data,shape,ndims); - - unsigned long nels = 1; - for (int m=0; m local_header; - local_header += "PK"; //first part of sig - local_header += (unsigned short) 0x0403; //second part of sig - local_header += (unsigned short) 20; //min version to extract - local_header += (unsigned short) 0; //general purpose bit flag - local_header += (unsigned short) 0; //compression method - local_header += (unsigned short) 0; //file last mod time - local_header += (unsigned short) 0; //file last mod date - local_header += (unsigned int) crc; //crc - local_header += (unsigned int) nbytes; //compressed size - local_header += (unsigned int) nbytes; //uncompressed size - local_header += (unsigned short) fname.size(); //fname length - local_header += (unsigned short) 0; //extra field length - local_header += fname; - - //build global header - global_header += "PK"; //first part of sig - global_header += (unsigned short) 0x0201; //second part of sig - global_header += (unsigned short) 20; //version made by - global_header.insert(global_header.end(),local_header.begin()+4,local_header.begin()+30); - global_header += (unsigned short) 0; //file comment length - global_header += (unsigned short) 0; //disk number where file starts - global_header += (unsigned short) 0; //internal file attributes - global_header += (unsigned int) 0; //external file attributes - global_header += (unsigned int) global_header_offset; //relative offset of local file header, since it begins where the global header used to begin - global_header += fname; - - //build footer - std::vector footer; - footer += "PK"; //first part of sig - footer += (unsigned short) 0x0605; //second part of sig - footer += (unsigned short) 0; //number of this disk - footer += (unsigned short) 0; //disk where footer starts - footer += (unsigned short) (nrecs+1); //number of records on this disk - footer += (unsigned short) (nrecs+1); //total number of records - footer += (unsigned int) global_header.size(); //nbytes of global headers - footer += (unsigned int) (global_header_offset + nbytes + local_header.size()); //offset of start of global headers, since global header now starts after newly written array - footer += (unsigned short) 0; //zip file comment length - - //write everything - fwrite(&local_header[0],sizeof(char),local_header.size(),fp); - fwrite(&npy_header[0],sizeof(char),npy_header.size(),fp); - fwrite(data,sizeof(T),nels,fp); - fwrite(&global_header[0],sizeof(char),global_header.size(),fp); - fwrite(&footer[0],sizeof(char),footer.size(),fp); - fclose(fp); - } - - template std::vector create_npy_header(const T* data, const unsigned int* shape, const unsigned int ndims) { - - std::vector dict; - dict += "{'descr': '"; - dict += BigEndianTest(); - dict += map_type(typeid(T)); - dict += tostring(sizeof(T)); - dict += "', 'fortran_order': False, 'shape': ("; - dict += tostring(shape[0]); - for(int i = 1;i < ndims;i++) { - dict += ", "; - dict += tostring(shape[i]); - } - if(ndims == 1) dict += ","; - dict += "), }"; - //pad with spaces so that preamble+dict is modulo 16 bytes. preamble is 10 bytes. dict needs to end with \n - int remainder = 16 - (10 + dict.size()) % 16; - dict.insert(dict.end(),remainder,' '); - dict.back() = '\n'; - - std::vector header; - header += (char) 0x93; - header += "NUMPY"; - header += (char) 0x01; //major version of numpy format - header += (char) 0x00; //minor version of numpy format - header += (unsigned short) dict.size(); - header.insert(header.end(),dict.begin(),dict.end()); - - return header; - } - - -} - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.h deleted file mode 100644 index 8d2019e89d..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.h +++ /dev/null @@ -1,116 +0,0 @@ -#ifndef DIONYSUS_COHOMOLOGY_PERSISTENCE_H -#define DIONYSUS_COHOMOLOGY_PERSISTENCE_H - -#include -#include - -#include -namespace bi = boost::intrusive; - -#include "reduction.h" -#include "chain.h" - -namespace dionysus -{ - -template> -class CohomologyPersistence -{ - public: - typedef Field_ Field; - typedef Index_ Index; - typedef Comparison_ Comparison; - - typedef typename Field::Element FieldElement; - - typedef bi::list_base_hook> auto_unlink_hook; - struct Entry; - struct ColumnHead; - - typedef std::vector Column; - typedef bi::list> Row; - typedef std::list Columns; - typedef typename Columns::iterator ColumnsIterator; - typedef Column Chain; - - using IndexColumn = std::tuple; - - CohomologyPersistence(const Field& field, - const Comparison& cmp = Comparison()): - field_(field), cmp_(cmp) {} - - CohomologyPersistence(Field&& field, - const Comparison& cmp = Comparison()): - field_(std::move(field)), - cmp_(cmp) {} - - CohomologyPersistence(CohomologyPersistence&& other): - field_(std::move(other.field_)), - cmp_(std::move(other.cmp_)), - columns_(std::move(other.columns_)), - rows_(std::move(other.rows_)) {} - - template - Index add(const ChainRange& chain); - - template - IndexColumn add(const ChainRange& chain, bool keep_cocycle); - - // TODO: no skip support for now - bool skip(Index) const { return false; } - void add_skip() {} - void set_skip(Index, bool flag = true) {} - - const Field& field() const { return field_; } - const Columns& columns() const { return columns_; } - void reserve(size_t s) { rows_.reserve(s); } - - struct AddtoVisitor; - - static const Index unpaired() { return Reduction::unpaired; } - - private: - Field field_; - Comparison cmp_; - Columns columns_; - std::vector rows_; -}; - - -template -struct CohomologyPersistence::ColumnHead -{ - ColumnHead(Index i): index_(i) {} - - Index index() const { return index_; } - - Index index_; - Column chain; -}; - -template -struct CohomologyPersistence::Entry: - public ChainEntry -{ - typedef ChainEntry Parent; - - Entry(FieldElement e, const Index& i): // slightly dangerous - Parent(e,i) {} - - Entry(FieldElement e, const Index& i, ColumnsIterator it): - Parent(e,i), column(it) {} - - Entry(const Entry& other) = default; - Entry(Entry&& other) = default; - - void unlink() { auto_unlink_hook::unlink(); } - bool is_linked() const { return auto_unlink_hook::is_linked(); } - - ColumnsIterator column; // TODO: I really don't like this overhead -}; - -} - -#include "cohomology-persistence.hpp" - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.hpp deleted file mode 100644 index b2334f99e1..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.hpp +++ /dev/null @@ -1,61 +0,0 @@ -template -template -typename dionysus::CohomologyPersistence::Index -dionysus::CohomologyPersistence:: -add(const ChainRange& chain) -{ - return std::get<0>(add(chain, false)); // return just the index -} - - -template -template -typename dionysus::CohomologyPersistence::IndexColumn -dionysus::CohomologyPersistence:: -add(const ChainRange& chain, bool keep_cocycle) -{ - auto entry_cmp = [this](const Entry& e1, const Entry& e2) { return this->cmp_(e1.index(), e2.index()); }; - std::set row_sum(entry_cmp); - for (auto it = std::begin(chain); it != std::end(chain); ++it) - for (auto& re : rows_[it->index()]) - dionysus::Chain::addto(row_sum, it->element(), Entry(re.element(), re.column->index(), re.column), field_, cmp_); - - if (row_sum.empty()) // Birth - { - columns_.emplace_back(rows_.size()); - auto before_end = columns_.end(); - --before_end; - columns_.back().chain.push_back(Entry(field_.id(), rows_.size(), before_end)); - rows_.emplace_back(); - rows_.back().push_back(columns_.back().chain.front()); - return std::make_tuple(unpaired(), Column()); - } else // Death - { - // Select front element in terms of comparison (rows are unsorted) - auto it = std::max_element(std::begin(row_sum), std::end(row_sum), entry_cmp); - - Entry first = std::move(*it); - row_sum.erase(it); - - for (auto& ce : row_sum) - { - FieldElement ay = field_.neg(field_.div(ce.element(), first.element())); - dionysus::Chain::addto(ce.column->chain, ay, first.column->chain, field_, - [this](const Entry& e1, const Entry& e2) - { return this->cmp_(e1.index(), e2.index()); }); - - for (auto& x : ce.column->chain) - { - x.column = ce.column; - rows_[x.index()].push_back(x); - } - } - Index pair = first.column->index(); - Column cocycle; - if (keep_cocycle) - cocycle = std::move(first.column->chain); - columns_.erase(first.column); - rows_.emplace_back(); // useless row; only present to make indices match - return std::make_tuple(pair, cocycle); - } -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/common.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/common.h deleted file mode 100644 index e012b10539..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/common.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef DIONYSUS_EXAMPLES_COMMON_H -#define DIONYSUS_EXAMPLES_COMMON_H - -#include -#include - -template -void read_points(const std::string& infilename, PointContainer& points) -{ - typedef typename PointContainer::value_type Point; - - std::ifstream in(infilename.c_str()); - std::string line; - while(std::getline(in, line)) - { - if (line[0] == '#') continue; // comment line in the file - std::stringstream linestream(line); - double x; - points.push_back(Point()); - while (linestream >> x) - points.back().push_back(x); - } -} - -#endif // DIONYSUS_EXAMPLES_COMMON_H diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/diagram.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/diagram.h deleted file mode 100644 index 04eb29a927..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/diagram.h +++ /dev/null @@ -1,114 +0,0 @@ -#ifndef DIONYSUS_DIAGRAM_H -#define DIONYSUS_DIAGRAM_H - -#include -#include -#include - -namespace dionysus -{ - -template -class Diagram -{ - public: - using Value = Value_; - using Data = Data_; - struct Point: public std::pair - { - using Parent = std::pair; - - Point(Value b, Value d, Data dd): - Parent(b,d), data(dd) {} - - Value birth() const { return Parent::first; } - Value death() const { return Parent::second; } - - // FIXME: temporary hack - Value operator[](size_t i) const { if (i == 0) return birth(); return death(); } - - Data data; - }; - - using Points = std::vector; - using iterator = typename Points::iterator; - using const_iterator = typename Points::const_iterator; - using value_type = Point; - - public: - const_iterator begin() const { return points.begin(); } - const_iterator end() const { return points.end(); } - iterator begin() { return points.begin(); } - iterator end() { return points.end(); } - - const Point& operator[](size_t i) const { return points[i]; } - - size_t size() const { return points.size(); } - void push_back(const Point& p) { points.push_back(p); } - template - void emplace_back(Args&&... args) { points.emplace_back(std::forward(args)...); } - - private: - std::vector points; -}; - -namespace detail -{ - template - struct Diagrams - { - using Value = decltype(std::declval()(std::declval())); - using Data = decltype(std::declval()(std::declval())); - using type = std::vector>; - }; -} - -template -typename detail::Diagrams::type -init_diagrams(const ReducedMatrix& m, const Filtration& f, const GetValue& get_value, const GetData& get_data) -{ - using Result = typename detail::Diagrams::type; - - Result diagrams; - for (typename ReducedMatrix::Index i = 0; i < m.size(); ++i) - { - if (m.skip(i)) - continue; - - auto s = f[i]; - auto d = s.dimension(); - - while (d + 1 > diagrams.size()) - diagrams.emplace_back(); - - auto pair = m.pair(i); - if (pair == m.unpaired()) - { - auto birth = get_value(s); - using Value = decltype(birth); - Value death = std::numeric_limits::infinity(); - diagrams[d].emplace_back(birth, death, get_data(i)); - } else if (pair > i) // positive - { - auto birth = get_value(s); - auto death = get_value(f[pair]); - - // hack to work with coboundaries - auto pd = f[pair].dimension(); - if (pd < d) - { - d = pd; - std::swap(birth, death); - } - - if (birth != death) // skip diagonal - diagrams[d].emplace_back(birth, death, get_data(i)); - } // else negative: do nothing - } - - return diagrams; -} - -} - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.h deleted file mode 100644 index 29cac601af..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.h +++ /dev/null @@ -1,93 +0,0 @@ -#ifndef DIONYSUS_DISTANCES_H -#define DIONYSUS_DISTANCES_H - -#include -#include - -namespace dionysus -{ - -/** - * Class: ExplicitDistances - * Stores the pairwise distances of Distances_ instance passed at construction. - * It's a protypical Distances template argument for the Rips complex. - */ -template -class ExplicitDistances -{ - public: - typedef Distances_ Distances; - typedef size_t IndexType; - typedef typename Distances::DistanceType DistanceType; - - ExplicitDistances(IndexType size): - size_(size), - distances_(size*(size + 1)/2 + size) {} - ExplicitDistances(const Distances& distances); - - DistanceType operator()(IndexType a, IndexType b) const; - DistanceType& operator()(IndexType a, IndexType b); - - size_t size() const { return size_; } - IndexType begin() const { return 0; } - IndexType end() const { return size(); } - - private: - std::vector distances_; - size_t size_; -}; - - -/** - * Class: PairwiseDistances - * Given a Container_ of points and a Distance_, it computes distances between elements - * in the container (given as instances of Index_ defaulted to unsigned) using the Distance_ functor. - * - * Container_ is assumed to be an std::vector. That simplifies a number of things. - */ -template -class PairwiseDistances -{ - public: - typedef Container_ Container; - typedef Distance_ Distance; - typedef Index_ IndexType; - typedef typename Distance::result_type DistanceType; - - - PairwiseDistances(const Container& container, - const Distance& distance = Distance()): - container_(container), distance_(distance) {} - - DistanceType operator()(IndexType a, IndexType b) const { return distance_(container_[a], container_[b]); } - - size_t size() const { return container_.size(); } - IndexType begin() const { return 0; } - IndexType end() const { return size(); } - - private: - const Container& container_; - Distance distance_; -}; - -template -struct L2Distance -{ - typedef Point_ Point; - typedef decltype(Point()[0] + 0) result_type; - - result_type operator()(const Point& p1, const Point& p2) const - { - result_type sum = 0; - for (size_t i = 0; i < p1.size(); ++i) - sum += (p1[i] - p2[i])*(p1[i] - p2[i]); - - return sqrt(sum); - } -}; - -} - -#include "distances.hpp" - -#endif // DIONYSUS_DISTANCES_H diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.hpp deleted file mode 100644 index 9b1f20aa2b..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.hpp +++ /dev/null @@ -1,30 +0,0 @@ -template -dionysus::ExplicitDistances:: -ExplicitDistances(const Distances& distances): - size_(distances.size()), distances_((distances.size() * (distances.size() + 1))/2) -{ - IndexType i = 0; - for (typename Distances::IndexType a = distances.begin(); a != distances.end(); ++a) - for (typename Distances::IndexType b = a; b != distances.end(); ++b) - { - distances_[i++] = distances(a,b); - } -} - -template -typename dionysus::ExplicitDistances::DistanceType -dionysus::ExplicitDistances:: -operator()(IndexType a, IndexType b) const -{ - if (a > b) std::swap(a,b); - return distances_[a*size_ - ((a*(a-1))/2) + (b-a)]; -} - -template -typename dionysus::ExplicitDistances::DistanceType& -dionysus::ExplicitDistances:: -operator()(IndexType a, IndexType b) -{ - if (a > b) std::swap(a,b); - return distances_[a*size_ - ((a*(a-1))/2) + (b-a)]; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/dlog/progress.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/dlog/progress.h deleted file mode 100644 index 12bf86a4a2..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/dlog/progress.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef DLOG_PROGRESS_H -#define DLOG_PROGRESS_H - -#include -#include -#include -#include - -namespace dlog -{ - -struct progress -{ - progress(size_t total): - current_(0), total_(total) { show_progress(); } - - progress& operator++() { current_++; if (current_ * 100 / total_ > (current_ - 1) * 100 / total_) show_progress(); check_done(); return *this; } - progress& operator=(size_t cur) { current_ = cur; show_progress(); check_done(); return *this; } - progress& operator()(const std::string& s) { message_ = s; show_progress(); check_done(); return *this; } - template - progress& operator()(const T& x) { std::ostringstream oss; oss << x; return (*this)(oss.str()); } - - inline void show_progress() const; - void check_done() const { if (current_ >= total_) std::cout << "\n" << std::flush; } - - private: - size_t current_, total_; - std::string message_; -}; - -} - -void -dlog::progress:: -show_progress() const -{ - int barWidth = 70; - - std::cout << "["; - int pos = barWidth * current_ / total_; - for (int i = 0; i < barWidth; ++i) - { - if (i < pos) - std::cout << "="; - else if (i == pos) - std::cout << ">"; - else - std::cout << " "; - } - std::cout << "] " << std::setw(3) << current_ * 100 / total_ << "%"; - if (!message_.empty()) - std::cout << " (" << message_ << ")"; - std::cout << "\r"; - std::cout.flush(); -} - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/q.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/q.h deleted file mode 100644 index 8972ae2b5a..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/q.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef DIONYSUS_Q_H -#define DIONYSUS_Q_H - -#include - -// TODO: eventually need to be able to adaptively switch to arbitrary precision arithmetic - -namespace dionysus -{ - -template -class Q -{ - public: - using BaseElement = Element_; - struct Element - { - BaseElement numerator, denominator; - - bool operator==(Element o) const { return numerator == o.numerator && denominator == o.denominator; } - bool operator!=(Element o) const { return !((*this) == o); } - - friend - std::ostream& operator<<(std::ostream& out, Element e) { out << e.numerator << '/' << e.denominator; return out; } - }; - - Element id() const { return { 1,1 }; } - Element zero() const { return { 0,1 }; } - Element init(BaseElement a) const { return { a,1 }; } - - Element neg(Element a) const { return { -a.numerator, a.denominator }; } - Element add(Element a, Element b) const { Element x { a.numerator*b.denominator + b.numerator*a.denominator, a.denominator*b.denominator }; normalize(x); return x; } - - Element inv(Element a) const { return { a.denominator, a.numerator }; } - Element mul(Element a, Element b) const { Element x { a.numerator*b.numerator, a.denominator*b.denominator }; normalize(x); return x; } - Element div(Element a, Element b) const { return mul(a, inv(b)); } - - bool is_zero(Element a) const { return a.numerator == 0; } - - BaseElement numerator(const Element& x) const { return x.numerator; } - BaseElement denominator(const Element& x) const { return x.denominator; } - - static void normalize(Element& x) - { - BaseElement q = gcd(abs(x.numerator), abs(x.denominator)); - x.numerator /= q; - x.denominator /= q; - if (x.denominator < 0) - { - x.numerator = -x.numerator; - x.denominator = -x.denominator; - } - } - - static BaseElement abs(BaseElement x) { if (x < 0) return -x; return x; } - static BaseElement gcd(BaseElement a, BaseElement b) { if (b < a) return gcd(b,a); while (a != 0) { b %= a; std::swap(a,b); } return b; } - - static bool is_prime(BaseElement x) { return false; } // Ok, since is_prime is only used as a shortcut -}; - -} - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/z2.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/z2.h deleted file mode 100644 index 6317ace6df..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/z2.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef DIONYSUS_Z2_H -#define DIONYSUS_Z2_H - -namespace dionysus -{ - -class Z2Field -{ - public: - typedef short Element; - - Z2Field() {} - - static Element id() { return 1; } - static Element zero() { return 0; } - static Element init(int a) { return (a % 2 + 2) % 2; } - - Element neg(Element a) const { return 2 - a; } - Element add(Element a, Element b) const { return (a+b) % 2; } - - Element inv(Element a) const { return a; } - Element mul(Element a, Element b) const { return a*b; } - Element div(Element a, Element b) const { return a; } - - bool is_zero(Element a) const { return a == 0; } -}; - -} - -#endif - diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/zp.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/zp.h deleted file mode 100644 index c70c61cc87..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/zp.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef DIONYSUS_ZP_H -#define DIONYSUS_ZP_H - -#include - -namespace dionysus -{ - -template -class ZpField -{ - public: - typedef Element_ Element; - - ZpField(Element p); - ZpField(const ZpField& other) = default; - ZpField(ZpField&& other) = default; - - Element id() const { return 1; } - Element zero() const { return 0; } - Element init(int a) const { return (a % p_ + p_) % p_; } - - Element neg(Element a) const { return p_ - a; } - Element add(Element a, Element b) const { return (a+b) % p_; } - - Element inv(Element a) const { while (a < 0) a += p_; return inverses_[a]; } - Element mul(Element a, Element b) const { return (a*b) % p_; } - Element div(Element a, Element b) const { return mul(a, inv(b)); } - - bool is_zero(Element a) const { return (a % p_) == 0; } - - Element prime() const { return p_; } - - private: - Element p_; - std::vector inverses_; -}; - -template -ZpField:: -ZpField(Element p): - p_(p), inverses_(p_) -{ - for (Element i = 1; i < p_; ++i) - for (Element j = 1; j < p_; ++j) - if (mul(i,j) == 1) - { - inverses_[i] = j; - break; - } -} - -} - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/filtration.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/filtration.h deleted file mode 100644 index caf871cdfb..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/filtration.h +++ /dev/null @@ -1,124 +0,0 @@ -#ifndef DIONYSUS_FILTRATION_H -#define DIONYSUS_FILTRATION_H - -#include -#include - -#include -#include -#include -#include - -namespace b = boost; -namespace bmi = boost::multi_index; - -namespace dionysus -{ - -// Filtration stores a filtered cell complex as boost::multi_index_container<...>. -// It allows for bidirectional translation between a cell and its index. -template>, - bool checked_index = false> -class Filtration -{ - public: - struct order {}; - - typedef Cell_ Cell; - typedef CellLookupIndex_ CellLookupIndex; - - typedef b::multi_index_container> - >> Container; - typedef typename Container::value_type value_type; - - typedef typename Container::template nth_index<0>::type Complex; - typedef typename Container::template nth_index<1>::type Order; - typedef typename Order::const_iterator OrderConstIterator; - typedef typename Order::iterator OrderIterator; - - - public: - Filtration() = default; - Filtration(Filtration&& other) = default; - Filtration& operator=(Filtration&& other) = default; - - Filtration(const std::initializer_list& cells): - Filtration(std::begin(cells), std::end(cells)) {} - - template - Filtration(Iterator bg, Iterator end): - cells_(bg, end) {} - - template - Filtration(const CellRange& cells): - Filtration(std::begin(cells), std::end(cells)) {} - - // Lookup - const Cell& operator[](size_t i) const { return cells_.template get()[i]; } - OrderConstIterator iterator(const Cell& s) const { return bmi::project(cells_, cells_.find(s)); } - size_t index(const Cell& s) const; - bool contains(const Cell& s) const { return cells_.find(s) != cells_.end(); } - - void push_back(const Cell& s) { cells_.template get().push_back(s); } - void push_back(Cell&& s) { cells_.template get().push_back(s); } - - void replace(size_t i, const Cell& s) { cells_.template get().replace(begin() + i, s); } - - // return index of the cell, adding it, if necessary - size_t add(const Cell& s) { size_t i = (iterator(s) - begin()); if (i == size()) emplace_back(s); return i; } - size_t add(Cell&& s) { size_t i = (iterator(s) - begin()); if (i == size()) emplace_back(std::move(s)); return i; } - - template - void emplace_back(Args&&... args) { cells_.template get().emplace_back(std::forward(args)...); } - - template> - void sort(const Cmp& cmp = Cmp()) { cells_.template get().sort(cmp); } - - void rearrange(const std::vector& indices); - - OrderConstIterator begin() const { return cells_.template get().begin(); } - OrderConstIterator end() const { return cells_.template get().end(); } - OrderIterator begin() { return cells_.template get().begin(); } - OrderIterator end() { return cells_.template get().end(); } - size_t size() const { return cells_.size(); } - void clear() { return Container().swap(cells_); } - - Cell& back() { return const_cast(cells_.template get().back()); } - const Cell& back() const { return cells_.template get().back(); } - - private: - Container cells_; -}; - -} - -template -size_t -dionysus::Filtration:: -index(const Cell& s) const -{ - auto it = iterator(s); - if (checked_index && it == end()) - { - std::ostringstream oss; - oss << "Trying to access non-existent cell: " << s; - throw std::runtime_error(oss.str()); - } - return it - begin(); -} - -template -void -dionysus::Filtration:: -rearrange(const std::vector& indices) -{ - std::vector> references; references.reserve(indices.size()); - for (size_t i : indices) - references.push_back(std::cref((*this)[i])); - cells_.template get().rearrange(references.begin()); -} - - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format.h deleted file mode 100644 index 7f7ba83318..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef DIONYSUS_FORMAT_H -#define DIONYSUS_FORMAT_H - -#define FMT_HEADER_ONLY - -#include "format/format.h" - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.cc b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.cc deleted file mode 100644 index a01e272fd7..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.cc +++ /dev/null @@ -1,1156 +0,0 @@ -/* - Formatting library for C++ - - Copyright (c) 2012 - 2014, Victor Zverovich - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "format.h" - -#include - -#include -#include -#include -#include -#include - -#ifdef _WIN32 -# ifdef __MINGW32__ -# include -# endif -# include -#endif - -using fmt::internal::Arg; - -// Check if exceptions are disabled. -#if __GNUC__ && !__EXCEPTIONS -# define FMT_EXCEPTIONS 0 -#endif -#if _MSC_VER && !_HAS_EXCEPTIONS -# define FMT_EXCEPTIONS 0 -#endif -#ifndef FMT_EXCEPTIONS -# define FMT_EXCEPTIONS 1 -#endif - -#if FMT_EXCEPTIONS -# define FMT_TRY try -# define FMT_CATCH(x) catch (x) -#else -# define FMT_TRY if (true) -# define FMT_CATCH(x) if (false) -#endif - -#ifndef FMT_THROW -# if FMT_EXCEPTIONS -# define FMT_THROW(x) throw x -# define FMT_RETURN_AFTER_THROW(x) -# else -# define FMT_THROW(x) assert(false) -# define FMT_RETURN_AFTER_THROW(x) return x -# endif -#endif - -#ifdef FMT_HEADER_ONLY -# define FMT_FUNC inline -#else -# define FMT_FUNC -#endif - -#if _MSC_VER -# pragma warning(push) -# pragma warning(disable: 4127) // conditional expression is constant -# pragma warning(disable: 4702) // unreachable code -#endif - -namespace { - -#ifndef _MSC_VER -# define FMT_SNPRINTF snprintf -#else // _MSC_VER -inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) { - va_list args; - va_start(args, format); - int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); - va_end(args); - return result; -} -# define FMT_SNPRINTF fmt_snprintf -#endif // _MSC_VER - -// Checks if a value fits in int - used to avoid warnings about comparing -// signed and unsigned integers. -template -struct IntChecker { - template - static bool fits_in_int(T value) { - unsigned max = INT_MAX; - return value <= max; - } -}; - -template <> -struct IntChecker { - template - static bool fits_in_int(T value) { - return value >= INT_MIN && value <= INT_MAX; - } -}; - -const char RESET_COLOR[] = "\x1b[0m"; - -typedef void (*FormatFunc)(fmt::Writer &, int, fmt::StringRef); - -// Portable thread-safe version of strerror. -// Sets buffer to point to a string describing the error code. -// This can be either a pointer to a string stored in buffer, -// or a pointer to some static immutable string. -// Returns one of the following values: -// 0 - success -// ERANGE - buffer is not large enough to store the error message -// other - failure -// Buffer should be at least of size 1. -int safe_strerror( - int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT { - assert(buffer != 0 && buffer_size != 0); - int result = 0; -#if ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !_GNU_SOURCE) || __ANDROID__ - // XSI-compliant version of strerror_r. - result = strerror_r(error_code, buffer, buffer_size); - if (result != 0) - result = errno; -#elif _GNU_SOURCE - // GNU-specific version of strerror_r. - char *message = strerror_r(error_code, buffer, buffer_size); - // If the buffer is full then the message is probably truncated. - if (message == buffer && strlen(buffer) == buffer_size - 1) - result = ERANGE; - buffer = message; -#elif __MINGW32__ - errno = 0; - (void)buffer_size; - buffer = strerror(error_code); - result = errno; -#elif _WIN32 - result = strerror_s(buffer, buffer_size, error_code); - // If the buffer is full then the message is probably truncated. - if (result == 0 && std::strlen(buffer) == buffer_size - 1) - result = ERANGE; -#else - result = strerror_r(error_code, buffer, buffer_size); - if (result == -1) - result = errno; // glibc versions before 2.13 return result in errno. -#endif - return result; -} - -void format_error_code(fmt::Writer &out, int error_code, - fmt::StringRef message) FMT_NOEXCEPT { - // Report error code making sure that the output fits into - // INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential - // bad_alloc. - out.clear(); - static const char SEP[] = ": "; - static const char ERR[] = "error "; - fmt::internal::IntTraits::MainType ec_value = error_code; - // Subtract 2 to account for terminating null characters in SEP and ERR. - std::size_t error_code_size = - sizeof(SEP) + sizeof(ERR) + fmt::internal::count_digits(ec_value) - 2; - if (message.size() <= fmt::internal::INLINE_BUFFER_SIZE - error_code_size) - out << message << SEP; - out << ERR << error_code; - assert(out.size() <= fmt::internal::INLINE_BUFFER_SIZE); -} - -void report_error(FormatFunc func, - int error_code, fmt::StringRef message) FMT_NOEXCEPT { - fmt::MemoryWriter full_message; - func(full_message, error_code, message); - // Use Writer::data instead of Writer::c_str to avoid potential memory - // allocation. - std::fwrite(full_message.data(), full_message.size(), 1, stderr); - std::fputc('\n', stderr); -} - -// IsZeroInt::visit(arg) returns true iff arg is a zero integer. -class IsZeroInt : public fmt::internal::ArgVisitor { - public: - template - bool visit_any_int(T value) { return value == 0; } -}; - -// Parses an unsigned integer advancing s to the end of the parsed input. -// This function assumes that the first character of s is a digit. -template -int parse_nonnegative_int(const Char *&s) { - assert('0' <= *s && *s <= '9'); - unsigned value = 0; - do { - unsigned new_value = value * 10 + (*s++ - '0'); - // Check if value wrapped around. - if (new_value < value) { - value = UINT_MAX; - break; - } - value = new_value; - } while ('0' <= *s && *s <= '9'); - if (value > INT_MAX) - FMT_THROW(fmt::FormatError("number is too big")); - return value; -} - -inline void require_numeric_argument(const Arg &arg, char spec) { - if (arg.type > Arg::LAST_NUMERIC_TYPE) { - std::string message = - fmt::format("format specifier '{}' requires numeric argument", spec); - FMT_THROW(fmt::FormatError(message)); - } -} - -template -void check_sign(const Char *&s, const Arg &arg) { - char sign = static_cast(*s); - require_numeric_argument(arg, sign); - if (arg.type == Arg::UINT || arg.type == Arg::ULONG_LONG) { - FMT_THROW(fmt::FormatError(fmt::format( - "format specifier '{}' requires signed argument", sign))); - } - ++s; -} - -// Checks if an argument is a valid printf width specifier and sets -// left alignment if it is negative. -class WidthHandler : public fmt::internal::ArgVisitor { - private: - fmt::FormatSpec &spec_; - - FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler); - - public: - explicit WidthHandler(fmt::FormatSpec &spec) : spec_(spec) {} - - unsigned visit_unhandled_arg() { - FMT_THROW(fmt::FormatError("width is not integer")); - FMT_RETURN_AFTER_THROW(0); - } - - template - unsigned visit_any_int(T value) { - typedef typename fmt::internal::IntTraits::MainType UnsignedType; - UnsignedType width = value; - if (fmt::internal::is_negative(value)) { - spec_.align_ = fmt::ALIGN_LEFT; - width = 0 - width; - } - if (width > INT_MAX) - FMT_THROW(fmt::FormatError("number is too big")); - return static_cast(width); - } -}; - -class PrecisionHandler : - public fmt::internal::ArgVisitor { - public: - unsigned visit_unhandled_arg() { - FMT_THROW(fmt::FormatError("precision is not integer")); - FMT_RETURN_AFTER_THROW(0); - } - - template - int visit_any_int(T value) { - if (!IntChecker::is_signed>::fits_in_int(value)) - FMT_THROW(fmt::FormatError("number is too big")); - return static_cast(value); - } -}; - -// Converts an integer argument to an integral type T for printf. -template -class ArgConverter : public fmt::internal::ArgVisitor, void> { - private: - fmt::internal::Arg &arg_; - wchar_t type_; - - FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter); - - public: - ArgConverter(fmt::internal::Arg &arg, wchar_t type) - : arg_(arg), type_(type) {} - - template - void visit_any_int(U value) { - bool is_signed = type_ == 'd' || type_ == 'i'; - using fmt::internal::Arg; - if (sizeof(T) <= sizeof(int)) { - // Extra casts are used to silence warnings. - if (is_signed) { - arg_.type = Arg::INT; - arg_.int_value = static_cast(static_cast(value)); - } else { - arg_.type = Arg::UINT; - arg_.uint_value = static_cast( - static_cast::Type>(value)); - } - } else { - if (is_signed) { - arg_.type = Arg::LONG_LONG; - arg_.long_long_value = - static_cast::Type>(value); - } else { - arg_.type = Arg::ULONG_LONG; - arg_.ulong_long_value = - static_cast::Type>(value); - } - } - } -}; - -// Converts an integer argument to char for printf. -class CharConverter : public fmt::internal::ArgVisitor { - private: - fmt::internal::Arg &arg_; - - FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter); - - public: - explicit CharConverter(fmt::internal::Arg &arg) : arg_(arg) {} - - template - void visit_any_int(T value) { - arg_.type = Arg::CHAR; - arg_.int_value = static_cast(value); - } -}; - -// This function template is used to prevent compile errors when handling -// incompatible string arguments, e.g. handling a wide string in a narrow -// string formatter. -template -Arg::StringValue ignore_incompatible_str(Arg::StringValue); - -template <> -inline Arg::StringValue ignore_incompatible_str( - Arg::StringValue) { return Arg::StringValue(); } - -template <> -inline Arg::StringValue ignore_incompatible_str( - Arg::StringValue s) { return s; } -} // namespace - -FMT_FUNC void fmt::SystemError::init( - int err_code, StringRef format_str, ArgList args) { - error_code_ = err_code; - MemoryWriter w; - internal::format_system_error(w, err_code, format(format_str, args)); - std::runtime_error &base = *this; - base = std::runtime_error(w.str()); -} - -template -int fmt::internal::CharTraits::format_float( - char *buffer, std::size_t size, const char *format, - unsigned width, int precision, T value) { - if (width == 0) { - return precision < 0 ? - FMT_SNPRINTF(buffer, size, format, value) : - FMT_SNPRINTF(buffer, size, format, precision, value); - } - return precision < 0 ? - FMT_SNPRINTF(buffer, size, format, width, value) : - FMT_SNPRINTF(buffer, size, format, width, precision, value); -} - -template -int fmt::internal::CharTraits::format_float( - wchar_t *buffer, std::size_t size, const wchar_t *format, - unsigned width, int precision, T value) { - if (width == 0) { - return precision < 0 ? - swprintf(buffer, size, format, value) : - swprintf(buffer, size, format, precision, value); - } - return precision < 0 ? - swprintf(buffer, size, format, width, value) : - swprintf(buffer, size, format, width, precision, value); -} - -template -const char fmt::internal::BasicData::DIGITS[] = - "0001020304050607080910111213141516171819" - "2021222324252627282930313233343536373839" - "4041424344454647484950515253545556575859" - "6061626364656667686970717273747576777879" - "8081828384858687888990919293949596979899"; - -#define FMT_POWERS_OF_10(factor) \ - factor * 10, \ - factor * 100, \ - factor * 1000, \ - factor * 10000, \ - factor * 100000, \ - factor * 1000000, \ - factor * 10000000, \ - factor * 100000000, \ - factor * 1000000000 - -template -const uint32_t fmt::internal::BasicData::POWERS_OF_10_32[] = { - 0, FMT_POWERS_OF_10(1) -}; - -template -const uint64_t fmt::internal::BasicData::POWERS_OF_10_64[] = { - 0, - FMT_POWERS_OF_10(1), - FMT_POWERS_OF_10(fmt::ULongLong(1000000000)), - // Multiply several constants instead of using a single long long constant - // to avoid warnings about C++98 not supporting long long. - fmt::ULongLong(1000000000) * fmt::ULongLong(1000000000) * 10 -}; - -FMT_FUNC void fmt::internal::report_unknown_type(char code, const char *type) { - if (std::isprint(static_cast(code))) { - FMT_THROW(fmt::FormatError( - fmt::format("unknown format code '{}' for {}", code, type))); - } - FMT_THROW(fmt::FormatError( - fmt::format("unknown format code '\\x{:02x}' for {}", - static_cast(code), type))); -} - -#ifdef _WIN32 - -FMT_FUNC fmt::internal::UTF8ToUTF16::UTF8ToUTF16(fmt::StringRef s) { - int length = MultiByteToWideChar( - CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, 0, 0); - static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16"; - if (length == 0) - FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); - buffer_.resize(length); - length = MultiByteToWideChar( - CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, &buffer_[0], length); - if (length == 0) - FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); -} - -FMT_FUNC fmt::internal::UTF16ToUTF8::UTF16ToUTF8(fmt::WStringRef s) { - if (int error_code = convert(s)) { - FMT_THROW(WindowsError(error_code, - "cannot convert string from UTF-16 to UTF-8")); - } -} - -FMT_FUNC int fmt::internal::UTF16ToUTF8::convert(fmt::WStringRef s) { - int length = WideCharToMultiByte(CP_UTF8, 0, s.c_str(), -1, 0, 0, 0, 0); - if (length == 0) - return GetLastError(); - buffer_.resize(length); - length = WideCharToMultiByte( - CP_UTF8, 0, s.c_str(), -1, &buffer_[0], length, 0, 0); - if (length == 0) - return GetLastError(); - return 0; -} - -FMT_FUNC void fmt::WindowsError::init( - int err_code, StringRef format_str, ArgList args) { - error_code_ = err_code; - MemoryWriter w; - internal::format_windows_error(w, err_code, format(format_str, args)); - std::runtime_error &base = *this; - base = std::runtime_error(w.str()); -} - -#endif - -FMT_FUNC void fmt::internal::format_system_error( - fmt::Writer &out, int error_code, - fmt::StringRef message) FMT_NOEXCEPT { - FMT_TRY { - MemoryBuffer buffer; - buffer.resize(INLINE_BUFFER_SIZE); - for (;;) { - char *system_message = &buffer[0]; - int result = safe_strerror(error_code, system_message, buffer.size()); - if (result == 0) { - out << message << ": " << system_message; - return; - } - if (result != ERANGE) - break; // Can't get error message, report error code instead. - buffer.resize(buffer.size() * 2); - } - } FMT_CATCH(...) {} - format_error_code(out, error_code, message); -} - -#ifdef _WIN32 -FMT_FUNC void fmt::internal::format_windows_error( - fmt::Writer &out, int error_code, - fmt::StringRef message) FMT_NOEXCEPT { - class String { - private: - LPWSTR str_; - - public: - String() : str_() {} - ~String() { LocalFree(str_); } - LPWSTR *ptr() { return &str_; } - LPCWSTR c_str() const { return str_; } - }; - FMT_TRY { - String system_message; - if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, - error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - reinterpret_cast(system_message.ptr()), 0, 0)) { - UTF16ToUTF8 utf8_message; - if (utf8_message.convert(system_message.c_str()) == ERROR_SUCCESS) { - out << message << ": " << utf8_message; - return; - } - } - } FMT_CATCH(...) {} - format_error_code(out, error_code, message); -} -#endif - -// An argument formatter. -template -class fmt::internal::ArgFormatter : - public fmt::internal::ArgVisitor, void> { - private: - fmt::BasicFormatter &formatter_; - fmt::BasicWriter &writer_; - fmt::FormatSpec &spec_; - const Char *format_; - - FMT_DISALLOW_COPY_AND_ASSIGN(ArgFormatter); - - public: - ArgFormatter( - fmt::BasicFormatter &f,fmt::FormatSpec &s, const Char *fmt) - : formatter_(f), writer_(f.writer()), spec_(s), format_(fmt) {} - - template - void visit_any_int(T value) { writer_.write_int(value, spec_); } - - template - void visit_any_double(T value) { writer_.write_double(value, spec_); } - - void visit_char(int value) { - if (spec_.type_ && spec_.type_ != 'c') { - spec_.flags_ |= CHAR_FLAG; - writer_.write_int(value, spec_); - return; - } - if (spec_.align_ == ALIGN_NUMERIC || spec_.flags_ != 0) - FMT_THROW(FormatError("invalid format specifier for char")); - typedef typename fmt::BasicWriter::CharPtr CharPtr; - Char fill = static_cast(spec_.fill()); - if (spec_.precision_ == 0) { - std::fill_n(writer_.grow_buffer(spec_.width_), spec_.width_, fill); - return; - } - CharPtr out = CharPtr(); - if (spec_.width_ > 1) { - out = writer_.grow_buffer(spec_.width_); - if (spec_.align_ == fmt::ALIGN_RIGHT) { - std::fill_n(out, spec_.width_ - 1, fill); - out += spec_.width_ - 1; - } else if (spec_.align_ == fmt::ALIGN_CENTER) { - out = writer_.fill_padding(out, spec_.width_, 1, fill); - } else { - std::fill_n(out + 1, spec_.width_ - 1, fill); - } - } else { - out = writer_.grow_buffer(1); - } - *out = static_cast(value); - } - - void visit_string(Arg::StringValue value) { - writer_.write_str(value, spec_); - } - void visit_wstring(Arg::StringValue value) { - writer_.write_str(ignore_incompatible_str(value), spec_); - } - - void visit_pointer(const void *value) { - if (spec_.type_ && spec_.type_ != 'p') - fmt::internal::report_unknown_type(spec_.type_, "pointer"); - spec_.flags_ = fmt::HASH_FLAG; - spec_.type_ = 'x'; - writer_.write_int(reinterpret_cast(value), spec_); - } - - void visit_custom(Arg::CustomValue c) { - c.format(&formatter_, c.value, &format_); - } -}; - -template -template -void fmt::BasicWriter::write_str( - const Arg::StringValue &s, const FormatSpec &spec) { - // Check if StrChar is convertible to Char. - internal::CharTraits::convert(StrChar()); - if (spec.type_ && spec.type_ != 's') - internal::report_unknown_type(spec.type_, "string"); - const StrChar *str_value = s.value; - std::size_t str_size = s.size; - if (str_size == 0) { - if (!str_value) - FMT_THROW(FormatError("string pointer is null")); - if (*str_value) - str_size = std::char_traits::length(str_value); - } - std::size_t precision = spec.precision_; - if (spec.precision_ >= 0 && precision < str_size) - str_size = spec.precision_; - write_str(str_value, str_size, spec); -} - -template -inline Arg fmt::BasicFormatter::parse_arg_index(const Char *&s) { - const char *error = 0; - Arg arg = *s < '0' || *s > '9' ? - next_arg(error) : get_arg(parse_nonnegative_int(s), error); - if (error) { - FMT_THROW(FormatError( - *s != '}' && *s != ':' ? "invalid format string" : error)); - } - return arg; -} - -FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg( - unsigned arg_index, const char *&error) { - Arg arg = args_[arg_index]; - if (arg.type == Arg::NONE) - error = "argument index out of range"; - return arg; -} - -inline Arg fmt::internal::FormatterBase::next_arg(const char *&error) { - if (next_arg_index_ >= 0) - return do_get_arg(next_arg_index_++, error); - error = "cannot switch from manual to automatic argument indexing"; - return Arg(); -} - -inline Arg fmt::internal::FormatterBase::get_arg( - unsigned arg_index, const char *&error) { - if (next_arg_index_ <= 0) { - next_arg_index_ = -1; - return do_get_arg(arg_index, error); - } - error = "cannot switch from automatic to manual argument indexing"; - return Arg(); -} - -template -void fmt::internal::PrintfFormatter::parse_flags( - FormatSpec &spec, const Char *&s) { - for (;;) { - switch (*s++) { - case '-': - spec.align_ = ALIGN_LEFT; - break; - case '+': - spec.flags_ |= SIGN_FLAG | PLUS_FLAG; - break; - case '0': - spec.fill_ = '0'; - break; - case ' ': - spec.flags_ |= SIGN_FLAG; - break; - case '#': - spec.flags_ |= HASH_FLAG; - break; - default: - --s; - return; - } - } -} - -template -Arg fmt::internal::PrintfFormatter::get_arg( - const Char *s, unsigned arg_index) { - const char *error = 0; - Arg arg = arg_index == UINT_MAX ? - next_arg(error) : FormatterBase::get_arg(arg_index - 1, error); - if (error) - FMT_THROW(FormatError(!*s ? "invalid format string" : error)); - return arg; -} - -template -unsigned fmt::internal::PrintfFormatter::parse_header( - const Char *&s, FormatSpec &spec) { - unsigned arg_index = UINT_MAX; - Char c = *s; - if (c >= '0' && c <= '9') { - // Parse an argument index (if followed by '$') or a width possibly - // preceded with '0' flag(s). - unsigned value = parse_nonnegative_int(s); - if (*s == '$') { // value is an argument index - ++s; - arg_index = value; - } else { - if (c == '0') - spec.fill_ = '0'; - if (value != 0) { - // Nonzero value means that we parsed width and don't need to - // parse it or flags again, so return now. - spec.width_ = value; - return arg_index; - } - } - } - parse_flags(spec, s); - // Parse width. - if (*s >= '0' && *s <= '9') { - spec.width_ = parse_nonnegative_int(s); - } else if (*s == '*') { - ++s; - spec.width_ = WidthHandler(spec).visit(get_arg(s)); - } - return arg_index; -} - -template -void fmt::internal::PrintfFormatter::format( - BasicWriter &writer, BasicStringRef format_str, - const ArgList &args) { - const Char *start = format_str.c_str(); - set_args(args); - const Char *s = start; - while (*s) { - Char c = *s++; - if (c != '%') continue; - if (*s == c) { - write(writer, start, s); - start = ++s; - continue; - } - write(writer, start, s - 1); - - FormatSpec spec; - spec.align_ = ALIGN_RIGHT; - - // Parse argument index, flags and width. - unsigned arg_index = parse_header(s, spec); - - // Parse precision. - if (*s == '.') { - ++s; - if ('0' <= *s && *s <= '9') { - spec.precision_ = parse_nonnegative_int(s); - } else if (*s == '*') { - ++s; - spec.precision_ = PrecisionHandler().visit(get_arg(s)); - } - } - - Arg arg = get_arg(s, arg_index); - if (spec.flag(HASH_FLAG) && IsZeroInt().visit(arg)) - spec.flags_ &= ~HASH_FLAG; - if (spec.fill_ == '0') { - if (arg.type <= Arg::LAST_NUMERIC_TYPE) - spec.align_ = ALIGN_NUMERIC; - else - spec.fill_ = ' '; // Ignore '0' flag for non-numeric types. - } - - // Parse length and convert the argument to the required type. - switch (*s++) { - case 'h': - if (*s == 'h') - ArgConverter(arg, *++s).visit(arg); - else - ArgConverter(arg, *s).visit(arg); - break; - case 'l': - if (*s == 'l') - ArgConverter(arg, *++s).visit(arg); - else - ArgConverter(arg, *s).visit(arg); - break; - case 'j': - ArgConverter(arg, *s).visit(arg); - break; - case 'z': - ArgConverter(arg, *s).visit(arg); - break; - case 't': - ArgConverter(arg, *s).visit(arg); - break; - case 'L': - // printf produces garbage when 'L' is omitted for long double, no - // need to do the same. - break; - default: - --s; - ArgConverter(arg, *s).visit(arg); - } - - // Parse type. - if (!*s) - FMT_THROW(FormatError("invalid format string")); - spec.type_ = static_cast(*s++); - if (arg.type <= Arg::LAST_INTEGER_TYPE) { - // Normalize type. - switch (spec.type_) { - case 'i': case 'u': - spec.type_ = 'd'; - break; - case 'c': - // TODO: handle wchar_t - CharConverter(arg).visit(arg); - break; - } - } - - start = s; - - // Format argument. - switch (arg.type) { - case Arg::INT: - writer.write_int(arg.int_value, spec); - break; - case Arg::UINT: - writer.write_int(arg.uint_value, spec); - break; - case Arg::LONG_LONG: - writer.write_int(arg.long_long_value, spec); - break; - case Arg::ULONG_LONG: - writer.write_int(arg.ulong_long_value, spec); - break; - case Arg::CHAR: { - if (spec.type_ && spec.type_ != 'c') - writer.write_int(arg.int_value, spec); - typedef typename BasicWriter::CharPtr CharPtr; - CharPtr out = CharPtr(); - if (spec.width_ > 1) { - Char fill = ' '; - out = writer.grow_buffer(spec.width_); - if (spec.align_ != ALIGN_LEFT) { - std::fill_n(out, spec.width_ - 1, fill); - out += spec.width_ - 1; - } else { - std::fill_n(out + 1, spec.width_ - 1, fill); - } - } else { - out = writer.grow_buffer(1); - } - *out = static_cast(arg.int_value); - break; - } - case Arg::DOUBLE: - writer.write_double(arg.double_value, spec); - break; - case Arg::LONG_DOUBLE: - writer.write_double(arg.long_double_value, spec); - break; - case Arg::CSTRING: - arg.string.size = 0; - writer.write_str(arg.string, spec); - break; - case Arg::STRING: - writer.write_str(arg.string, spec); - break; - case Arg::WSTRING: - writer.write_str(ignore_incompatible_str(arg.wstring), spec); - break; - case Arg::POINTER: - if (spec.type_ && spec.type_ != 'p') - internal::report_unknown_type(spec.type_, "pointer"); - spec.flags_= HASH_FLAG; - spec.type_ = 'x'; - writer.write_int(reinterpret_cast(arg.pointer), spec); - break; - case Arg::CUSTOM: { - if (spec.type_) - internal::report_unknown_type(spec.type_, "object"); - const void *str_format = "s"; - arg.custom.format(&writer, arg.custom.value, &str_format); - break; - } - default: - assert(false); - break; - } - } - write(writer, start, s); -} - -template -const Char *fmt::BasicFormatter::format( - const Char *&format_str, const Arg &arg) { - const Char *s = format_str; - FormatSpec spec; - if (*s == ':') { - if (arg.type == Arg::CUSTOM) { - arg.custom.format(this, arg.custom.value, &s); - return s; - } - ++s; - // Parse fill and alignment. - if (Char c = *s) { - const Char *p = s + 1; - spec.align_ = ALIGN_DEFAULT; - do { - switch (*p) { - case '<': - spec.align_ = ALIGN_LEFT; - break; - case '>': - spec.align_ = ALIGN_RIGHT; - break; - case '=': - spec.align_ = ALIGN_NUMERIC; - break; - case '^': - spec.align_ = ALIGN_CENTER; - break; - } - if (spec.align_ != ALIGN_DEFAULT) { - if (p != s) { - if (c == '}') break; - if (c == '{') - FMT_THROW(FormatError("invalid fill character '{'")); - s += 2; - spec.fill_ = c; - } else ++s; - if (spec.align_ == ALIGN_NUMERIC) - require_numeric_argument(arg, '='); - break; - } - } while (--p >= s); - } - - // Parse sign. - switch (*s) { - case '+': - check_sign(s, arg); - spec.flags_ |= SIGN_FLAG | PLUS_FLAG; - break; - case '-': - check_sign(s, arg); - spec.flags_ |= MINUS_FLAG; - break; - case ' ': - check_sign(s, arg); - spec.flags_ |= SIGN_FLAG; - break; - } - - if (*s == '#') { - require_numeric_argument(arg, '#'); - spec.flags_ |= HASH_FLAG; - ++s; - } - - // Parse width and zero flag. - if ('0' <= *s && *s <= '9') { - if (*s == '0') { - require_numeric_argument(arg, '0'); - spec.align_ = ALIGN_NUMERIC; - spec.fill_ = '0'; - } - // Zero may be parsed again as a part of the width, but it is simpler - // and more efficient than checking if the next char is a digit. - spec.width_ = parse_nonnegative_int(s); - } - - // Parse precision. - if (*s == '.') { - ++s; - spec.precision_ = 0; - if ('0' <= *s && *s <= '9') { - spec.precision_ = parse_nonnegative_int(s); - } else if (*s == '{') { - ++s; - const Arg &precision_arg = parse_arg_index(s); - if (*s++ != '}') - FMT_THROW(FormatError("invalid format string")); - ULongLong value = 0; - switch (precision_arg.type) { - case Arg::INT: - if (precision_arg.int_value < 0) - FMT_THROW(FormatError("negative precision")); - value = precision_arg.int_value; - break; - case Arg::UINT: - value = precision_arg.uint_value; - break; - case Arg::LONG_LONG: - if (precision_arg.long_long_value < 0) - FMT_THROW(FormatError("negative precision")); - value = precision_arg.long_long_value; - break; - case Arg::ULONG_LONG: - value = precision_arg.ulong_long_value; - break; - default: - FMT_THROW(FormatError("precision is not integer")); - } - if (value > INT_MAX) - FMT_THROW(FormatError("number is too big")); - spec.precision_ = static_cast(value); - } else { - FMT_THROW(FormatError("missing precision specifier")); - } - if (arg.type < Arg::LAST_INTEGER_TYPE || arg.type == Arg::POINTER) { - FMT_THROW(FormatError( - fmt::format("precision not allowed in {} format specifier", - arg.type == Arg::POINTER ? "pointer" : "integer"))); - } - } - - // Parse type. - if (*s != '}' && *s) - spec.type_ = static_cast(*s++); - } - - if (*s++ != '}') - FMT_THROW(FormatError("missing '}' in format string")); - start_ = s; - - // Format argument. - internal::ArgFormatter(*this, spec, s - 1).visit(arg); - return s; -} - -template -void fmt::BasicFormatter::format( - BasicStringRef format_str, const ArgList &args) { - const Char *s = start_ = format_str.c_str(); - set_args(args); - while (*s) { - Char c = *s++; - if (c != '{' && c != '}') continue; - if (*s == c) { - write(writer_, start_, s); - start_ = ++s; - continue; - } - if (c == '}') - FMT_THROW(FormatError("unmatched '}' in format string")); - write(writer_, start_, s - 1); - Arg arg = parse_arg_index(s); - s = format(s, arg); - } - write(writer_, start_, s); -} - -FMT_FUNC void fmt::report_system_error( - int error_code, fmt::StringRef message) FMT_NOEXCEPT { - report_error(internal::format_system_error, error_code, message); -} - -#ifdef _WIN32 -FMT_FUNC void fmt::report_windows_error( - int error_code, fmt::StringRef message) FMT_NOEXCEPT { - report_error(internal::format_windows_error, error_code, message); -} -#endif - -FMT_FUNC void fmt::print(std::FILE *f, StringRef format_str, ArgList args) { - MemoryWriter w; - w.write(format_str, args); - std::fwrite(w.data(), 1, w.size(), f); -} - -FMT_FUNC void fmt::print(StringRef format_str, ArgList args) { - print(stdout, format_str, args); -} - -FMT_FUNC void fmt::print(std::ostream &os, StringRef format_str, ArgList args) { - MemoryWriter w; - w.write(format_str, args); - os.write(w.data(), w.size()); -} - -FMT_FUNC void fmt::print_colored(Color c, StringRef format, ArgList args) { - char escape[] = "\x1b[30m"; - escape[3] = '0' + static_cast(c); - std::fputs(escape, stdout); - print(format, args); - std::fputs(RESET_COLOR, stdout); -} - -FMT_FUNC int fmt::fprintf(std::FILE *f, StringRef format, ArgList args) { - MemoryWriter w; - printf(w, format, args); - std::size_t size = w.size(); - return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast(size); -} - -// Explicit instantiations for char. - -template const char *fmt::BasicFormatter::format( - const char *&format_str, const fmt::internal::Arg &arg); - -template void fmt::BasicFormatter::format( - BasicStringRef format, const ArgList &args); - -template void fmt::internal::PrintfFormatter::format( - BasicWriter &writer, BasicStringRef format, const ArgList &args); - -template int fmt::internal::CharTraits::format_float( - char *buffer, std::size_t size, const char *format, - unsigned width, int precision, double value); - -template int fmt::internal::CharTraits::format_float( - char *buffer, std::size_t size, const char *format, - unsigned width, int precision, long double value); - -// Explicit instantiations for wchar_t. - -template const wchar_t *fmt::BasicFormatter::format( - const wchar_t *&format_str, const fmt::internal::Arg &arg); - -template void fmt::BasicFormatter::format( - BasicStringRef format, const ArgList &args); - -template void fmt::internal::PrintfFormatter::format( - BasicWriter &writer, BasicStringRef format, - const ArgList &args); - -template int fmt::internal::CharTraits::format_float( - wchar_t *buffer, std::size_t size, const wchar_t *format, - unsigned width, int precision, double value); - -template int fmt::internal::CharTraits::format_float( - wchar_t *buffer, std::size_t size, const wchar_t *format, - unsigned width, int precision, long double value); - -#if _MSC_VER -# pragma warning(pop) -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.h deleted file mode 100644 index 03ed685383..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.h +++ /dev/null @@ -1,2546 +0,0 @@ -/* - Formatting library for C++ - - Copyright (c) 2012 - 2014, Victor Zverovich - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef FMT_FORMAT_H_ -#define FMT_FORMAT_H_ - -#include - -#include -#include -#include // for std::ptrdiff_t -#include -#include -#include -#include -#include -#include - -#if _SECURE_SCL -# include -#endif - -#ifdef _MSC_VER -# include // _BitScanReverse, _BitScanReverse64 - -namespace fmt { -namespace internal { -# pragma intrinsic(_BitScanReverse) -inline uint32_t clz(uint32_t x) { - unsigned long r = 0; - _BitScanReverse(&r, x); - return 31 - r; -} -# define FMT_BUILTIN_CLZ(n) fmt::internal::clz(n) -inline uint32_t clzll(uint64_t x) { - unsigned long r = 0; -# ifdef _WIN64 -# pragma intrinsic(_BitScanReverse64) - _BitScanReverse64(&r, x); -# else - // Scan the high 32 bits. - if (_BitScanReverse(&r, static_cast(x >> 32))) - return 63 - (r + 32); - - // Scan the low 32 bits. - _BitScanReverse(&r, static_cast(x)); -# endif - return 63 - r; -} -# define FMT_BUILTIN_CLZLL(n) fmt::internal::clzll(n) -} -} -#endif - -#ifdef __GNUC__ -# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -# define FMT_GCC_EXTENSION __extension__ -# if FMT_GCC_VERSION >= 406 -# pragma GCC diagnostic push -// Disable the warning about "long long" which is sometimes reported even -// when using __extension__. -# pragma GCC diagnostic ignored "-Wlong-long" -// Disable the warning about declaration shadowing because it affects too -// many valid cases. -# pragma GCC diagnostic ignored "-Wshadow" -# endif -# if __cplusplus >= 201103L || defined __GXX_EXPERIMENTAL_CXX0X__ -# define FMT_HAS_GXX_CXX11 1 -# endif -#else -# define FMT_GCC_EXTENSION -#endif - -#ifdef __clang__ -# pragma clang diagnostic ignored "-Wdocumentation-unknown-command" -#endif - -#ifdef __GNUC_LIBSTD__ -# define FMT_GNUC_LIBSTD_VERSION (__GNUC_LIBSTD__ * 100 + __GNUC_LIBSTD_MINOR__) -#endif - -#ifdef __has_feature -# define FMT_HAS_FEATURE(x) __has_feature(x) -#else -# define FMT_HAS_FEATURE(x) 0 -#endif - -#ifdef __has_builtin -# define FMT_HAS_BUILTIN(x) __has_builtin(x) -#else -# define FMT_HAS_BUILTIN(x) 0 -#endif - -#ifdef __has_cpp_attribute -# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) -#else -# define FMT_HAS_CPP_ATTRIBUTE(x) 0 -#endif - -#ifndef FMT_USE_VARIADIC_TEMPLATES -// Variadic templates are available in GCC since version 4.4 -// (http://gcc.gnu.org/projects/cxx0x.html) and in Visual C++ -// since version 2013. -# define FMT_USE_VARIADIC_TEMPLATES \ - (FMT_HAS_FEATURE(cxx_variadic_templates) || \ - (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1800) -#endif - -#ifndef FMT_USE_RVALUE_REFERENCES -// Don't use rvalue references when compiling with clang and an old libstdc++ -// as the latter doesn't provide std::move. -# if defined(FMT_GNUC_LIBSTD_VERSION) && FMT_GNUC_LIBSTD_VERSION <= 402 -# define FMT_USE_RVALUE_REFERENCES 0 -# else -# define FMT_USE_RVALUE_REFERENCES \ - (FMT_HAS_FEATURE(cxx_rvalue_references) || \ - (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1600) -# endif -#endif - -#if FMT_USE_RVALUE_REFERENCES -# include // for std::move -#endif - -// Define FMT_USE_NOEXCEPT to make C++ Format use noexcept (C++11 feature). -#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ - (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) -# define FMT_NOEXCEPT noexcept -#else -# define FMT_NOEXCEPT throw() -#endif - -// A macro to disallow the copy constructor and operator= functions -// This should be used in the private: declarations for a class -#if FMT_USE_DELETED_FUNCTIONS || FMT_HAS_FEATURE(cxx_deleted_functions) || \ - (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1800 -# define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&) = delete; \ - TypeName& operator=(const TypeName&) = delete -#else -# define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&); \ - TypeName& operator=(const TypeName&) -#endif - -namespace fmt { - -// Fix the warning about long long on older versions of GCC -// that don't support the diagnostic pragma. -FMT_GCC_EXTENSION typedef long long LongLong; -FMT_GCC_EXTENSION typedef unsigned long long ULongLong; - -#if FMT_USE_RVALUE_REFERENCES -using std::move; -#endif - -template -class BasicWriter; - -typedef BasicWriter Writer; -typedef BasicWriter WWriter; - -template -class BasicFormatter; - -template -void format(BasicFormatter &f, const Char *&format_str, const T &value); - -/** - \rst - A string reference. It can be constructed from a C string or - ``std::string``. - - You can use one of the following typedefs for common character types: - - +------------+-------------------------+ - | Type | Definition | - +============+=========================+ - | StringRef | BasicStringRef | - +------------+-------------------------+ - | WStringRef | BasicStringRef | - +------------+-------------------------+ - - This class is most useful as a parameter type to allow passing - different types of strings to a function, for example:: - - template - std::string format(StringRef format_str, const Args & ... args); - - format("{}", 42); - format(std::string("{}"), 42); - \endrst - */ -template -class BasicStringRef { - private: - const Char *data_; - std::size_t size_; - - public: - /** - Constructs a string reference object from a C string and a size. - */ - BasicStringRef(const Char *s, std::size_t size) : data_(s), size_(size) {} - - /** - Constructs a string reference object from a C string computing - the size with ``std::char_traits::length``. - */ - BasicStringRef(const Char *s) - : data_(s), size_(std::char_traits::length(s)) {} - - /** - Constructs a string reference from an `std::string` object. - */ - BasicStringRef(const std::basic_string &s) - : data_(s.c_str()), size_(s.size()) {} - - /** - Converts a string reference to an `std::string` object. - */ - operator std::basic_string() const { - return std::basic_string(data_, size()); - } - - /** - Returns the pointer to a C string. - */ - const Char *c_str() const { return data_; } - - /** - Returns the string size. - */ - std::size_t size() const { return size_; } - - friend bool operator==(BasicStringRef lhs, BasicStringRef rhs) { - return lhs.data_ == rhs.data_; - } - friend bool operator!=(BasicStringRef lhs, BasicStringRef rhs) { - return lhs.data_ != rhs.data_; - } -}; - -typedef BasicStringRef StringRef; -typedef BasicStringRef WStringRef; - -/** - A formatting error such as invalid format string. -*/ -class FormatError : public std::runtime_error { -public: - explicit FormatError(StringRef message) - : std::runtime_error(message.c_str()) {} -}; - -namespace internal { - -// The number of characters to store in the MemoryBuffer object itself -// to avoid dynamic memory allocation. -enum { INLINE_BUFFER_SIZE = 500 }; - -#if _SECURE_SCL -// Use checked iterator to avoid warnings on MSVC. -template -inline stdext::checked_array_iterator make_ptr(T *ptr, std::size_t size) { - return stdext::checked_array_iterator(ptr, size); -} -#else -template -inline T *make_ptr(T *ptr, std::size_t) { return ptr; } -#endif - -// A buffer for POD types. It supports a subset of std::vector's operations. -template -class Buffer { - private: - FMT_DISALLOW_COPY_AND_ASSIGN(Buffer); - - protected: - T *ptr_; - std::size_t size_; - std::size_t capacity_; - - Buffer(T *ptr = 0, std::size_t capacity = 0) - : ptr_(ptr), size_(0), capacity_(capacity) {} - - virtual void grow(std::size_t size) = 0; - - public: - virtual ~Buffer() {} - - // Returns the size of this buffer. - std::size_t size() const { return size_; } - - // Returns the capacity of this buffer. - std::size_t capacity() const { return capacity_; } - - // Resizes the buffer. If T is a POD type new elements are not initialized. - void resize(std::size_t new_size) { - if (new_size > capacity_) - grow(new_size); - size_ = new_size; - } - - // Reserves space to store at least capacity elements. - void reserve(std::size_t capacity) { - if (capacity > capacity_) - grow(capacity); - } - - void clear() FMT_NOEXCEPT { size_ = 0; } - - void push_back(const T &value) { - if (size_ == capacity_) - grow(size_ + 1); - ptr_[size_++] = value; - } - - // Appends data to the end of the buffer. - void append(const T *begin, const T *end); - - T &operator[](std::size_t index) { return ptr_[index]; } - const T &operator[](std::size_t index) const { return ptr_[index]; } -}; - -template -void Buffer::append(const T *begin, const T *end) { - std::ptrdiff_t num_elements = end - begin; - if (size_ + num_elements > capacity_) - grow(size_ + num_elements); - std::copy(begin, end, make_ptr(ptr_, capacity_) + size_); - size_ += num_elements; -} - -// A memory buffer for POD types with the first SIZE elements stored in -// the object itself. -template > -class MemoryBuffer : private Allocator, public Buffer { - private: - T data_[SIZE]; - - // Free memory allocated by the buffer. - void free() { - if (this->ptr_ != data_) this->deallocate(this->ptr_, this->capacity_); - } - - protected: - void grow(std::size_t size); - - public: - explicit MemoryBuffer(const Allocator &alloc = Allocator()) - : Allocator(alloc), Buffer(data_, SIZE) {} - ~MemoryBuffer() { free(); } - -#if FMT_USE_RVALUE_REFERENCES - private: - // Move data from other to this buffer. - void move(MemoryBuffer &other) { - Allocator &this_alloc = *this, &other_alloc = other; - this_alloc = std::move(other_alloc); - this->size_ = other.size_; - this->capacity_ = other.capacity_; - if (other.ptr_ == other.data_) { - this->ptr_ = data_; - std::copy(other.data_, - other.data_ + this->size_, make_ptr(data_, this->capacity_)); - } else { - this->ptr_ = other.ptr_; - // Set pointer to the inline array so that delete is not called - // when freeing. - other.ptr_ = other.data_; - } - } - - public: - MemoryBuffer(MemoryBuffer &&other) { - move(other); - } - - MemoryBuffer &operator=(MemoryBuffer &&other) { - assert(this != &other); - free(); - move(other); - return *this; - } -#endif - - // Returns a copy of the allocator associated with this buffer. - Allocator get_allocator() const { return *this; } -}; - -template -void MemoryBuffer::grow(std::size_t size) { - std::size_t new_capacity = - (std::max)(size, this->capacity_ + this->capacity_ / 2); - T *new_ptr = this->allocate(new_capacity); - // The following code doesn't throw, so the raw pointer above doesn't leak. - std::copy(this->ptr_, - this->ptr_ + this->size_, make_ptr(new_ptr, new_capacity)); - std::size_t old_capacity = this->capacity_; - T *old_ptr = this->ptr_; - this->capacity_ = new_capacity; - this->ptr_ = new_ptr; - // deallocate may throw (at least in principle), but it doesn't matter since - // the buffer already uses the new storage and will deallocate it in case - // of exception. - if (old_ptr != data_) - this->deallocate(old_ptr, old_capacity); -} - -#ifndef _MSC_VER -// Portable version of signbit. -inline int getsign(double x) { - // When compiled in C++11 mode signbit is no longer a macro but a function - // defined in namespace std and the macro is undefined. -# ifdef signbit - return signbit(x); -# else - return std::signbit(x); -# endif -} - -// Portable version of isinf. -# ifdef isinf -inline int isinfinity(double x) { return isinf(x); } -inline int isinfinity(long double x) { return isinf(x); } -# else -inline int isinfinity(double x) { return std::isinf(x); } -inline int isinfinity(long double x) { return std::isinf(x); } -# endif -#else -inline int getsign(double value) { - if (value < 0) return 1; - if (value == value) return 0; - int dec = 0, sign = 0; - char buffer[2]; // The buffer size must be >= 2 or _ecvt_s will fail. - _ecvt_s(buffer, sizeof(buffer), value, 0, &dec, &sign); - return sign; -} -inline int isinfinity(double x) { return !_finite(x); } -inline int isinfinity(long double x) { return !_finite(static_cast(x)); } -#endif - -template -class BasicCharTraits { - public: -#if _SECURE_SCL - typedef stdext::checked_array_iterator CharPtr; -#else - typedef Char *CharPtr; -#endif -}; - -template -class CharTraits; - -template <> -class CharTraits : public BasicCharTraits { - private: - // Conversion from wchar_t to char is not allowed. - static char convert(wchar_t); - -public: - typedef const wchar_t *UnsupportedStrType; - - static char convert(char value) { return value; } - - // Formats a floating-point number. - template - static int format_float(char *buffer, std::size_t size, - const char *format, unsigned width, int precision, T value); -}; - -template <> -class CharTraits : public BasicCharTraits { - public: - typedef const char *UnsupportedStrType; - - static wchar_t convert(char value) { return value; } - static wchar_t convert(wchar_t value) { return value; } - - template - static int format_float(wchar_t *buffer, std::size_t size, - const wchar_t *format, unsigned width, int precision, T value); -}; - -// Checks if a number is negative - used to avoid warnings. -template -struct SignChecker { - template - static bool is_negative(T value) { return value < 0; } -}; - -template <> -struct SignChecker { - template - static bool is_negative(T) { return false; } -}; - -// Returns true if value is negative, false otherwise. -// Same as (value < 0) but doesn't produce warnings if T is an unsigned type. -template -inline bool is_negative(T value) { - return SignChecker::is_signed>::is_negative(value); -} - -// Selects uint32_t if FitsIn32Bits is true, uint64_t otherwise. -template -struct TypeSelector { typedef uint32_t Type; }; - -template <> -struct TypeSelector { typedef uint64_t Type; }; - -template -struct IntTraits { - // Smallest of uint32_t and uint64_t that is large enough to represent - // all values of T. - typedef typename - TypeSelector::digits <= 32>::Type MainType; -}; - -// MakeUnsigned::Type gives an unsigned type corresponding to integer type T. -template -struct MakeUnsigned { typedef T Type; }; - -#define FMT_SPECIALIZE_MAKE_UNSIGNED(T, U) \ - template <> \ - struct MakeUnsigned { typedef U Type; } - -FMT_SPECIALIZE_MAKE_UNSIGNED(char, unsigned char); -FMT_SPECIALIZE_MAKE_UNSIGNED(signed char, unsigned char); -FMT_SPECIALIZE_MAKE_UNSIGNED(short, unsigned short); -FMT_SPECIALIZE_MAKE_UNSIGNED(int, unsigned); -FMT_SPECIALIZE_MAKE_UNSIGNED(long, unsigned long); -FMT_SPECIALIZE_MAKE_UNSIGNED(LongLong, ULongLong); - -void report_unknown_type(char code, const char *type); - -// Static data is placed in this class template to allow header-only -// configuration. -template -struct BasicData { - static const uint32_t POWERS_OF_10_32[]; - static const uint64_t POWERS_OF_10_64[]; - static const char DIGITS[]; -}; - -typedef BasicData<> Data; - -#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clz) -# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) -#endif - -#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clzll) -# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) -#endif - -#ifdef FMT_BUILTIN_CLZLL -// Returns the number of decimal digits in n. Leading zeros are not counted -// except for n == 0 in which case count_digits returns 1. -inline unsigned count_digits(uint64_t n) { - // Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 - // and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits. - unsigned t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12; - return t - (n < Data::POWERS_OF_10_64[t]) + 1; -} -#else -// Fallback version of count_digits used when __builtin_clz is not available. -inline unsigned count_digits(uint64_t n) { - unsigned count = 1; - for (;;) { - // Integer division is slow so do it for a group of four digits instead - // of for every digit. The idea comes from the talk by Alexandrescu - // "Three Optimization Tips for C++". See speed-test for a comparison. - if (n < 10) return count; - if (n < 100) return count + 1; - if (n < 1000) return count + 2; - if (n < 10000) return count + 3; - n /= 10000u; - count += 4; - } -} -#endif - -#ifdef FMT_BUILTIN_CLZ -// Optional version of count_digits for better performance on 32-bit platforms. -inline unsigned count_digits(uint32_t n) { - uint32_t t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12; - return t - (n < Data::POWERS_OF_10_32[t]) + 1; -} -#endif - -// Formats a decimal unsigned integer value writing into buffer. -template -inline void format_decimal(Char *buffer, UInt value, unsigned num_digits) { - --num_digits; - while (value >= 100) { - // Integer division is slow so do it for a group of two digits instead - // of for every digit. The idea comes from the talk by Alexandrescu - // "Three Optimization Tips for C++". See speed-test for a comparison. - unsigned index = (value % 100) * 2; - value /= 100; - buffer[num_digits] = Data::DIGITS[index + 1]; - buffer[num_digits - 1] = Data::DIGITS[index]; - num_digits -= 2; - } - if (value < 10) { - *buffer = static_cast('0' + value); - return; - } - unsigned index = static_cast(value * 2); - buffer[1] = Data::DIGITS[index + 1]; - buffer[0] = Data::DIGITS[index]; -} - -#ifdef _WIN32 -// A converter from UTF-8 to UTF-16. -// It is only provided for Windows since other systems support UTF-8 natively. -class UTF8ToUTF16 { - private: - MemoryBuffer buffer_; - - public: - explicit UTF8ToUTF16(StringRef s); - operator WStringRef() const { return WStringRef(&buffer_[0], size()); } - size_t size() const { return buffer_.size() - 1; } - const wchar_t *c_str() const { return &buffer_[0]; } - std::wstring str() const { return std::wstring(&buffer_[0], size()); } -}; - -// A converter from UTF-16 to UTF-8. -// It is only provided for Windows since other systems support UTF-8 natively. -class UTF16ToUTF8 { - private: - MemoryBuffer buffer_; - - public: - UTF16ToUTF8() {} - explicit UTF16ToUTF8(WStringRef s); - operator StringRef() const { return StringRef(&buffer_[0], size()); } - size_t size() const { return buffer_.size() - 1; } - const char *c_str() const { return &buffer_[0]; } - std::string str() const { return std::string(&buffer_[0], size()); } - - // Performs conversion returning a system error code instead of - // throwing exception on conversion error. This method may still throw - // in case of memory allocation error. - int convert(WStringRef s); -}; -#endif - -void format_system_error(fmt::Writer &out, int error_code, - fmt::StringRef message) FMT_NOEXCEPT; - -#ifdef _WIN32 -void format_windows_error(fmt::Writer &out, int error_code, - fmt::StringRef message) FMT_NOEXCEPT; -#endif - -// Computes max(Arg, 1) at compile time. It is used to avoid errors about -// allocating an array of 0 size. -template -struct NonZero { - enum { VALUE = Arg }; -}; - -template <> -struct NonZero<0> { - enum { VALUE = 1 }; -}; - -// The value of a formatting argument. It is a POD type to allow storage in -// internal::MemoryBuffer. -struct Value { - template - struct StringValue { - const Char *value; - std::size_t size; - }; - - typedef void (*FormatFunc)( - void *formatter, const void *arg, void *format_str_ptr); - - struct CustomValue { - const void *value; - FormatFunc format; - }; - - union { - int int_value; - unsigned uint_value; - LongLong long_long_value; - ULongLong ulong_long_value; - double double_value; - long double long_double_value; - const void *pointer; - StringValue string; - StringValue sstring; - StringValue ustring; - StringValue wstring; - CustomValue custom; - }; -}; - -struct Arg : Value { - enum Type { - NONE, - // Integer types should go first, - INT, UINT, LONG_LONG, ULONG_LONG, CHAR, LAST_INTEGER_TYPE = CHAR, - // followed by floating-point types. - DOUBLE, LONG_DOUBLE, LAST_NUMERIC_TYPE = LONG_DOUBLE, - CSTRING, STRING, WSTRING, POINTER, CUSTOM - }; - Type type; -}; - -// Makes a Value object from any type. -template -class MakeValue : public Value { - private: - // The following two methods are private to disallow formatting of - // arbitrary pointers. If you want to output a pointer cast it to - // "void *" or "const void *". In particular, this forbids formatting - // of "[const] volatile char *" which is printed as bool by iostreams. - // Do not implement! - template - MakeValue(const T *value); - template - MakeValue(T *value); - - void set_string(StringRef str) { - string.value = str.c_str(); - string.size = str.size(); - } - - void set_string(WStringRef str) { - CharTraits::convert(wchar_t()); - wstring.value = str.c_str(); - wstring.size = str.size(); - } - - // Formats an argument of a custom type, such as a user-defined class. - template - static void format_custom_arg( - void *formatter, const void *arg, void *format_str_ptr) { - format(*static_cast*>(formatter), - *static_cast(format_str_ptr), - *static_cast(arg)); - } - -public: - MakeValue() {} - -#define FMT_MAKE_VALUE(Type, field, TYPE) \ - MakeValue(Type value) { field = value; } \ - static uint64_t type(Type) { return Arg::TYPE; } - - FMT_MAKE_VALUE(bool, int_value, INT) - FMT_MAKE_VALUE(short, int_value, INT) - FMT_MAKE_VALUE(unsigned short, uint_value, UINT) - FMT_MAKE_VALUE(int, int_value, INT) - FMT_MAKE_VALUE(unsigned, uint_value, UINT) - - MakeValue(long value) { - // To minimize the number of types we need to deal with, long is - // translated either to int or to long long depending on its size. - if (sizeof(long) == sizeof(int)) - int_value = static_cast(value); - else - long_long_value = value; - } - static uint64_t type(long) { - return sizeof(long) == sizeof(int) ? Arg::INT : Arg::LONG_LONG; - } - - MakeValue(unsigned long value) { - if (sizeof(unsigned long) == sizeof(unsigned)) - uint_value = static_cast(value); - else - ulong_long_value = value; - } - static uint64_t type(unsigned long) { - return sizeof(unsigned long) == sizeof(unsigned) ? - Arg::UINT : Arg::ULONG_LONG; - } - - FMT_MAKE_VALUE(LongLong, long_long_value, LONG_LONG) - FMT_MAKE_VALUE(ULongLong, ulong_long_value, ULONG_LONG) - FMT_MAKE_VALUE(float, double_value, DOUBLE) - FMT_MAKE_VALUE(double, double_value, DOUBLE) - FMT_MAKE_VALUE(long double, long_double_value, LONG_DOUBLE) - FMT_MAKE_VALUE(signed char, int_value, CHAR) - FMT_MAKE_VALUE(unsigned char, int_value, CHAR) - FMT_MAKE_VALUE(char, int_value, CHAR) - - MakeValue(wchar_t value) { - int_value = internal::CharTraits::convert(value); - } - static uint64_t type(wchar_t) { return Arg::CHAR; } - -#define FMT_MAKE_STR_VALUE(Type, TYPE) \ - MakeValue(Type value) { set_string(value); } \ - static uint64_t type(Type) { return Arg::TYPE; } - - FMT_MAKE_VALUE(char *, string.value, CSTRING) - FMT_MAKE_VALUE(const char *, string.value, CSTRING) - FMT_MAKE_VALUE(const signed char *, sstring.value, CSTRING) - FMT_MAKE_VALUE(const unsigned char *, ustring.value, CSTRING) - FMT_MAKE_STR_VALUE(const std::string &, STRING) - FMT_MAKE_STR_VALUE(StringRef, STRING) - - FMT_MAKE_STR_VALUE(wchar_t *, WSTRING) - FMT_MAKE_STR_VALUE(const wchar_t *, WSTRING) - FMT_MAKE_STR_VALUE(const std::wstring &, WSTRING) - FMT_MAKE_STR_VALUE(WStringRef, WSTRING) - - FMT_MAKE_VALUE(void *, pointer, POINTER) - FMT_MAKE_VALUE(const void *, pointer, POINTER) - - template - MakeValue(const T &value) { - custom.value = &value; - custom.format = &format_custom_arg; - } - template - static uint64_t type(const T &) { return Arg::CUSTOM; } -}; - -#define FMT_DISPATCH(call) static_cast(this)->call - -// An argument visitor. -// To use ArgVisitor define a subclass that implements some or all of the -// visit methods with the same signatures as the methods in ArgVisitor, -// for example, visit_int(int). -// Specify the subclass name as the Impl template parameter. Then calling -// ArgVisitor::visit for some argument will dispatch to a visit method -// specific to the argument type. For example, if the argument type is -// double then visit_double(double) method of a subclass will be called. -// If the subclass doesn't contain a method with this signature, then -// a corresponding method of ArgVisitor will be called. -// -// Example: -// class MyArgVisitor : public ArgVisitor { -// public: -// void visit_int(int value) { print("{}", value); } -// void visit_double(double value) { print("{}", value ); } -// }; -// -// ArgVisitor uses the curiously recurring template pattern: -// http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern -template -class ArgVisitor { - public: - Result visit_unhandled_arg() { return Result(); } - - Result visit_int(int value) { - return FMT_DISPATCH(visit_any_int(value)); - } - Result visit_long_long(LongLong value) { - return FMT_DISPATCH(visit_any_int(value)); - } - Result visit_uint(unsigned value) { - return FMT_DISPATCH(visit_any_int(value)); - } - Result visit_ulong_long(ULongLong value) { - return FMT_DISPATCH(visit_any_int(value)); - } - Result visit_char(int value) { - return FMT_DISPATCH(visit_any_int(value)); - } - template - Result visit_any_int(T) { - return FMT_DISPATCH(visit_unhandled_arg()); - } - - Result visit_double(double value) { - return FMT_DISPATCH(visit_any_double(value)); - } - Result visit_long_double(long double value) { - return FMT_DISPATCH(visit_any_double(value)); - } - template - Result visit_any_double(T) { - return FMT_DISPATCH(visit_unhandled_arg()); - } - - Result visit_string(Arg::StringValue) { - return FMT_DISPATCH(visit_unhandled_arg()); - } - Result visit_wstring(Arg::StringValue) { - return FMT_DISPATCH(visit_unhandled_arg()); - } - Result visit_pointer(const void *) { - return FMT_DISPATCH(visit_unhandled_arg()); - } - Result visit_custom(Arg::CustomValue) { - return FMT_DISPATCH(visit_unhandled_arg()); - } - - Result visit(const Arg &arg) { - switch (arg.type) { - default: - assert(false); - return Result(); - case Arg::INT: - return FMT_DISPATCH(visit_int(arg.int_value)); - case Arg::UINT: - return FMT_DISPATCH(visit_uint(arg.uint_value)); - case Arg::LONG_LONG: - return FMT_DISPATCH(visit_long_long(arg.long_long_value)); - case Arg::ULONG_LONG: - return FMT_DISPATCH(visit_ulong_long(arg.ulong_long_value)); - case Arg::DOUBLE: - return FMT_DISPATCH(visit_double(arg.double_value)); - case Arg::LONG_DOUBLE: - return FMT_DISPATCH(visit_long_double(arg.long_double_value)); - case Arg::CHAR: - return FMT_DISPATCH(visit_char(arg.int_value)); - case Arg::CSTRING: { - Value::StringValue str = arg.string; - str.size = 0; - return FMT_DISPATCH(visit_string(str)); - } - case Arg::STRING: - return FMT_DISPATCH(visit_string(arg.string)); - case Arg::WSTRING: - return FMT_DISPATCH(visit_wstring(arg.wstring)); - case Arg::POINTER: - return FMT_DISPATCH(visit_pointer(arg.pointer)); - case Arg::CUSTOM: - return FMT_DISPATCH(visit_custom(arg.custom)); - } - } -}; - -class RuntimeError : public std::runtime_error { - protected: - RuntimeError() : std::runtime_error("") {} -}; - -template -class ArgFormatter; -} // namespace internal - -/** - An argument list. - */ -class ArgList { - private: - uint64_t types_; - const internal::Value *values_; - - public: - // Maximum number of arguments that can be passed in ArgList. - enum { MAX_ARGS = 16 }; - - ArgList() : types_(0) {} - ArgList(ULongLong types, const internal::Value *values) - : types_(types), values_(values) {} - - /** - Returns the argument at specified index. - */ - internal::Arg operator[](unsigned index) const { - using internal::Arg; - Arg arg; - if (index >= MAX_ARGS) { - arg.type = Arg::NONE; - return arg; - } - unsigned shift = index * 4; - uint64_t mask = 0xf; - Arg::Type type = - static_cast((types_ & (mask << shift)) >> shift); - arg.type = type; - if (type != Arg::NONE) { - internal::Value &value = arg; - value = values_[index]; - } - return arg; - } -}; - -struct FormatSpec; - -namespace internal { - -class FormatterBase { - private: - ArgList args_; - int next_arg_index_; - - // Returns the argument with specified index. - Arg do_get_arg(unsigned arg_index, const char *&error); - - protected: - void set_args(const ArgList &args) { - args_ = args; - next_arg_index_ = 0; - } - - // Returns the next argument. - Arg next_arg(const char *&error); - - // Checks if manual indexing is used and returns the argument with - // specified index. - Arg get_arg(unsigned arg_index, const char *&error); - - template - void write(BasicWriter &w, const Char *start, const Char *end) { - if (start != end) - w << BasicStringRef(start, end - start); - } -}; - -// A printf formatter. -template -class PrintfFormatter : private FormatterBase { - private: - void parse_flags(FormatSpec &spec, const Char *&s); - - // Returns the argument with specified index or, if arg_index is equal - // to the maximum unsigned value, the next argument. - Arg get_arg(const Char *s, - unsigned arg_index = (std::numeric_limits::max)()); - - // Parses argument index, flags and width and returns the argument index. - unsigned parse_header(const Char *&s, FormatSpec &spec); - - public: - void format(BasicWriter &writer, - BasicStringRef format_str, const ArgList &args); -}; -} // namespace internal - -// A formatter. -template -class BasicFormatter : private internal::FormatterBase { - private: - BasicWriter &writer_; - const Char *start_; - - FMT_DISALLOW_COPY_AND_ASSIGN(BasicFormatter); - - // Parses argument index and returns corresponding argument. - internal::Arg parse_arg_index(const Char *&s); - - public: - explicit BasicFormatter(BasicWriter &w) : writer_(w) {} - - BasicWriter &writer() { return writer_; } - - void format(BasicStringRef format_str, const ArgList &args); - - const Char *format(const Char *&format_str, const internal::Arg &arg); -}; - -enum Alignment { - ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC -}; - -// Flags. -enum { - SIGN_FLAG = 1, PLUS_FLAG = 2, MINUS_FLAG = 4, HASH_FLAG = 8, - CHAR_FLAG = 0x10 // Argument has char type - used in error reporting. -}; - -// An empty format specifier. -struct EmptySpec {}; - -// A type specifier. -template -struct TypeSpec : EmptySpec { - Alignment align() const { return ALIGN_DEFAULT; } - unsigned width() const { return 0; } - int precision() const { return -1; } - bool flag(unsigned) const { return false; } - char type() const { return TYPE; } - char fill() const { return ' '; } -}; - -// A width specifier. -struct WidthSpec { - unsigned width_; - // Fill is always wchar_t and cast to char if necessary to avoid having - // two specialization of WidthSpec and its subclasses. - wchar_t fill_; - - WidthSpec(unsigned width, wchar_t fill) : width_(width), fill_(fill) {} - - unsigned width() const { return width_; } - wchar_t fill() const { return fill_; } -}; - -// An alignment specifier. -struct AlignSpec : WidthSpec { - Alignment align_; - - AlignSpec(unsigned width, wchar_t fill, Alignment align = ALIGN_DEFAULT) - : WidthSpec(width, fill), align_(align) {} - - Alignment align() const { return align_; } - - int precision() const { return -1; } -}; - -// An alignment and type specifier. -template -struct AlignTypeSpec : AlignSpec { - AlignTypeSpec(unsigned width, wchar_t fill) : AlignSpec(width, fill) {} - - bool flag(unsigned) const { return false; } - char type() const { return TYPE; } -}; - -// A full format specifier. -struct FormatSpec : AlignSpec { - unsigned flags_; - int precision_; - char type_; - - FormatSpec( - unsigned width = 0, char type = 0, wchar_t fill = ' ') - : AlignSpec(width, fill), flags_(0), precision_(-1), type_(type) {} - - bool flag(unsigned f) const { return (flags_ & f) != 0; } - int precision() const { return precision_; } - char type() const { return type_; } -}; - -// An integer format specifier. -template , typename Char = char> -class IntFormatSpec : public SpecT { - private: - T value_; - - public: - IntFormatSpec(T val, const SpecT &spec = SpecT()) - : SpecT(spec), value_(val) {} - - T value() const { return value_; } -}; - -// A string format specifier. -template -class StrFormatSpec : public AlignSpec { - private: - const T *str_; - - public: - StrFormatSpec(const T *str, unsigned width, wchar_t fill) - : AlignSpec(width, fill), str_(str) {} - - const T *str() const { return str_; } -}; - -/** - Returns an integer format specifier to format the value in base 2. - */ -IntFormatSpec > bin(int value); - -/** - Returns an integer format specifier to format the value in base 8. - */ -IntFormatSpec > oct(int value); - -/** - Returns an integer format specifier to format the value in base 16 using - lower-case letters for the digits above 9. - */ -IntFormatSpec > hex(int value); - -/** - Returns an integer formatter format specifier to format in base 16 using - upper-case letters for the digits above 9. - */ -IntFormatSpec > hexu(int value); - -/** - \rst - Returns an integer format specifier to pad the formatted argument with the - fill character to the specified width using the default (right) numeric - alignment. - - **Example**:: - - MemoryWriter out; - out << pad(hex(0xcafe), 8, '0'); - // out.str() == "0000cafe" - - \endrst - */ -template -IntFormatSpec, Char> pad( - int value, unsigned width, Char fill = ' '); - -#define FMT_DEFINE_INT_FORMATTERS(TYPE) \ -inline IntFormatSpec > bin(TYPE value) { \ - return IntFormatSpec >(value, TypeSpec<'b'>()); \ -} \ - \ -inline IntFormatSpec > oct(TYPE value) { \ - return IntFormatSpec >(value, TypeSpec<'o'>()); \ -} \ - \ -inline IntFormatSpec > hex(TYPE value) { \ - return IntFormatSpec >(value, TypeSpec<'x'>()); \ -} \ - \ -inline IntFormatSpec > hexu(TYPE value) { \ - return IntFormatSpec >(value, TypeSpec<'X'>()); \ -} \ - \ -template \ -inline IntFormatSpec > pad( \ - IntFormatSpec > f, unsigned width) { \ - return IntFormatSpec >( \ - f.value(), AlignTypeSpec(width, ' ')); \ -} \ - \ -/* For compatibility with older compilers we provide two overloads for pad, */ \ -/* one that takes a fill character and one that doesn't. In the future this */ \ -/* can be replaced with one overload making the template argument Char */ \ -/* default to char (C++11). */ \ -template \ -inline IntFormatSpec, Char> pad( \ - IntFormatSpec, Char> f, \ - unsigned width, Char fill) { \ - return IntFormatSpec, Char>( \ - f.value(), AlignTypeSpec(width, fill)); \ -} \ - \ -inline IntFormatSpec > pad( \ - TYPE value, unsigned width) { \ - return IntFormatSpec >( \ - value, AlignTypeSpec<0>(width, ' ')); \ -} \ - \ -template \ -inline IntFormatSpec, Char> pad( \ - TYPE value, unsigned width, Char fill) { \ - return IntFormatSpec, Char>( \ - value, AlignTypeSpec<0>(width, fill)); \ -} - -FMT_DEFINE_INT_FORMATTERS(int) -FMT_DEFINE_INT_FORMATTERS(long) -FMT_DEFINE_INT_FORMATTERS(unsigned) -FMT_DEFINE_INT_FORMATTERS(unsigned long) -FMT_DEFINE_INT_FORMATTERS(LongLong) -FMT_DEFINE_INT_FORMATTERS(ULongLong) - -/** - \rst - Returns a string formatter that pads the formatted argument with the fill - character to the specified width using the default (left) string alignment. - - **Example**:: - - std::string s = str(MemoryWriter() << pad("abc", 8)); - // s == "abc " - - \endrst - */ -template -inline StrFormatSpec pad( - const Char *str, unsigned width, Char fill = ' ') { - return StrFormatSpec(str, width, fill); -} - -inline StrFormatSpec pad( - const wchar_t *str, unsigned width, char fill = ' ') { - return StrFormatSpec(str, width, fill); -} - -// Generates a comma-separated list with results of applying f to -// numbers 0..n-1. -# define FMT_GEN(n, f) FMT_GEN##n(f) -# define FMT_GEN1(f) f(0) -# define FMT_GEN2(f) FMT_GEN1(f), f(1) -# define FMT_GEN3(f) FMT_GEN2(f), f(2) -# define FMT_GEN4(f) FMT_GEN3(f), f(3) -# define FMT_GEN5(f) FMT_GEN4(f), f(4) -# define FMT_GEN6(f) FMT_GEN5(f), f(5) -# define FMT_GEN7(f) FMT_GEN6(f), f(6) -# define FMT_GEN8(f) FMT_GEN7(f), f(7) -# define FMT_GEN9(f) FMT_GEN8(f), f(8) -# define FMT_GEN10(f) FMT_GEN9(f), f(9) -# define FMT_GEN11(f) FMT_GEN10(f), f(10) -# define FMT_GEN12(f) FMT_GEN11(f), f(11) -# define FMT_GEN13(f) FMT_GEN12(f), f(12) -# define FMT_GEN14(f) FMT_GEN13(f), f(13) -# define FMT_GEN15(f) FMT_GEN14(f), f(14) - -namespace internal { -inline uint64_t make_type() { return 0; } - -template -inline uint64_t make_type(const T &arg) { return MakeValue::type(arg); } - -#if FMT_USE_VARIADIC_TEMPLATES -template -inline uint64_t make_type(const Arg &first, const Args & ... tail) { - return make_type(first) | (make_type(tail...) << 4); -} -#else - -struct ArgType { - uint64_t type; - - ArgType() : type(0) {} - - template - ArgType(const T &arg) : type(make_type(arg)) {} -}; - -# define FMT_ARG_TYPE_DEFAULT(n) ArgType t##n = ArgType() - -inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) { - return t0.type | (t1.type << 4) | (t2.type << 8) | (t3.type << 12) | - (t4.type << 16) | (t5.type << 20) | (t6.type << 24) | (t7.type << 28) | - (t8.type << 32) | (t9.type << 36) | (t10.type << 40) | (t11.type << 44) | - (t12.type << 48) | (t13.type << 52) | (t14.type << 56); -} -#endif -} // namespace internal - -# define FMT_MAKE_TEMPLATE_ARG(n) typename T##n -# define FMT_MAKE_ARG_TYPE(n) T##n -# define FMT_MAKE_ARG(n) const T##n &v##n -# define FMT_MAKE_REF_char(n) fmt::internal::MakeValue(v##n) -# define FMT_MAKE_REF_wchar_t(n) fmt::internal::MakeValue(v##n) - -#if FMT_USE_VARIADIC_TEMPLATES -// Defines a variadic function returning void. -# define FMT_VARIADIC_VOID(func, arg_type) \ - template \ - void func(arg_type arg1, const Args & ... args) { \ - const fmt::internal::Value values[ \ - fmt::internal::NonZero::VALUE] = { \ - fmt::internal::MakeValue(args)... \ - }; \ - func(arg1, ArgList(fmt::internal::make_type(args...), values)); \ - } - -// Defines a variadic constructor. -# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ - template \ - ctor(arg0_type arg0, arg1_type arg1, const Args & ... args) { \ - using fmt::internal::MakeValue; \ - const fmt::internal::Value values[ \ - fmt::internal::NonZero::VALUE] = { \ - MakeValue(args)... \ - }; \ - func(arg0, arg1, ArgList(fmt::internal::make_type(args...), values)); \ - } - -#else - -# define FMT_MAKE_REF(n) fmt::internal::MakeValue(v##n) -# define FMT_MAKE_REF2(n) v##n - -// Defines a wrapper for a function taking one argument of type arg_type -// and n additional arguments of arbitrary types. -# define FMT_WRAP1(func, arg_type, n) \ - template \ - inline void func(arg_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ - const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF)}; \ - func(arg1, fmt::ArgList( \ - fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), vals)); \ - } - -// Emulates a variadic function returning void on a pre-C++11 compiler. -# define FMT_VARIADIC_VOID(func, arg_type) \ - inline void func(arg_type arg) { func(arg, fmt::ArgList()); } \ - FMT_WRAP1(func, arg_type, 1) FMT_WRAP1(func, arg_type, 2) \ - FMT_WRAP1(func, arg_type, 3) FMT_WRAP1(func, arg_type, 4) \ - FMT_WRAP1(func, arg_type, 5) FMT_WRAP1(func, arg_type, 6) \ - FMT_WRAP1(func, arg_type, 7) FMT_WRAP1(func, arg_type, 8) \ - FMT_WRAP1(func, arg_type, 9) FMT_WRAP1(func, arg_type, 10) - -# define FMT_CTOR(ctor, func, arg0_type, arg1_type, n) \ - template \ - ctor(arg0_type arg0, arg1_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ - const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF)}; \ - func(arg0, arg1, fmt::ArgList( \ - fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), vals)); \ - } - -// Emulates a variadic constructor on a pre-C++11 compiler. -# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 1) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 2) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 3) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 4) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 5) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 6) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 7) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 8) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 9) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 10) -#endif - -// Generates a comma-separated list with results of applying f to pairs -// (argument, index). -#define FMT_FOR_EACH1(f, x0) f(x0, 0) -#define FMT_FOR_EACH2(f, x0, x1) \ - FMT_FOR_EACH1(f, x0), f(x1, 1) -#define FMT_FOR_EACH3(f, x0, x1, x2) \ - FMT_FOR_EACH2(f, x0 ,x1), f(x2, 2) -#define FMT_FOR_EACH4(f, x0, x1, x2, x3) \ - FMT_FOR_EACH3(f, x0, x1, x2), f(x3, 3) -#define FMT_FOR_EACH5(f, x0, x1, x2, x3, x4) \ - FMT_FOR_EACH4(f, x0, x1, x2, x3), f(x4, 4) -#define FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5) \ - FMT_FOR_EACH5(f, x0, x1, x2, x3, x4), f(x5, 5) -#define FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6) \ - FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5), f(x6, 6) -#define FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7) \ - FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6), f(x7, 7) -#define FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8) \ - FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7), f(x8, 8) -#define FMT_FOR_EACH10(f, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) \ - FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8), f(x9, 9) - -/** - An error returned by an operating system or a language runtime, - for example a file opening error. -*/ -class SystemError : public internal::RuntimeError { - private: - void init(int err_code, StringRef format_str, ArgList args); - - protected: - int error_code_; - - typedef char Char; // For FMT_VARIADIC_CTOR. - - SystemError() {} - - public: - /** - \rst - Constructs a :class:`fmt::SystemError` object with the description - of the form - - .. parsed-literal:: - **: ** - - where ** is the formatted message and ** is - the system message corresponding to the error code. - *error_code* is a system error code as given by ``errno``. - If *error_code* is not a valid error code such as -1, the system message - may look like "Unknown error -1" and is platform-dependent. - - **Example**:: - - // This throws a SystemError with the description - // cannot open file 'madeup': No such file or directory - // or similar (system message may vary). - const char *filename = "madeup"; - std::FILE *file = std::fopen(filename, "r"); - if (!file) - throw fmt::SystemError(errno, "cannot open file '{}'", filename); - \endrst - */ - SystemError(int error_code, StringRef message) { - init(error_code, message, ArgList()); - } - FMT_VARIADIC_CTOR(SystemError, init, int, StringRef) - - int error_code() const { return error_code_; } -}; - -/** - \rst - This template provides operations for formatting and writing data into - a character stream. The output is stored in a buffer provided by a subclass - such as :class:`fmt::BasicMemoryWriter`. - - You can use one of the following typedefs for common character types: - - +---------+----------------------+ - | Type | Definition | - +=========+======================+ - | Writer | BasicWriter | - +---------+----------------------+ - | WWriter | BasicWriter | - +---------+----------------------+ - - \endrst - */ -template -class BasicWriter { - private: - // Output buffer. - internal::Buffer &buffer_; - - FMT_DISALLOW_COPY_AND_ASSIGN(BasicWriter); - - typedef typename internal::CharTraits::CharPtr CharPtr; - -#if _SECURE_SCL - // Returns pointer value. - static Char *get(CharPtr p) { return p.base(); } -#else - static Char *get(Char *p) { return p; } -#endif - - // Fills the padding around the content and returns the pointer to the - // content area. - static CharPtr fill_padding(CharPtr buffer, - unsigned total_size, std::size_t content_size, wchar_t fill); - - // Grows the buffer by n characters and returns a pointer to the newly - // allocated area. - CharPtr grow_buffer(std::size_t n) { - std::size_t size = buffer_.size(); - buffer_.resize(size + n); - return internal::make_ptr(&buffer_[size], n); - } - - // Prepare a buffer for integer formatting. - CharPtr prepare_int_buffer(unsigned num_digits, - const EmptySpec &, const char *prefix, unsigned prefix_size) { - unsigned size = prefix_size + num_digits; - CharPtr p = grow_buffer(size); - std::copy(prefix, prefix + prefix_size, p); - return p + size - 1; - } - - template - CharPtr prepare_int_buffer(unsigned num_digits, - const Spec &spec, const char *prefix, unsigned prefix_size); - - // Formats an integer. - template - void write_int(T value, Spec spec); - - // Formats a floating-point number (double or long double). - template - void write_double(T value, const FormatSpec &spec); - - // Writes a formatted string. - template - CharPtr write_str( - const StrChar *s, std::size_t size, const AlignSpec &spec); - - template - void write_str( - const internal::Arg::StringValue &str, const FormatSpec &spec); - - // This method is private to disallow writing a wide string to a - // char stream and vice versa. If you want to print a wide string - // as a pointer as std::ostream does, cast it to const void*. - // Do not implement! - void operator<<(typename internal::CharTraits::UnsupportedStrType); - - // Appends floating-point length specifier to the format string. - // The second argument is only used for overload resolution. - void append_float_length(Char *&format_ptr, long double) { - *format_ptr++ = 'L'; - } - - template - void append_float_length(Char *&, T) {} - - friend class internal::ArgFormatter; - friend class internal::PrintfFormatter; - - protected: - /** - Constructs a ``BasicWriter`` object. - */ - explicit BasicWriter(internal::Buffer &b) : buffer_(b) {} - - public: - /** - Destroys a ``BasicWriter`` object. - */ - virtual ~BasicWriter() {} - - /** - Returns the total number of characters written. - */ - std::size_t size() const { return buffer_.size(); } - - /** - Returns a pointer to the output buffer content. No terminating null - character is appended. - */ - const Char *data() const FMT_NOEXCEPT { return &buffer_[0]; } - - /** - Returns a pointer to the output buffer content with terminating null - character appended. - */ - const Char *c_str() const { - std::size_t size = buffer_.size(); - buffer_.reserve(size + 1); - buffer_[size] = '\0'; - return &buffer_[0]; - } - - /** - Returns the content of the output buffer as an `std::string`. - */ - std::basic_string str() const { - return std::basic_string(&buffer_[0], buffer_.size()); - } - - /** - \rst - Writes formatted data. - - *args* is an argument list representing arbitrary arguments. - - **Example**:: - - MemoryWriter out; - out.write("Current point:\n"); - out.write("({:+f}, {:+f})", -3.14, 3.14); - - This will write the following output to the ``out`` object: - - .. code-block:: none - - Current point: - (-3.140000, +3.140000) - - The output can be accessed using :func:`data()`, :func:`c_str` or - :func:`str` methods. - - See also :ref:`syntax`. - \endrst - */ - void write(BasicStringRef format, ArgList args) { - BasicFormatter(*this).format(format, args); - } - FMT_VARIADIC_VOID(write, BasicStringRef) - - BasicWriter &operator<<(int value) { - return *this << IntFormatSpec(value); - } - BasicWriter &operator<<(unsigned value) { - return *this << IntFormatSpec(value); - } - BasicWriter &operator<<(long value) { - return *this << IntFormatSpec(value); - } - BasicWriter &operator<<(unsigned long value) { - return *this << IntFormatSpec(value); - } - BasicWriter &operator<<(LongLong value) { - return *this << IntFormatSpec(value); - } - - /** - Formats *value* and writes it to the stream. - */ - BasicWriter &operator<<(ULongLong value) { - return *this << IntFormatSpec(value); - } - - BasicWriter &operator<<(double value) { - write_double(value, FormatSpec()); - return *this; - } - - /** - Formats *value* using the general format for floating-point numbers - (``'g'``) and writes it to the stream. - */ - BasicWriter &operator<<(long double value) { - write_double(value, FormatSpec()); - return *this; - } - - /** - Writes a character to the stream. - */ - BasicWriter &operator<<(char value) { - buffer_.push_back(value); - return *this; - } - - BasicWriter &operator<<(wchar_t value) { - buffer_.push_back(internal::CharTraits::convert(value)); - return *this; - } - - /** - Writes *value* to the stream. - */ - BasicWriter &operator<<(fmt::BasicStringRef value) { - const Char *str = value.c_str(); - buffer_.append(str, str + value.size()); - return *this; - } - - template - BasicWriter &operator<<(IntFormatSpec spec) { - internal::CharTraits::convert(FillChar()); - write_int(spec.value(), spec); - return *this; - } - - template - BasicWriter &operator<<(const StrFormatSpec &spec) { - const StrChar *s = spec.str(); - // TODO: error if fill is not convertible to Char - write_str(s, std::char_traits::length(s), spec); - return *this; - } - - void clear() FMT_NOEXCEPT { buffer_.clear(); } -}; - -template -template -typename BasicWriter::CharPtr BasicWriter::write_str( - const StrChar *s, std::size_t size, const AlignSpec &spec) { - CharPtr out = CharPtr(); - if (spec.width() > size) { - out = grow_buffer(spec.width()); - Char fill = static_cast(spec.fill()); - if (spec.align() == ALIGN_RIGHT) { - std::fill_n(out, spec.width() - size, fill); - out += spec.width() - size; - } else if (spec.align() == ALIGN_CENTER) { - out = fill_padding(out, spec.width(), size, fill); - } else { - std::fill_n(out + size, spec.width() - size, fill); - } - } else { - out = grow_buffer(size); - } - std::copy(s, s + size, out); - return out; -} - -template -typename BasicWriter::CharPtr - BasicWriter::fill_padding( - CharPtr buffer, unsigned total_size, - std::size_t content_size, wchar_t fill) { - std::size_t padding = total_size - content_size; - std::size_t left_padding = padding / 2; - Char fill_char = static_cast(fill); - std::fill_n(buffer, left_padding, fill_char); - buffer += left_padding; - CharPtr content = buffer; - std::fill_n(buffer + content_size, padding - left_padding, fill_char); - return content; -} - -template -template -typename BasicWriter::CharPtr - BasicWriter::prepare_int_buffer( - unsigned num_digits, const Spec &spec, - const char *prefix, unsigned prefix_size) { - unsigned width = spec.width(); - Alignment align = spec.align(); - Char fill = static_cast(spec.fill()); - if (spec.precision() > static_cast(num_digits)) { - // Octal prefix '0' is counted as a digit, so ignore it if precision - // is specified. - if (prefix_size > 0 && prefix[prefix_size - 1] == '0') - --prefix_size; - unsigned number_size = prefix_size + spec.precision(); - AlignSpec subspec(number_size, '0', ALIGN_NUMERIC); - if (number_size >= width) - return prepare_int_buffer(num_digits, subspec, prefix, prefix_size); - buffer_.reserve(width); - unsigned fill_size = width - number_size; - if (align != ALIGN_LEFT) { - CharPtr p = grow_buffer(fill_size); - std::fill(p, p + fill_size, fill); - } - CharPtr result = prepare_int_buffer( - num_digits, subspec, prefix, prefix_size); - if (align == ALIGN_LEFT) { - CharPtr p = grow_buffer(fill_size); - std::fill(p, p + fill_size, fill); - } - return result; - } - unsigned size = prefix_size + num_digits; - if (width <= size) { - CharPtr p = grow_buffer(size); - std::copy(prefix, prefix + prefix_size, p); - return p + size - 1; - } - CharPtr p = grow_buffer(width); - CharPtr end = p + width; - if (align == ALIGN_LEFT) { - std::copy(prefix, prefix + prefix_size, p); - p += size; - std::fill(p, end, fill); - } else if (align == ALIGN_CENTER) { - p = fill_padding(p, width, size, fill); - std::copy(prefix, prefix + prefix_size, p); - p += size; - } else { - if (align == ALIGN_NUMERIC) { - if (prefix_size != 0) { - p = std::copy(prefix, prefix + prefix_size, p); - size -= prefix_size; - } - } else { - std::copy(prefix, prefix + prefix_size, end - size); - } - std::fill(p, end - size, fill); - p = end; - } - return p - 1; -} - -template -template -void BasicWriter::write_int(T value, Spec spec) { - unsigned prefix_size = 0; - typedef typename internal::IntTraits::MainType UnsignedType; - UnsignedType abs_value = value; - char prefix[4] = ""; - if (internal::is_negative(value)) { - prefix[0] = '-'; - ++prefix_size; - abs_value = 0 - abs_value; - } else if (spec.flag(SIGN_FLAG)) { - prefix[0] = spec.flag(PLUS_FLAG) ? '+' : ' '; - ++prefix_size; - } - switch (spec.type()) { - case 0: case 'd': { - unsigned num_digits = internal::count_digits(abs_value); - CharPtr p = prepare_int_buffer( - num_digits, spec, prefix, prefix_size) + 1 - num_digits; - internal::format_decimal(get(p), abs_value, num_digits); - break; - } - case 'x': case 'X': { - UnsignedType n = abs_value; - if (spec.flag(HASH_FLAG)) { - prefix[prefix_size++] = '0'; - prefix[prefix_size++] = spec.type(); - } - unsigned num_digits = 0; - do { - ++num_digits; - } while ((n >>= 4) != 0); - Char *p = get(prepare_int_buffer( - num_digits, spec, prefix, prefix_size)); - n = abs_value; - const char *digits = spec.type() == 'x' ? - "0123456789abcdef" : "0123456789ABCDEF"; - do { - *p-- = digits[n & 0xf]; - } while ((n >>= 4) != 0); - break; - } - case 'b': case 'B': { - UnsignedType n = abs_value; - if (spec.flag(HASH_FLAG)) { - prefix[prefix_size++] = '0'; - prefix[prefix_size++] = spec.type(); - } - unsigned num_digits = 0; - do { - ++num_digits; - } while ((n >>= 1) != 0); - Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size)); - n = abs_value; - do { - *p-- = '0' + (n & 1); - } while ((n >>= 1) != 0); - break; - } - case 'o': { - UnsignedType n = abs_value; - if (spec.flag(HASH_FLAG)) - prefix[prefix_size++] = '0'; - unsigned num_digits = 0; - do { - ++num_digits; - } while ((n >>= 3) != 0); - Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size)); - n = abs_value; - do { - *p-- = '0' + (n & 7); - } while ((n >>= 3) != 0); - break; - } - default: - internal::report_unknown_type( - spec.type(), spec.flag(CHAR_FLAG) ? "char" : "integer"); - break; - } -} - -template -template -void BasicWriter::write_double( - T value, const FormatSpec &spec) { - // Check type. - char type = spec.type(); - bool upper = false; - switch (type) { - case 0: - type = 'g'; - break; - case 'e': case 'f': case 'g': case 'a': - break; - case 'F': -#ifdef _MSC_VER - // MSVC's printf doesn't support 'F'. - type = 'f'; -#endif - // Fall through. - case 'E': case 'G': case 'A': - upper = true; - break; - default: - internal::report_unknown_type(type, "double"); - break; - } - - char sign = 0; - // Use getsign instead of value < 0 because the latter is always - // false for NaN. - if (internal::getsign(static_cast(value))) { - sign = '-'; - value = -value; - } else if (spec.flag(SIGN_FLAG)) { - sign = spec.flag(PLUS_FLAG) ? '+' : ' '; - } - - if (value != value) { - // Format NaN ourselves because sprintf's output is not consistent - // across platforms. - std::size_t nan_size = 4; - const char *nan = upper ? " NAN" : " nan"; - if (!sign) { - --nan_size; - ++nan; - } - CharPtr out = write_str(nan, nan_size, spec); - if (sign) - *out = sign; - return; - } - - if (internal::isinfinity(value)) { - // Format infinity ourselves because sprintf's output is not consistent - // across platforms. - std::size_t inf_size = 4; - const char *inf = upper ? " INF" : " inf"; - if (!sign) { - --inf_size; - ++inf; - } - CharPtr out = write_str(inf, inf_size, spec); - if (sign) - *out = sign; - return; - } - - std::size_t offset = buffer_.size(); - unsigned width = spec.width(); - if (sign) { - buffer_.reserve(buffer_.size() + (std::max)(width, 1u)); - if (width > 0) - --width; - ++offset; - } - - // Build format string. - enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg - Char format[MAX_FORMAT_SIZE]; - Char *format_ptr = format; - *format_ptr++ = '%'; - unsigned width_for_sprintf = width; - if (spec.flag(HASH_FLAG)) - *format_ptr++ = '#'; - if (spec.align() == ALIGN_CENTER) { - width_for_sprintf = 0; - } else { - if (spec.align() == ALIGN_LEFT) - *format_ptr++ = '-'; - if (width != 0) - *format_ptr++ = '*'; - } - if (spec.precision() >= 0) { - *format_ptr++ = '.'; - *format_ptr++ = '*'; - } - - append_float_length(format_ptr, value); - *format_ptr++ = type; - *format_ptr = '\0'; - - // Format using snprintf. - Char fill = static_cast(spec.fill()); - for (;;) { - std::size_t buffer_size = buffer_.capacity() - offset; -#if _MSC_VER - // MSVC's vsnprintf_s doesn't work with zero size, so reserve - // space for at least one extra character to make the size non-zero. - // Note that the buffer's capacity will increase by more than 1. - if (buffer_size == 0) { - buffer_.reserve(offset + 1); - buffer_size = buffer_.capacity() - offset; - } -#endif - Char *start = &buffer_[offset]; - int n = internal::CharTraits::format_float( - start, buffer_size, format, width_for_sprintf, spec.precision(), value); - if (n >= 0 && offset + n < buffer_.capacity()) { - if (sign) { - if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) || - *start != ' ') { - *(start - 1) = sign; - sign = 0; - } else { - *(start - 1) = fill; - } - ++n; - } - if (spec.align() == ALIGN_CENTER && - spec.width() > static_cast(n)) { - width = spec.width(); - CharPtr p = grow_buffer(width); - std::copy(p, p + n, p + (width - n) / 2); - fill_padding(p, spec.width(), n, fill); - return; - } - if (spec.fill() != ' ' || sign) { - while (*start == ' ') - *start++ = fill; - if (sign) - *(start - 1) = sign; - } - grow_buffer(n); - return; - } - // If n is negative we ask to increase the capacity by at least 1, - // but as std::vector, the buffer grows exponentially. - buffer_.reserve(n >= 0 ? offset + n + 1 : buffer_.capacity() + 1); - } -} - -/** - \rst - This template provides operations for formatting and writing data into - a character stream. The output is stored in a memory buffer that grows - dynamically. - - You can use one of the following typedefs for common character types - and the standard allocator: - - +---------------+-----------------------------------------------+ - | Type | Definition | - +===============+===============================================+ - | MemoryWriter | BasicWriter> | - +---------------+-----------------------------------------------+ - | WMemoryWriter | BasicWriter> | - +---------------+-----------------------------------------------+ - - **Example**:: - - MemoryWriter out; - out << "The answer is " << 42 << "\n"; - out.write("({:+f}, {:+f})", -3.14, 3.14); - - This will write the following output to the ``out`` object: - - .. code-block:: none - - The answer is 42 - (-3.140000, +3.140000) - - The output can be converted to an ``std::string`` with ``out.str()`` or - accessed as a C string with ``out.c_str()``. - \endrst - */ -template > -class BasicMemoryWriter : public BasicWriter { - private: - internal::MemoryBuffer buffer_; - - public: - explicit BasicMemoryWriter(const Allocator& alloc = Allocator()) - : BasicWriter(buffer_), buffer_(alloc) {} - -#if FMT_USE_RVALUE_REFERENCES - /** - Constructs a :class:`fmt::BasicMemoryWriter` object moving the content - of the other object to it. - */ - BasicMemoryWriter(BasicMemoryWriter &&other) - : BasicWriter(buffer_), buffer_(std::move(other.buffer_)) { - } - - /** - Moves the content of the other ``BasicMemoryWriter`` object to this one. - */ - BasicMemoryWriter &operator=(BasicMemoryWriter &&other) { - buffer_ = std::move(other.buffer_); - return *this; - } -#endif -}; - -typedef BasicMemoryWriter MemoryWriter; -typedef BasicMemoryWriter WMemoryWriter; - -// Formats a value. -template -void format(BasicFormatter &f, const Char *&format_str, const T &value) { - std::basic_ostringstream os; - os << value; - internal::Arg arg; - internal::Value &arg_value = arg; - std::basic_string str = os.str(); - arg_value = internal::MakeValue(str); - arg.type = static_cast(internal::MakeValue::type(str)); - format_str = f.format(format_str, arg); -} - -// Reports a system error without throwing an exception. -// Can be used to report errors from destructors. -void report_system_error(int error_code, StringRef message) FMT_NOEXCEPT; - -#ifdef _WIN32 - -/** A Windows error. */ -class WindowsError : public SystemError { - private: - void init(int error_code, StringRef format_str, ArgList args); - - public: - /** - \rst - Constructs a :class:`fmt::WindowsError` object with the description - of the form - - .. parsed-literal:: - **: ** - - where ** is the formatted message and ** is the system - message corresponding to the error code. - *error_code* is a Windows error code as given by ``GetLastError``. - If *error_code* is not a valid error code such as -1, the system message - will look like "error -1". - - **Example**:: - - // This throws a WindowsError with the description - // cannot open file 'madeup': The system cannot find the file specified. - // or similar (system message may vary). - const char *filename = "madeup"; - LPOFSTRUCT of = LPOFSTRUCT(); - HFILE file = OpenFile(filename, &of, OF_READ); - if (file == HFILE_ERROR) - throw fmt::WindowsError(GetLastError(), "cannot open file '{}'", filename); - \endrst - */ - WindowsError(int error_code, StringRef message) { - init(error_code, message, ArgList()); - } - FMT_VARIADIC_CTOR(WindowsError, init, int, StringRef) -}; - -// Reports a Windows error without throwing an exception. -// Can be used to report errors from destructors. -void report_windows_error(int error_code, StringRef message) FMT_NOEXCEPT; - -#endif - -enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE }; - -/** - Formats a string and prints it to stdout using ANSI escape sequences - to specify color (experimental). - Example: - PrintColored(fmt::RED, "Elapsed time: {0:.2f} seconds") << 1.23; - */ -void print_colored(Color c, StringRef format, ArgList args); - -/** - \rst - Formats arguments and returns the result as a string. - - **Example**:: - - std::string message = format("The answer is {}", 42); - \endrst -*/ -inline std::string format(StringRef format_str, ArgList args) { - MemoryWriter w; - w.write(format_str, args); - return w.str(); -} - -inline std::wstring format(WStringRef format_str, ArgList args) { - WMemoryWriter w; - w.write(format_str, args); - return w.str(); -} - -/** - \rst - Prints formatted data to the file *f*. - - **Example**:: - - print(stderr, "Don't {}!", "panic"); - \endrst - */ -void print(std::FILE *f, StringRef format_str, ArgList args); - -/** - \rst - Prints formatted data to ``stdout``. - - **Example**:: - - print("Elapsed time: {0:.2f} seconds", 1.23); - \endrst - */ -void print(StringRef format_str, ArgList args); - -/** - \rst - Prints formatted data to the stream *os*. - - **Example**:: - - print(cerr, "Don't {}!", "panic"); - \endrst - */ -void print(std::ostream &os, StringRef format_str, ArgList args); - -template -void printf(BasicWriter &w, BasicStringRef format, ArgList args) { - internal::PrintfFormatter().format(w, format, args); -} - -/** - \rst - Formats arguments and returns the result as a string. - - **Example**:: - - std::string message = fmt::sprintf("The answer is %d", 42); - \endrst -*/ -inline std::string sprintf(StringRef format, ArgList args) { - MemoryWriter w; - printf(w, format, args); - return w.str(); -} - -/** - \rst - Prints formatted data to the file *f*. - - **Example**:: - - fmt::fprintf(stderr, "Don't %s!", "panic"); - \endrst - */ -int fprintf(std::FILE *f, StringRef format, ArgList args); - -/** - \rst - Prints formatted data to ``stdout``. - - **Example**:: - - fmt::printf("Elapsed time: %.2f seconds", 1.23); - \endrst - */ -inline int printf(StringRef format, ArgList args) { - return fprintf(stdout, format, args); -} - -/** - Fast integer formatter. - */ -class FormatInt { - private: - // Buffer should be large enough to hold all digits (digits10 + 1), - // a sign and a null character. - enum {BUFFER_SIZE = std::numeric_limits::digits10 + 3}; - mutable char buffer_[BUFFER_SIZE]; - char *str_; - - // Formats value in reverse and returns the number of digits. - char *format_decimal(ULongLong value) { - char *buffer_end = buffer_ + BUFFER_SIZE - 1; - while (value >= 100) { - // Integer division is slow so do it for a group of two digits instead - // of for every digit. The idea comes from the talk by Alexandrescu - // "Three Optimization Tips for C++". See speed-test for a comparison. - unsigned index = (value % 100) * 2; - value /= 100; - *--buffer_end = internal::Data::DIGITS[index + 1]; - *--buffer_end = internal::Data::DIGITS[index]; - } - if (value < 10) { - *--buffer_end = static_cast('0' + value); - return buffer_end; - } - unsigned index = static_cast(value * 2); - *--buffer_end = internal::Data::DIGITS[index + 1]; - *--buffer_end = internal::Data::DIGITS[index]; - return buffer_end; - } - - void FormatSigned(LongLong value) { - ULongLong abs_value = static_cast(value); - bool negative = value < 0; - if (negative) - abs_value = 0 - abs_value; - str_ = format_decimal(abs_value); - if (negative) - *--str_ = '-'; - } - - public: - explicit FormatInt(int value) { FormatSigned(value); } - explicit FormatInt(long value) { FormatSigned(value); } - explicit FormatInt(LongLong value) { FormatSigned(value); } - explicit FormatInt(unsigned value) : str_(format_decimal(value)) {} - explicit FormatInt(unsigned long value) : str_(format_decimal(value)) {} - explicit FormatInt(ULongLong value) : str_(format_decimal(value)) {} - - /** - Returns the number of characters written to the output buffer. - */ - std::size_t size() const { return buffer_ - str_ + BUFFER_SIZE - 1; } - - /** - Returns a pointer to the output buffer content. No terminating null - character is appended. - */ - const char *data() const { return str_; } - - /** - Returns a pointer to the output buffer content with terminating null - character appended. - */ - const char *c_str() const { - buffer_[BUFFER_SIZE - 1] = '\0'; - return str_; - } - - /** - Returns the content of the output buffer as an `std::string`. - */ - std::string str() const { return std::string(str_, size()); } -}; - -// Formats a decimal integer value writing into buffer and returns -// a pointer to the end of the formatted string. This function doesn't -// write a terminating null character. -template -inline void format_decimal(char *&buffer, T value) { - typename internal::IntTraits::MainType abs_value = value; - if (internal::is_negative(value)) { - *buffer++ = '-'; - abs_value = 0 - abs_value; - } - if (abs_value < 100) { - if (abs_value < 10) { - *buffer++ = static_cast('0' + abs_value); - return; - } - unsigned index = static_cast(abs_value * 2); - *buffer++ = internal::Data::DIGITS[index]; - *buffer++ = internal::Data::DIGITS[index + 1]; - return; - } - unsigned num_digits = internal::count_digits(abs_value); - internal::format_decimal(buffer, abs_value, num_digits); - buffer += num_digits; -} -} - -#if FMT_GCC_VERSION -// Use the system_header pragma to suppress warnings about variadic macros -// because suppressing -Wvariadic-macros with the diagnostic pragma doesn't -// work. It is used at the end because we want to suppress as little warnings -// as possible. -# pragma GCC system_header -#endif - -// This is used to work around VC++ bugs in handling variadic macros. -#define FMT_EXPAND(args) args - -// Returns the number of arguments. -// Based on https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s. -#define FMT_NARG(...) FMT_NARG_(__VA_ARGS__, FMT_RSEQ_N()) -#define FMT_NARG_(...) FMT_EXPAND(FMT_ARG_N(__VA_ARGS__)) -#define FMT_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N -#define FMT_RSEQ_N() 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 - -#define FMT_CONCAT(a, b) a##b -#define FMT_FOR_EACH_(N, f, ...) \ - FMT_EXPAND(FMT_CONCAT(FMT_FOR_EACH, N)(f, __VA_ARGS__)) -#define FMT_FOR_EACH(f, ...) \ - FMT_EXPAND(FMT_FOR_EACH_(FMT_NARG(__VA_ARGS__), f, __VA_ARGS__)) - -#define FMT_ADD_ARG_NAME(type, index) type arg##index -#define FMT_GET_ARG_NAME(type, index) arg##index - -#if FMT_USE_VARIADIC_TEMPLATES -# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ - template \ - ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ - const Args & ... args) { \ - using fmt::internal::Value; \ - const Value values[fmt::internal::NonZero::VALUE] = { \ - fmt::internal::MakeValue(args)... \ - }; \ - call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \ - fmt::internal::make_type(args...), values)); \ - } -#else -// Defines a wrapper for a function taking __VA_ARGS__ arguments -// and n additional arguments of arbitrary types. -# define FMT_WRAP(Char, ReturnType, func, call, n, ...) \ - template \ - inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ - FMT_GEN(n, FMT_MAKE_ARG)) { \ - const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF_##Char)}; \ - call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \ - fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), vals)); \ - } - -# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ - inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__)) { \ - call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList()); \ - } \ - FMT_WRAP(Char, ReturnType, func, call, 1, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 2, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 3, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 4, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 5, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 6, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 7, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 8, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 9, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 10, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 11, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 12, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 13, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 14, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 15, __VA_ARGS__) -#endif // FMT_USE_VARIADIC_TEMPLATES - -/** - \rst - Defines a variadic function with the specified return type, function name - and argument types passed as variable arguments to this macro. - - **Example**:: - - void print_error(const char *file, int line, const char *format, - fmt::ArgList args) { - fmt::print("{}: {}: ", file, line); - fmt::print(format, args); - } - FMT_VARIADIC(void, print_error, const char *, int, const char *) - - ``FMT_VARIADIC`` is used for compatibility with legacy C++ compilers that - don't implement variadic templates. You don't have to use this macro if - you don't need legacy compiler support and can use variadic templates - directly:: - - template - void print_error(const char *file, int line, const char *format, - const Args & ... args) { - fmt::print("{}: {}: ", file, line); - fmt::print(format, args...); - } - \endrst - */ -#define FMT_VARIADIC(ReturnType, func, ...) \ - FMT_VARIADIC_(char, ReturnType, func, return func, __VA_ARGS__) - -#define FMT_VARIADIC_W(ReturnType, func, ...) \ - FMT_VARIADIC_(wchar_t, ReturnType, func, return func, __VA_ARGS__) - -namespace fmt { -FMT_VARIADIC(std::string, format, StringRef) -FMT_VARIADIC_W(std::wstring, format, WStringRef) -FMT_VARIADIC(void, print, StringRef) -FMT_VARIADIC(void, print, std::FILE *, StringRef) -FMT_VARIADIC(void, print, std::ostream &, StringRef) -FMT_VARIADIC(void, print_colored, Color, StringRef) -FMT_VARIADIC(std::string, sprintf, StringRef) -FMT_VARIADIC(int, printf, StringRef) -FMT_VARIADIC(int, fprintf, std::FILE *, StringRef) -} - -// Restore warnings. -#if FMT_GCC_VERSION >= 406 -# pragma GCC diagnostic pop -#endif - -#ifdef __clang__ -# pragma clang diagnostic pop -#endif - -#ifdef FMT_HEADER_ONLY -# include "format.cc" -#endif - -#endif // FMT_FORMAT_H_ diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.h deleted file mode 100644 index 5602141cb8..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.h +++ /dev/null @@ -1,136 +0,0 @@ -#ifndef BOX_H -#define BOX_H - -#include -#include - -#include "grid.h" -#include "vertices.h" - -namespace grid -{ - -namespace ba = boost::adaptors; - -template -class Box -{ - public: - typedef GridRef GridProxy; - typedef typename GridProxy::Vertex Position; - - struct InternalTest; - struct BoundaryTest; - struct BoundsTest; - struct PositionToVertex; - - class FreudenthalLinkIterator; - typedef boost::iterator_range FreudenthalLinkRange; - - typedef VerticesIterator VI; - typedef boost::transformed_range - > VertexRange; - - // Topology interface - typedef typename GridProxy::Index Vertex; - typedef boost::transformed_range - > Link; - - - Box(): g_(0, Position()) {} - Box(const Position& shape): - g_(0, shape), to_(shape - Position::one()) {} - Box(const Position& shape, - const Position& from, - const Position& to): - g_(0, shape), from_(from), to_(to) {} - - - const Position& from() const { return from_; } - const Position& to() const { return to_; } - Position& from() { return from_; } - Position& to() { return to_; } - Position shape() const { return to_ - from_ + Position::one(); } - const Position& grid_shape() const { return g_.shape(); } - static unsigned dimension() { return D; } - - size_t size() const { size_t c = 1; for (unsigned i = 0; i < D; ++i) c *= (to_[i] - from_[i] + 1); return c; } - - VertexRange vertices() const { return boost::iterator_range(VI::begin(from_, to_), VI::end(from_, to_)) - | ba::transformed(position_to_vertex()); } - Link link(const Position& p) const { return FreudenthalLinkRange(FreudenthalLinkIterator::begin(p), FreudenthalLinkIterator::end(p)) - | ba::filtered(bounds_test()) - | ba::transformed(position_to_vertex()); } - Link link(const Vertex& v) const { return link(position(v)); } - - Box intersect(const Box& other) const; - bool intersects(const Box& other) const; - void merge(const Box& other); - - bool contains(const Position& p) const; - bool contains(const Vertex& v) const { return contains(position(v)); } - - bool boundary(const Position& p, bool degenerate = false) const; - bool boundary(const Vertex& v, bool deg = false) const { return boundary(position(v), deg); } - Box side(unsigned axis, bool upper) const; - - InternalTest internal_test() const { return InternalTest(*this); } - BoundaryTest boundary_test() const { return BoundaryTest(*this); } - BoundsTest bounds_test() const { return BoundsTest(*this); } - PositionToVertex position_to_vertex() const { return PositionToVertex(*this); } - - - void swap(Box& other) { g_.swap(other.g_); std::swap(from_, other.from_); std::swap(to_, other.to_); } - - bool operator==(const Box& other) const { return from_ == other.from_ && to_ == other.to_; } - - template - friend std::basic_ostream& - operator<<(std::basic_ostream& out, const Box& b) { out << "Box: " << b.from_ << " - " << b.to_ << " inside " << b.g_.shape(); return out; } - - struct InternalTest - { - InternalTest(const Box& box): box_(box) {} - bool operator()(const Vertex& v) const { return !box_.boundary(v); } - const Box& box_; - }; - - struct BoundaryTest - { - BoundaryTest(const Box& box): box_(box) {} - bool operator()(const Vertex& v) const { return box_.boundary(v); } - const Box& box_; - }; - - struct BoundsTest - { - BoundsTest(const Box& box): box_(box) {} - bool operator()(const Position& p) const { return box_.contains(p); } - bool operator()(const Vertex& v) const { return box_.contains(v); } - const Box& box_; - }; - - struct PositionToVertex - { - typedef Vertex result_type; - PositionToVertex(const Box& box): box_(box) {} - Vertex operator()(Position p) const { for (unsigned i = 0; i < D; ++i) p[i] %= box_.grid_shape()[i]; return box_.g_.index(p); } - const Box& box_; - }; - - // computes position inside the box (adjusted for the wrap-around, if need be) - Position position(const Vertex& v) const { Position p = g_.vertex(v); for (unsigned i = 0; i < D; ++i) if (p[i] < from()[i]) p[i] += grid_shape()[i]; return p; } - - private: - GridProxy g_; - Position from_, to_; -}; - -} - -#include "box.hpp" - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.hpp deleted file mode 100644 index f3af50c047..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.hpp +++ /dev/null @@ -1,141 +0,0 @@ -template -grid::Box -grid::Box:: -intersect(const Box& other) const -{ - Position from, to; - for (unsigned i = 0; i < D; ++i) - { - from[i] = std::max(from_[i], other.from_[i]); - to[i] = std::min(to_[i], other.to_[i]); - } - - return Box(g_, from, to); -} - -template -bool -grid::Box:: -intersects(const Box& other) const -{ - for (unsigned i = 0; i < D; ++i) - if (std::max(from_[i], other.from_[i]) > std::min(to_[i], other.to_[i])) - return false; - - return true; -} - -template -bool -grid::Box:: -contains(const Position& p) const -{ - for (unsigned i = 0; i < D; ++i) - if (p[i] > to_[i] || p[i] < from_[i]) - return false; - return true; -} - -template -bool -grid::Box:: -boundary(const Position& p, bool degenerate) const -{ - for (unsigned i = 0; i < D; ++i) - { - if (degenerate && from_[i] == to_[i]) continue; - if (p[i] == from_[i] || p[i] == to_[i]) - return true; - } - - return false; -} - -template -grid::Box -grid::Box:: -side(unsigned axis, bool upper) const -{ - Box res(*this); - - if (upper) - res.from()[axis] = res.to()[axis]; - else - res.to()[axis] = res.from()[axis]; - - return res; -} - -template -void -grid::Box:: -merge(const Box& other) -{ - for (unsigned i = 0; i < D; ++i) - { - from_[i] = std::min(from_[i], other.from_[i]); - to_[i] = std::max(to_[i], other.to_[i]); - } -} - -/* Box::FreudenthalLinkIterator */ -template -class grid::Box::FreudenthalLinkIterator: - public boost::iterator_facade -{ - typedef boost::iterator_facade Parent; - - - public: - typedef typename Parent::value_type value_type; - typedef typename Parent::difference_type difference_type; - typedef typename Parent::reference reference; - - FreudenthalLinkIterator(): loc_(0), dir_(0) {} - FreudenthalLinkIterator(const Position& p, int loc = 0, int dir = 1): - p_(p), v_(p), loc_(loc), dir_(dir) {} - - static FreudenthalLinkIterator - begin(const Position& p) { FreudenthalLinkIterator it(p); ++it; return it; } - static FreudenthalLinkIterator - end(const Position& p) { return FreudenthalLinkIterator(p, 0, -1); } - - private: - void increment(); - bool equal(const FreudenthalLinkIterator& other) const { return v_ == other.v_; } - reference dereference() const { return v_; } - - friend class ::boost::iterator_core_access; - - private: - Position p_, v_; - int loc_; - int dir_; -}; - -template -void -grid::Box::FreudenthalLinkIterator:: -increment() -{ - loc_ += dir_; - if (loc_ == (1 << D)) - { - dir_ = -1; - loc_ += dir_; - } - - for (unsigned i = 0; i < D; ++i) - if (loc_ & (1 << i)) - v_[i] = p_[i] + dir_; - else - v_[i] = p_[i]; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/grid.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/grid.h deleted file mode 100644 index c63fc7c598..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/grid.h +++ /dev/null @@ -1,143 +0,0 @@ -#ifndef GRID_H -#define GRID_H - -#include "point.h" - -namespace grid -{ - -template -struct Grid; - -template -struct GridRef -{ - public: - typedef C Value; - - typedef Point Vertex; - typedef size_t Index; - - public: - template - GridRef(C* data, const Point& shape, bool c_order = true): - data_(data), shape_(shape), c_order_(c_order) { set_stride(); } - - GridRef(Grid& g): - data_(g.data()), shape_(g.shape()), - c_order_(g.c_order()) { set_stride(); } - - template - C operator()(const Point& v) const { return data_[v*stride_]; } - - template - C& operator()(const Point& v) { return data_[v*stride_]; } - - C operator()(Index i) const { return data_[i]; } - C& operator()(Index i) { return data_[i]; } - - const Vertex& - shape() const { return shape_; } - - const C* - data() const { return data_; } - C* data() { return data_; } - - // Set every element to the given value - GridRef& operator=(C value) { Index s = size(); for (Index i = 0; i < s; ++i) data_[i] = value; return *this; } - GridRef& operator/=(C value) { Index s = size(); for (Index i = 0; i < s; ++i) data_[i] /= value; return *this; } - - Vertex vertex(Index idx) const { Vertex v; for (unsigned i = 0; i < D; ++i) { v[i] = idx / stride_[i]; idx %= stride_[i]; } return v; } - Index index(const Vertex& v) const { return v*stride_; } - - Index size() const { return size(shape()); } - void swap(GridRef& other) { std::swap(data_, other.data_); std::swap(shape_, other.shape_); std::swap(stride_, other.stride_); } - - bool c_order() const { return c_order_; } - - protected: - static Index - size(const Vertex& v) { Index res = 1; for (unsigned i = 0; i < D; ++i) res *= v[i]; return res; } - - void set_stride() - { - Index cur = 1; - if (c_order_) - for (unsigned i = D; i > 0; --i) { stride_[i-1] = cur; cur *= shape_[i-1]; } - else - for (unsigned i = 0; i < D; ++i) { stride_[i] = cur; cur *= shape_[i]; } - - } - void set_shape(const Vertex& v) { shape_ = v; set_stride(); } - void set_data(C* data) { data_ = data; } - void set_c_order(bool order) { c_order_ = order; } - - private: - C* data_; - Vertex shape_; - Vertex stride_; - bool c_order_; -}; - - -template -struct Grid: public GridRef -{ - public: - typedef GridRef Parent; - typedef typename Parent::Value Value; - typedef typename Parent::Index Index; - typedef Parent Reference; - - template - struct rebind { typedef Grid type; }; - - public: - template - Grid(const Point& shape, bool c_order = true): - Parent(new C[size(shape)], shape, c_order) - {} - - Grid(const Parent& g): - Parent(new C[size(g.shape())], g.shape(), - g.c_order()) { copy_data(g.data()); } - - template - Grid(const OtherGrid& g): - Parent(new C[size(g.shape())], - g.shape(), - g.c_order()) { copy_data(g.data()); } - - ~Grid() { delete[] Parent::data(); } - - template - Grid& operator=(const GridRef& other) - { - delete[] Parent::data(); - Parent::set_c_order(other.c_order()); // NB: order needs to be set before the shape, to set the stride correctly - Parent::set_shape(other.shape()); - Index s = size(shape()); - Parent::set_data(new C[s]); - copy_data(other.data()); - return *this; - } - - using Parent::data; - using Parent::shape; - using Parent::operator(); - using Parent::operator=; - using Parent::size; - - private: - template - void copy_data(const OC* data) - { - Index s = size(shape()); - for (Index i = 0; i < s; ++i) - Parent::data()[i] = data[i]; - } -}; - -} - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/point.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/point.h deleted file mode 100644 index 0e867c34aa..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/point.h +++ /dev/null @@ -1,132 +0,0 @@ -#ifndef POINT_H -#define POINT_H - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -namespace grid -{ - -template -class Point: public boost::array, - private boost::addable< Point // Point + Point - , boost::subtractable< Point // Point - Point - , boost::dividable2< Point, Coordinate_ // Point / Coordinate - , boost::multipliable2< Point, Coordinate_ // Point * Coordinate, Coordinate * Point - > > > > -{ - public: - typedef Coordinate_ Coordinate; - typedef boost::array ArrayParent; - - typedef Point LPoint; - typedef Point UPoint; - - template - struct rebind { typedef Point type; }; - - public: - Point() { for (unsigned i = 0; i < D; ++i) (*this)[i] = 0; } - Point(const ArrayParent& a): - ArrayParent(a) {} - template Point(const Point& p) { for (size_t i = 0; i < D; ++i) (*this)[i] = p[i]; } - template Point(const T* a) { for (unsigned i = 0; i < D; ++i) (*this)[i] = a[i]; } - template Point(const std::vector& a) { for (unsigned i = 0; i < D; ++i) (*this)[i] = a[i]; } - - static - unsigned dimension() { return D; } - - static Point zero() { return Point(); } - static Point one() { Point p; for (unsigned i = 0; i < D; ++i) p[i] = 1; return p; } - - LPoint drop(int dim) const { LPoint p; unsigned c = 0; for (unsigned i = 0; i < D; ++i) { if (i == dim) continue; p[c++] = (*this)[i]; } return p; } - UPoint lift(int dim, Coordinate x) const { UPoint p; for (unsigned i = 0; i < D+1; ++i) { if (i < dim) p[i] = (*this)[i]; else if (i == dim) p[i] = x; else if (i > dim) p[i] = (*this)[i-1]; } return p; } - - using ArrayParent::operator[]; - - Point& operator+=(const Point& y) { for (unsigned i = 0; i < D; ++i) (*this)[i] += y[i]; return *this; } - Point& operator-=(const Point& y) { for (unsigned i = 0; i < D; ++i) (*this)[i] -= y[i]; return *this; } - Point& operator*=(Coordinate a) { for (unsigned i = 0; i < D; ++i) (*this)[i] *= a; return *this; } - Point& operator/=(Coordinate a) { for (unsigned i = 0; i < D; ++i) (*this)[i] /= a; return *this; } - - Point operator-() const { Point res; for (unsigned i = 0; i < D; ++i) res[i] = -(*this)[i]; return res; } - - Coordinate norm() const { return (*this)*(*this); } - - std::ostream& operator<<(std::ostream& out) const { out << (*this)[0]; for (unsigned i = 1; i < D; ++i) out << " " << (*this)[i]; return out; } - std::istream& operator>>(std::istream& in); - - friend - Coordinate operator*(const Point& x, const Point& y) { Coordinate n = 0; for (size_t i = 0; i < D; ++i) n += x[i] * y[i]; return n; } - - template - friend - Coordinate operator*(const Point& x, const Point& y) { Coordinate n = 0; for (size_t i = 0; i < D; ++i) n += x[i] * y[i]; return n; } - - private: - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) { ar & boost::serialization::base_object(*this); } -}; - -template -std::istream& -Point:: -operator>>(std::istream& in) -{ - std::string point_str; - in >> point_str; // read until ' ' - std::stringstream ps(point_str); - - char x; - for (unsigned i = 0; i < dimension(); ++i) - { - ps >> (*this)[i]; - ps >> x; - } - - return in; -} - - -template -Coordinate norm2(const Point& p) -{ Coordinate res = 0; for (unsigned i = 0; i < D; ++i) res += p[i]*p[i]; return res; } - -template -std::ostream& -operator<<(std::ostream& out, const Point& p) -{ return p.operator<<(out); } - -template -std::istream& -operator>>(std::istream& in, Point& p) -{ return p.operator>>(in); } - -} - -namespace opts -{ - template - struct Traits; - - template - struct Traits< grid::Point > - { - static - std::string type_string() { return "POINT"; } - }; -} - - -#endif // POINT_H diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/vertices.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/vertices.h deleted file mode 100644 index 339782e8c4..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/vertices.h +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef VERTICES_H -#define VERTICES_H - -#include - -namespace grid -{ - -template -class VerticesIterator: - public boost::iterator_facade, - Vertex_, - boost::forward_traversal_tag, - Vertex_, - std::ptrdiff_t> -{ - typedef boost::iterator_facade Parent; - - - public: - typedef typename Parent::value_type value_type; - typedef typename Parent::difference_type difference_type; - typedef typename Parent::reference reference; - - typedef value_type Vertex; - typedef typename Vertex::Coordinate Coordinate; - - // upper bounds are non-inclusive - VerticesIterator(const Vertex& bounds): - to_(bounds - Vertex::one()) {} - - VerticesIterator(const Vertex& pos, - const Vertex& bounds): - pos_(pos), to_(bounds - Vertex::one()) {} - - VerticesIterator(const Vertex& pos, - const Vertex& from, - const Vertex& to): - pos_(pos), from_(from), - to_(to) {} - - - static VerticesIterator - begin(const Vertex& bounds) { return VerticesIterator(bounds); } - static VerticesIterator - end(const Vertex& bounds) { Vertex e; e[0] = bounds[0]; return VerticesIterator(e, bounds); } - - static VerticesIterator - begin(const Vertex& from, const Vertex& to) { return VerticesIterator(from, from, to); } - static VerticesIterator - end(const Vertex& from, const Vertex& to) { Vertex e = from; e[0] = to[0] + 1; return VerticesIterator(e, from, to); } - - private: - void increment(); - bool equal(const VerticesIterator& other) const { return pos_ == other.pos_; } - reference dereference() const { return pos_; } - - friend class ::boost::iterator_core_access; - - private: - Vertex pos_; - Vertex from_; - Vertex to_; -}; - -} - -template -void -grid::VerticesIterator:: -increment() -{ - unsigned j = Vertex::dimension() - 1; - while (j > 0 && pos_[j] == to_[j]) - { - pos_[j] = from_[j]; - --j; - } - ++pos_[j]; -} - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/matrix-filtration.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/matrix-filtration.h deleted file mode 100644 index 516f27b2d1..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/matrix-filtration.h +++ /dev/null @@ -1,120 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace dionysus -{ - - -template -class MatrixFiltrationCell; - - -// adapt Matrix as a Filtration to make it possible to feed into reduction algorithms -template -class MatrixFiltration -{ - public: - using Matrix = Matrix_; - using CellValue = CellValue_; - using Dimensions = std::vector; - using Values = std::vector; - using Cell = MatrixFiltrationCell; - - - public: - MatrixFiltration(Matrix m, Dimensions dimensions, Values values): - m_(std::move(m)), - dimensions_(dimensions), - values_(values) { assert(m_->size() == dimensions_.size()); assert(m_->size() == values_.size()); } - - Cell operator[](size_t i) const { return Cell(this, i); } - size_t size() const { return m_.size(); } - - size_t index(const Cell& c) const; - - Cell begin() const { return Cell(this, 0); } - Cell end() const { return Cell(this, size()); } - - const Dimensions& dimensions() const { return dimensions_; } - const Values& values() const { return values_; } - - private: - Matrix m_; - Dimensions dimensions_; - Values values_; - - friend class MatrixFiltrationCell; -}; - - -template -class MatrixFiltrationCell -{ - public: - using MatrixFiltration = MatrixFiltration_; - using Matrix = typename MatrixFiltration::Matrix; - using Data = typename MatrixFiltration::CellValue; - using Field = typename Matrix::Field; - - template - using Entry = ChainEntry; - - template - using BoundaryChain = std::vector>; - - public: - MatrixFiltrationCell(const MatrixFiltration* mf, size_t i): - mf_(mf), i_(i) {} - - short unsigned dimension() const { return mf_->dimensions_[i_]; } - const Data& data() const { return mf_->values_[i_]; } - - bool operator==(const MatrixFiltrationCell& other) const { return i_ == other.i_; } - bool operator!=(const MatrixFiltrationCell& other) const { return i_ != other.i_; } - - BoundaryChain<> boundary() const - { - BoundaryChain<> bdry; - for (auto& entry : (mf_->m_)[i_]) - bdry.emplace_back(Entry<> { entry.e, MatrixFiltrationCell(mf_, entry.i) }); - return bdry; - } - - template - BoundaryChain boundary(const Field_& field) const - { - BoundaryChain bdry; - for (auto& entry : (mf_->m_)[i_]) - bdry.emplace_back(Entry { field.init(entry.e), MatrixFiltrationCell(mf_, entry.i) }); - return bdry; - } - - // iterator interface - MatrixFiltrationCell operator++(int) { MatrixFiltrationCell copy = *this; i_++; return copy; } - MatrixFiltrationCell& operator++() { ++i_; return *this; } - - const MatrixFiltrationCell& operator*() const { return *this; } - MatrixFiltrationCell& operator*() { return *this; } - - size_t i() const { return i_; } - - friend - std::ostream& operator<<(std::ostream& out, const MatrixFiltrationCell& c) - { out << c.i_; return out; } - - private: - const MatrixFiltration* mf_ = nullptr; - size_t i_; -}; - -template -size_t -MatrixFiltration::index(const Cell& c) const -{ - return c.i(); -} - -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.h deleted file mode 100644 index 3d83d4ae44..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.h +++ /dev/null @@ -1,145 +0,0 @@ -#ifndef DIONYSUS_OMNI_FIELD_REDUCTION_H -#define DIONYSUS_OMNI_FIELD_REDUCTION_H - -#include -#include - -#include "reduction.h" // for unpaired -#include "fields/q.h" -#include "fields/zp.h" -#include "chain.h" - -namespace dionysus -{ - -template, class Q_ = ::dionysus::Q<>, class Zp_ = ::dionysus::ZpField> -class OmniFieldPersistence -{ - public: - using Index = Index_; - using Q = Q_; - using Field = Q; - using Comparison = Comparison_; - - using BaseElement = typename Q::BaseElement; - using Zp = Zp_; - using Zps = std::unordered_map; - - using QElement = typename Q::Element; - using QEntry = ChainEntry; - using QChain = std::vector; - - using ZpElement = typename Zp::Element; - using ZpEntry = ChainEntry; - using ZpChain = std::vector; - - using QChains = std::vector; - using ZpChains = std::unordered_map>; - - using QLows = std::unordered_map; - using ZpLows = std::unordered_map>; - - using QPairs = std::vector; - using ZpPairs = std::unordered_map>; - - using Factors = std::vector; - - using Specials = std::unordered_map>; - - const Field& field() const { return q_; } - - void sort(QChain& c) { std::sort(c.begin(), c.end(), - [this](const QEntry& e1, const QEntry& e2) - { return this->cmp_(e1.index(), e2.index()); }); } - - template - void add(const ChainRange& chain) { return add(QChain(std::begin(chain), std::end(chain))); } - void add(QChain&& chain); - - void reserve(size_t s) { q_chains_.reserve(s); q_pairs_.reserve(s); } - size_t size() const { return q_pairs_.size(); } - - void reduce(ZpChain& zp_chain, BaseElement p); - ZpChain convert(const QChain& c, const Zp& field) const; - bool special(Index i, BaseElement p) const { auto it = zp_chains_.find(i); if (it == zp_chains_.end()) return false; if (it->second.find(p) == it->second.end()) return false; return true; } - Specials specials() const - { - Specials specials; - for (auto& x : zp_chains_) - for (auto& y : x.second) - specials[x.first].push_back(y.first); - return specials; - } - - const Zp& zp(BaseElement p) const { auto it = zps_.find(p); if (it != zps_.end()) return it->second; return zps_.emplace(p, Zp(p)).first->second; } - - static Factors factor(BaseElement x); - - const QChains& q_chains() const { return q_chains_; } - const ZpChains& zp_chains() const { return zp_chains_; } - - // This is a bit of a hack; it takes advantage of the fact that zp(p) - // generates field on-demand and memoizes them. So there is an entry in - // zps_ only if something special happened over the prime. - Factors primes() const { Factors result; result.reserve(zps_.size()); for (auto& x : zps_) result.push_back(x.first); return result; } - - // TODO: no skip support for now - bool skip(Index) const { return false; } - void add_skip() {} - void set_skip(Index, bool flag = true) {} - - Index pair(Index i, BaseElement p) const; - void set_pair(Index i, Index j); - void set_pair(Index i, Index j, BaseElement p); - static const Index unpaired() { return Reduction::unpaired; } - - private: - QChains q_chains_; - ZpChains zp_chains_; - - QLows q_lows_; - ZpLows zp_lows_; - - QPairs q_pairs_; - ZpPairs zp_pairs_; - - Q q_; - mutable Zps zps_; - - Comparison cmp_; -}; - -// Make OmniFieldPersistence act like a ReducedMatrix (e.g., for the purpose of constructing a persistence diagram) -template -struct PrimeAdapter -{ - using Persistence = OmniFieldPersistence; - using Prime = typename Persistence::BaseElement; - using Index = typename Persistence::Index; - - PrimeAdapter(const Persistence& persistence, Prime p): - persistence_(persistence), p_(p) {} - - bool skip(Index i) const { return persistence_.skip(i); } - - size_t size() const { return persistence_.size(); } - Index pair(Index i) const { return persistence_.pair(i, p_); } - static const Index unpaired() { return Persistence::unpaired(); } - - const Persistence& persistence_; - Prime p_; -}; - -template -PrimeAdapter -prime_adapter(const OmniFieldPersistence& persistence, - typename PrimeAdapter::Prime p) -{ - return PrimeAdapter(persistence, p); -} - -} // dionysus - -#include "omni-field-persistence.hpp" - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.hpp deleted file mode 100644 index 68d5fbede7..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.hpp +++ /dev/null @@ -1,250 +0,0 @@ -template -void -dionysus::OmniFieldPersistence:: -add(QChain&& chain) -{ - sort(chain); - - q_chains_.emplace_back(std::move(chain)); - q_pairs_.emplace_back(unpaired()); - Index i = q_chains_.size() - 1; - - QChain& c = q_chains_.back(); - - auto reduce = [this,&c,i](BaseElement p) - { - auto zp_chain = convert(c, zp(p)); - - this->reduce(zp_chain, p); - - if (!zp_chain.empty()) - { - auto l = zp_chain.back().index(); - zp_lows_[l].emplace(p,i); - set_pair(l,i,p); - } - - zp_chains_[i].emplace(p, std::move(zp_chain)); // empty chain is still a valid indicator that we don't need to bother with this field - }; - - // reduce - auto entry_cmp = [this](const QEntry& e1, const QEntry& e2) { return this->cmp_(e1.index(), e2.index()); }; - while (!c.empty()) - { - auto& low = c.back(); - - auto e = low.element(); - auto l = low.index(); - assert(!q_.is_zero(e)); - if (e != q_.id()) - { - auto factors = factor(q_.numerator(e)); - for (auto p : factors) - { - if (!special(i, p)) // there is already a dedicated column over p - reduce(p); - } - } - - auto it_zp = zp_lows_.find(l); - if (it_zp != zp_lows_.end()) - for (auto& x : it_zp->second) - { - auto p = x.first; - if (!special(i,p)) - reduce(p); - } - - auto it_q = q_lows_.find(l); - if (it_q != q_lows_.end()) - { - Index j = it_q->second; - - // add the primes from j to i - auto it_zp = zp_chains_.find(j); - if (it_zp != zp_chains_.end()) - for (auto& x : it_zp->second) - { - auto p = x.first; - if (!special(i,p)) - reduce(p); - } - - // reduce over Q - auto j_chain = q_chains_[j]; - auto j_e = j_chain.back().element(); - - auto m = q_.neg(q_.div(e,j_e)); - Chain::addto(c, m, j_chain, q_, entry_cmp); - assert(c.empty() || !q_.is_zero(c.back().element())); - } else - { - q_lows_.emplace(l,i); - set_pair(l,i); - break; - } - } -} - -template -void -dionysus::OmniFieldPersistence:: -reduce(ZpChain& zp_chain, BaseElement p) -{ - auto& field = zp(p); - - auto entry_cmp = [this](const ZpEntry& e1, const ZpEntry& e2) { return this->cmp_(e1.index(), e2.index()); }; - - while (!zp_chain.empty()) - { - auto& low = zp_chain.back(); - auto j = low.index(); - - auto it = zp_lows_.find(j); - if (it != zp_lows_.end()) - { - auto it2 = it->second.find(p); - if (it2 != it->second.end()) - { - const ZpChain& co = zp_chains_[it2->second][p]; - - auto m = field.neg(field.div(low.element(), co.back().element())); - assert(m < p); - Chain::addto(zp_chain, m, co, field, entry_cmp); - continue; - } - } - - auto qit = q_lows_.find(j); - if (qit == q_lows_.end() || special(qit->second, p)) // no valid pivot over Q - return; - - // TODO: this could be optimized (add and convert on the fly) - auto& q_chain = q_chains_[qit->second]; - assert(q_chain.empty() || !q_.is_zero(q_chain.back().element())); - - auto co = convert(q_chain, field); - auto m = field.neg(field.div(low.element(), co.back().element())); - Chain::addto(zp_chain, m, co, field, entry_cmp); - - assert(!zp_chain.empty() || zp_chain.back().index() != j); - } -} - -template -typename dionysus::OmniFieldPersistence::ZpChain -dionysus::OmniFieldPersistence:: -convert(const QChain& c, const Zp& field) const -{ - ZpChain result; - result.reserve(c.size()); - auto p = field.prime(); - for (auto& x : c) - { - auto num = q_.numerator(x.element()) % p; - if (num != 0) - { - while (num < 0) num += p; - auto denom = q_.denominator(x.element()) % p; - while (denom < 0) denom += p; - assert(denom % p != 0); - result.emplace_back(field.div(num, denom), x.index()); - } - } - return result; -} - - -template -typename dionysus::OmniFieldPersistence::Factors -dionysus::OmniFieldPersistence:: -factor(BaseElement x) -{ - if (x < 0) - x = -x; - Factors result; - - if (Q::is_prime(x)) - { - result.push_back(x); - return result; - } - - BaseElement p { 2 }; - while (p*p <= x) - { - if (x % p == 0) - { - result.push_back(p); - do { x /= p; } while (x % p == 0); - if (Q::is_prime(x)) - { - result.push_back(x); - break; - } - } - ++p; - } - if (x > 1) - result.push_back(x); - - return result; -} - -template -typename dionysus::OmniFieldPersistence::Index -dionysus::OmniFieldPersistence:: -pair(Index i, BaseElement p) const -{ - if (p == 1) - return q_pairs_[i]; - else - { - auto it = zp_pairs_.find(p); - if (it == zp_pairs_.end()) - return q_pairs_[i]; - else - { - auto pit = it->second.find(i); - if (pit == it->second.end()) - return q_pairs_[i]; - else - return pit->second; - } - } -} - -template -void -dionysus::OmniFieldPersistence:: -set_pair(Index i, Index j, BaseElement p) -{ - auto& pairs = zp_pairs_[p]; - pairs[i] = j; - pairs[j] = i; -} - -template -void -dionysus::OmniFieldPersistence:: -set_pair(Index i, Index j) -{ - q_pairs_[i] = j; - q_pairs_[j] = i; - - auto it = zp_chains_.find(j); - if (it == zp_chains_.end()) - return; - - auto& chains = it->second; - for (auto& x : chains) - { - auto p = x.first; - auto& chain = x.second; - if (chain.empty()) - { - zp_pairs_[p][j] = unpaired(); - zp_pairs_[p][i] = unpaired(); - } - } -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/opts/opts.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/opts/opts.h deleted file mode 100644 index 1a9bbf71bf..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/opts/opts.h +++ /dev/null @@ -1,499 +0,0 @@ -/** - * Author: Dmitriy Morozov - * The interface is heavily influenced by GetOptPP (https://code.google.com/p/getoptpp/). - * The parsing logic is from ProgramOptions.hxx (https://github.com/Fytch/ProgramOptions.hxx). - * - * History: - * - 2015-06-01: added Traits<...>::type_string() for long, unsigned long - * - ... - * - 2018-04-27: replace parsing logic with the one from ProgramOptions.hxx to - * make the parser compliant with [GNU Program Argument Syntax - * Conventions](https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html) - * - 2018-05-11: add dashed_non_option(), to accept arguments that are negative numbers - */ - -#ifndef OPTS_OPTS_H -#define OPTS_OPTS_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace opts { - -// Converters -template -struct Converter -{ - Converter() {} - static - bool convert(const std::string& val, T& res) - { - std::istringstream iss(val); - iss >> res; - return !iss.fail() && iss.eof(); - } -}; - -// Type -template -struct Traits -{ - static std::string type_string() { return "UNKNOWN TYPE"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "INT"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "SHORT INT"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "LONG"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "UNSIGNED INT"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "SHORT UNSIGNED INT"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "UNSIGNED LONG"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "FLOAT"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "DOUBLE"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "STRING"; } -}; - - -struct BasicOption -{ - using IsShort = std::function; - - BasicOption(char s_, - std::string l_, - std::string default_, - std::string type_, - std::string help_): - s(s_), l(l_), d(default_), t(type_), help(help_) {} - virtual ~BasicOption() {} - - int long_size() const { return l.size() + 1 + t.size(); } - - void output(std::ostream& out, int max_long) const - { - out << " "; - if (s) - out << '-' << s << ", "; - else - out << " "; - - out << "--" << l << ' '; - - if (!t.empty()) - out << t; - - for (int i = long_size(); i < max_long; ++i) - out << ' '; - - out << " " << help; - - if (!d.empty()) - { - out << " [default: " << d << "]"; - } - out << '\n'; - } - - virtual bool flag() const { return false; } - virtual bool parse(int argc, char** argv, int& i, int j, IsShort is_short); - virtual bool set(std::string arg) =0; - - char s; - std::string l; - std::string d; - std::string t; - std::string help; -}; - -// Option -template -struct OptionContainer: public BasicOption -{ - OptionContainer(char s_, - const std::string& l_, - T& var_, - const std::string& help_, - const std::string& type_ = Traits::type_string()): - BasicOption(s_, l_, default_value(var_), type_, help_), - var(&var_) {} - - static - std::string default_value(const T& def) - { - std::ostringstream oss; - oss << def; - return oss.str(); - } - - bool set(std::string s) override { return Converter::convert(s, *var); } - - T* var; -}; - -template<> -struct OptionContainer: public BasicOption -{ - OptionContainer(char s_, - const std::string& l_, - bool& var_, - const std::string& help_): - BasicOption(s_, l_, "", "", help_), - var(&var_) { *var = false; } - - bool parse(int, char**, int&, int, IsShort) override { *var = true; return true; } - bool set(std::string) override { return true; } - bool flag() const override { return true; } - - bool* var; -}; - -template -struct OptionContainer< std::vector >: public BasicOption -{ - OptionContainer(char s_, - const std::string& l_, - std::vector& var_, - const std::string& help_, - const std::string& type_ = "SEQUENCE"): - BasicOption(s_, l_, default_value(var_), type_, help_), - var(&var_), first(true) { } - - static - std::string default_value(const std::vector& def) - { - std::ostringstream oss; - oss << "("; - if (def.size()) - oss << def[0]; - for (size_t i = 1; i < def.size(); ++i) - oss << ", " << def[i]; - oss << ")"; - return oss.str(); - } - - bool set(std::string s) override - { - if (first) - { - var->clear(); - first = false; - } - - T x; - bool result = Converter::convert(s,x); - var->emplace_back(std::move(x)); - return result; - } - - std::vector* var; - mutable bool first; -}; - - -template -std::unique_ptr -Option(char s, const std::string& l, T& var, const std::string& help) { return std::unique_ptr{new OptionContainer(s, l, var, help)}; } - -template -std::unique_ptr -Option(char s, const std::string& l, T& var, - const std::string& type, const std::string& help) { return std::unique_ptr{new OptionContainer(s, l, var, help, type)}; } - -template -std::unique_ptr -Option(const std::string& l, T& var, const std::string& help) { return std::unique_ptr{new OptionContainer(0, l, var, help)}; } - -template -std::unique_ptr -Option(const std::string& l, T& var, - const std::string& type, const std::string& help) { return std::unique_ptr{new OptionContainer(0, l, var, help, type)}; } - -// PosOption -template -struct PosOptionContainer -{ - PosOptionContainer(T& var_): - var(&var_) {} - - bool parse(std::list& args) const - { - if (args.empty()) - return false; - - bool result = Converter::convert(args.front(), *var); - if (!result) - std::cerr << "error: failed to parse " << args.front() << '\n'; - args.pop_front(); - return result; - } - - T* var; -}; - -template -PosOptionContainer -PosOption(T& var) { return PosOptionContainer(var); } - - -// Options -struct Options -{ - Options(): - failed(false) {} - - inline - Options& operator>>(std::unique_ptr opt); - template - Options& operator>>(const PosOptionContainer& poc); - - operator bool() { return !failed; } - - - friend - std::ostream& - operator<<(std::ostream& out, const Options& ops) - { - int max_long = 0; - for (auto& cur : ops.options) - { - int cur_long = cur->long_size(); - if (cur_long > max_long) - max_long = cur_long; - } - - out << "Options:\n"; - for (auto& cur : ops.options) - cur->output(out, max_long); - - return out; - } - - bool parse(int argc, char** argv); - - void unrecognized_option(std::string arg) const - { - std::cerr << "error: unrecognized option " << arg << '\n'; - } - - static bool dashed_non_option(char* arg, BasicOption::IsShort is_short) - { - return arg[ 0 ] == '-' - && (std::isdigit(arg[ 1 ]) || arg[ 1 ] == '.') - && !is_short(arg[ 1 ]); - } - - private: - std::list args; - std::list> options; - bool failed; -}; - -bool -BasicOption::parse(int argc, char** argv, int& i, int j, IsShort is_short) -{ - char* argument; - char* cur_arg = argv[i]; - // -v... - if (argv[i][j] == '\0') - { - // -v data - if (i + 1 < argc && (argv[i+1][0] != '-' || Options::dashed_non_option(argv[i+1], is_short))) - { - ++i; - argument = argv[i]; - } else - { - std::cerr << "error: cannot find the argument; ignoring " << argv[i] << '\n'; - return false; - } - } else if (argv[i][j] == '=') - { - // -v=data - argument = &argv[i][j+1]; - } else if( j == 2 ) { // only for short options - // -vdata - argument = &argv[i][j]; - } else - { - std::cerr << "error: unexpected character \'" << argv[i][j] << "\' ignoring " << argv[i] << '\n'; - return false; - } - bool result = set(argument); - if (!result) - std::cerr << "error: failed to parse " << argument << " in " << cur_arg << '\n'; - return result; -} - -bool -Options::parse(int argc, char** argv) -{ - std::map short_opts; - std::map long_opts; - - for (auto& opt : options) - { - if (opt->s) - short_opts[opt->s] = opt.get(); - - long_opts[opt->l] = opt.get(); - } - - auto is_short = [&short_opts](char c) -> bool { return short_opts.find(c) != short_opts.end(); }; - - for (int i = 1; i < argc; ++i) - { - if( argv[ i ][ 0 ] == '\0' ) - continue; - if( argv[ i ][ 0 ] != '-' || dashed_non_option(argv[i], is_short)) - args.push_back(argv[i]); - else - { - // -... - if( argv[ i ][ 1 ] == '\0' ) - { - // - - args.push_back(argv[i]); - } else if( argv[ i ][ 1 ] == '-' ) - { - if( argv[ i ][ 2 ] == '\0' ) - { - // -- - while( ++i < argc ) - args.push_back(argv[i]); - } else { - // --... - char* first = &argv[ i ][ 2 ]; - char* last = first; - for(; *last != '=' && *last != '\0'; ++last); - if (first == last) - { - failed = true; - unrecognized_option(argv[i]); - } else - { - auto opt_it = long_opts.find(std::string{first,last}); - if (opt_it == long_opts.end()) - { - failed = true; - unrecognized_option(argv[i]); - } else - { - failed |= !opt_it->second->parse(argc, argv, i, last - argv[i], is_short); - } - } - } - } else - { - // -f... - auto opt_it = short_opts.find(argv[i][1]); - if (opt_it == short_opts.end()) - { - failed = true; - unrecognized_option(argv[i]); - } else if (opt_it->second->flag()) - { - opt_it->second->parse(argc, argv, i, 0, is_short); // arguments are meaningless; just sets the flag - - // -fgh - char c; - for(int j = 1; (c = argv[i][j]) != '\0'; ++j) - { - if (!std::isprint(c) || c == '-') - { - failed = true; - std::cerr << "error: invalid character\'" << c << " ignoring " << &argv[i][j] << '\n'; - break; - } - opt_it = short_opts.find(c); - if (opt_it == short_opts.end()) - { - failed = true; - unrecognized_option("-" + std::string(1, c)); - continue; - } - if (!opt_it->second->flag()) - { - failed = true; - std::cerr << "error: non-void options not allowed in option packs; ignoring " << c << '\n'; - continue; - } - opt_it->second->parse(argc, argv, i, 0, is_short); // arguments are meaningless; just sets the flag - } - } else - { - failed |= !opt_it->second->parse(argc, argv, i, 2, is_short); - } - } - } - } - - return !failed; -} - -Options& -Options::operator>>(std::unique_ptr opt) -{ - options.emplace_back(std::move(opt)); - return *this; -} - -template -Options& -Options::operator>>(const PosOptionContainer& poc) -{ - if (!failed) - failed = !poc.parse(args); - return *this; -} - -} - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/ordinary-persistence.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/ordinary-persistence.h deleted file mode 100644 index 5f26bd2a1b..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/ordinary-persistence.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef DIONYSUS_ORDINARY_PERSISTENCE_H -#define DIONYSUS_ORDINARY_PERSISTENCE_H - -#include "reduced-matrix.h" - -namespace dionysus -{ - -/* Move this into a ReducedMatrix class */ - -// Ordinary D -> R reduction -template, - template class... Visitors> -using OrdinaryPersistence = ReducedMatrix; - -// No negative optimization -template> -struct NoNegative -{ - template - struct Visitor: public EmptyVisitor - { - template - void chain_initialized(Self* matrix, Chain& c) - { - for (auto cur = std::begin(c); cur != std::end(c); ++cur) - { - Index i = cur->index(); - Index p = matrix->pair(i); - if (!(p == Self::unpaired() || (*matrix)[i].empty())) - c.erase(cur--); - } - } - }; - - template - using V2 = EmptyVisitor; -}; - -template, - template class... Visitors> -using OrdinaryPersistenceNoNegative = ReducedMatrix::template Visitor, - Visitors...>; - -// TODO: add clearing optimization (possibly bake it into the code itself) - -template, - template class... Visitors> -using FastPersistence = ReducedMatrix::template Visitor, - //Clearing::template Visitor, // FIXME - Visitors...>; - - -} - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/pair-recorder.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/pair-recorder.h deleted file mode 100644 index 81c066bda6..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/pair-recorder.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef DIONYSUS_PAIR_RECORDER_H -#define DIONYSUS_PAIR_RECORDER_H - -namespace dionysus -{ - -template -struct PairRecorder: public Persistence_ -{ - typedef Persistence_ Persistence; - typedef typename Persistence::Index Index; - - - using Persistence::Persistence; - - template - Index add(const ChainRange& chain) - { - Index p = Persistence::add(chain); - pairs_.push_back(p); - if (p != unpaired()) - pairs_[p] = pairs_.size() - 1; - - return p; - } - - Index pair(Index i) const { return pairs_[i]; } - - void resize(size_t s) { Persistence::resize(s); pairs_.resize(s, unpaired()); } - size_t size() const { return pairs_.size(); } - static const Index unpaired() { return Reduction::unpaired; } - - std::vector pairs_; -}; - -template -struct PairChainRecorder: public PairRecorder -{ - using Persistence = Persistence_; - using Parent = PairRecorder; - using Index = typename Persistence_::Index; - using Chain = typename Persistence_::Chain; - - using Parent::Parent; - - template - Index add(const ChainRange& chain) - { - auto p_chain = Persistence::add(chain, keep_cocycles); - Index p = std::get<0>(p_chain); - - pairs_.push_back(p); - chains_.emplace_back(); - - if (p != unpaired()) - { - pairs_[p] = pairs_.size() - 1; - chains_[p] = std::move(std::get<1>(p_chain)); - } - - return p; - } - - using Parent::unpaired; - - Index pair(Index i) const { return pairs_[i]; } - const Chain& chain(Index i) const { return chains_[i]; } // chain that dies at i - void resize(size_t s) { Parent::resize(s); chains_.resize(s); } - - std::vector chains_; - using Parent::pairs_; - - bool keep_cocycles = true; -}; - -} - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.h deleted file mode 100644 index f7a04b130d..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.h +++ /dev/null @@ -1,170 +0,0 @@ -#ifndef DIONYSUS_REDUCED_MATRIX_H -#define DIONYSUS_REDUCED_MATRIX_H - -#include -#include - -#include "chain.h" -#include "reduction.h" - -namespace dionysus -{ - -template, template class... Visitors> -class ReducedMatrix -{ - public: - typedef ReducedMatrix Self; - - typedef Field_ Field; - typedef Index_ Index; - typedef Comparison_ Comparison; - - typedef std::tuple...> VisitorsTuple; - template - using Visitor = std::tuple_element; - - typedef typename Field::Element FieldElement; - typedef ChainEntry Entry; - typedef std::vector Chain; - - typedef std::vector Chains; - typedef std::vector Indices; - typedef std::vector SkipFlags; - - public: - ReducedMatrix(const Field& field): - field_(field) {} - - ReducedMatrix(const Field& field, - const Comparison& cmp, - const Visitors&... visitors): - field_(field), - cmp_(cmp), - visitors_(visitors...) {} - - ReducedMatrix(Field&& field, - Comparison&& cmp, - Visitors&&... visitors): - field_(std::move(field)), - cmp_(std::move(cmp)), - visitors_(visitors...) {} - - ReducedMatrix(Self&& m) = default; - ReducedMatrix(const Self& m) = default; - - template class... OtherVisitors> - ReducedMatrix(ReducedMatrix&& other): - field_(other.field_), - cmp_(other.cmp_), - reduced_(std::move(other.reduced_)), - pairs_(std::move(other.pairs_)), - skip_(std::move(other.skip_)) {} - - template - Index add(const ChainRange& chain) { return add(Chain(std::begin(chain), std::end(chain))); } - Index add(Chain&& chain); - - template - void set(Index i, const ChainRange& chain) { return set(i, Chain(std::begin(chain), std::end(chain))); } - void set(Index i, Chain&& chain); - - Index reduce(Index i); - Index reduce(Chain& c) { return reduce(c, reduced_, pairs_); } - template - Index reduce(Chain& c, const ChainsLookup& chains, const LowLookup& low); - - Index reduce_upto(Index i); // TODO - - size_t size() const { return pairs_.size(); } - void clear() { Chains().swap(reduced_); Indices().swap(pairs_); } - - void sort(Chain& c) { std::sort(c.begin(), c.end(), [this](const Entry& e1, const Entry& e2) { return this->cmp_(e1.index(), e2.index()); }); } - - const Chain& operator[](Index i) const { return reduced_[i]; } - Index pair(Index i) const { return pairs_[i]; } - void set_pair(Index i, Index j) { pairs_[i] = j; pairs_[j] = i; } - - Chain& column(Index i) { return reduced_[i]; } - - bool skip(Index i) const { return skip_[i]; } - void add_skip(); - void set_skip(Index i, bool flag = true) { skip_[i] = flag; } - - const Field& field() const { return field_; } - const Comparison& cmp() const { return cmp_; } - void reserve(size_t s) { reduced_.reserve(s); pairs_.reserve(s); } - void resize(size_t s); - - const Chains& columns() const { return reduced_; } - - template - Visitor& visitor() { return std::get(visitors_); } - - static const Index unpaired() { return Reduction::unpaired; } - - private: - template class... Vs> - friend class ReducedMatrix; // let's all be friends - - public: - // Visitors::chain_initialized(c) - template - typename std::enable_if::type - visitors_chain_initialized(Chain& c) {} - - template - typename std::enable_if::type - visitors_chain_initialized(Chain& c) { std::get(visitors_).chain_initialized(this, c); visitors_chain_initialized(c); } - - // Visitors::addto(m, cl) - template - typename std::enable_if::type - visitors_addto(FieldElement m, Index cl) {} - - template - typename std::enable_if::type - visitors_addto(FieldElement m, Index cl) { std::get(visitors_).addto(this, m, cl); visitors_addto(m, cl); } - - // Visitors::reduction_finished(m, cl) - template - typename std::enable_if::type - visitors_reduction_finished() {} - - template - typename std::enable_if::type - visitors_reduction_finished() { std::get(visitors_).reduction_finished(this); visitors_reduction_finished(); } - - private: - Field field_; - Comparison cmp_; - Chains reduced_; // matrix R - Indices pairs_; - SkipFlags skip_; // indicates whether the column should be skipped (e.g., for relative homology) - VisitorsTuple visitors_; -}; - -/* Visitors */ - -// The prototypical visitor. Others may (and probably should) inherit from it. -template -struct EmptyVisitor -{ - EmptyVisitor() = default; - - template - EmptyVisitor(const EmptyVisitor&) {} - - - template - void chain_initialized(Self*, Chain& c) {} - - void addto(Self*, typename Field::Element m, Index cl) {} - void reduction_finished(Self*) {} -}; - -} - -#include "reduced-matrix.hpp" - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.hpp deleted file mode 100644 index 3e4aca8f29..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.hpp +++ /dev/null @@ -1,78 +0,0 @@ -template class... V> -void -dionysus::ReducedMatrix:: -resize(size_t s) -{ - reduced_.resize(s); - pairs_.resize(s, unpaired()); - skip_.resize(s, false); -} - -template class... V> -typename dionysus::ReducedMatrix::Index -dionysus::ReducedMatrix:: -add(Chain&& chain) -{ - // TODO: skip the computation entirely if we already know this is positive (in case of the clearing optimization) - Index i = pairs_.size(); - pairs_.emplace_back(unpaired()); - reduced_.emplace_back(); - skip_.push_back(false); - - set(i, std::move(chain)); - - return reduce(i); -} - -template class... V> -void -dionysus::ReducedMatrix:: -add_skip() -{ - pairs_.emplace_back(unpaired()); - reduced_.emplace_back(); - skip_.push_back(true); -} - -template class... V> -void -dionysus::ReducedMatrix:: -set(Index i, Chain&& c) -{ - sort(c); - visitors_chain_initialized(c); - reduced_[i] = std::move(c); -} - -template class... V> -typename dionysus::ReducedMatrix::Index -dionysus::ReducedMatrix:: -reduce(Index i) -{ - Chain& c = column(i); - Index pair = reduce(c); - - if (pair != unpaired()) - pairs_[pair] = i; - - pairs_[i] = pair; - visitors_reduction_finished<>(); - - return pair; -} - -template class... V> -template -typename dionysus::ReducedMatrix::Index -dionysus::ReducedMatrix:: -reduce( Chain& c, - const ChainsLookup& chains, - const LowLookup& lows) -{ - auto entry_cmp = [this](const Entry& e1, const Entry& e2) { return this->cmp_(e1.index(), e2.index()); }; - return Reduction::reduce(c, chains, lows, field_, - [this](FieldElement m, Index cl) - { this->visitors_addto<>(m, cl); }, - entry_cmp); -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduction.h deleted file mode 100644 index 2afd333d41..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduction.h +++ /dev/null @@ -1,109 +0,0 @@ -#ifndef DIONYSUS_REDUCTION_H -#define DIONYSUS_REDUCTION_H - -#include -#include -#include -#include -#include "chain.h" - -namespace dionysus -{ - -namespace detail -{ - -template -struct Unpaired -{ static constexpr Index value() { return std::numeric_limits::max(); } }; - -} - -template -struct Reduction -{ - typedef Index_ Index; - - template - using AddtoVisitor = std::function; - - template - struct CallToSub; - - static const Index unpaired; - - template> - static - Index reduce(Chain1& c, - const ChainsLookup& chains, - const LowLookup& lows, - const Field& field, - const AddtoVisitor& visitor = [](typename Field::Element, Index) {}, - const Comparison& cmp = Comparison()) - { - typedef typename Field::Element FieldElement; - - while (!c.empty()) - { - //auto& low = c.back(); - auto& low = *(std::prev(c.end())); - Index l = low.index(); - Index cl = lows(l); - // std::cout << "idx: " << std::get<0>(cl) << ", " << std::get<1>(cl) << "\n"; - if (cl == unpaired) - return l; - else - { - // Reduce further - auto& co = chains(cl); - auto& co_low = co.back(); - FieldElement m = field.neg(field.div(low.element(), co_low.element())); - // c += m*co - Chain::addto(c, m, co, field, cmp); - visitor(m, cl); - } - } - return unpaired; - } - - template> - static - Index reduce(Chain1& c, - const std::vector& chains, - const std::vector& lows, - const Field& field, - const AddtoVisitor& visitor = [](typename Field::Element, Index) {}, - const Comparison& cmp = Comparison()) - { - return reduce(c, - CallToSub(chains), - CallToSub(lows), - field, visitor, cmp); - } - - // This is a work-around a bug in GCC (should really be a lambda function) - template - struct CallToSub - { - CallToSub(const std::vector& items_): - items(items_) {} - const Item& operator()(Index i) const { return items[i]; } - const std::vector& items; - }; -}; - - -template -const Index -Reduction::unpaired = detail::Unpaired::value(); - -} - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.h deleted file mode 100644 index 167a32779d..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef RELATIVE_HOMOLOGY_ZIGZAG_H -#define RELATIVE_HOMOLOGY_ZIGZAG_H - -#include -#include - -#include "zigzag-persistence.h" - -namespace dionysus -{ - -namespace ba = boost::adaptors; - -template> -class RelativeHomologyZigzag -{ - public: - typedef Field_ Field; - typedef Index_ Index; - typedef Comparison_ Comparison; - - typedef ZigzagPersistence ZZP; - typedef typename ZZP::IndexChain IndexChain; - typedef typename ZZP::FieldElement FieldElement; - typedef typename IndexChain::value_type ChainEntry; - - - typedef Comparison Cmp; - - RelativeHomologyZigzag(const Field& field, - const Comparison& cmp = Comparison()): - zzp_(field, cmp) - { - zzp_.add( IndexChain() ); // vertex w - ++zzp_op_; - ++zzp_cell_; - } - - template - void add_both(const ChainRange& chain); - - void remove_both(Index cell); - - // index of the absolute cell; chain = its boundary - template - Index add(Index cell, const ChainRange& chain); // add to the relative part - - Index remove(Index cell); // remove from the relative part - - const Field& field() const { return zzp_.field(); } - const Cmp& cmp() const { return zzp_.cmp(); } - - size_t alive_size() const { return zzp_.alive_size() - 1; } // -1 for the cone vertex - - static - const Index unpaired() { return ZZP::unpaired(); } - - private: - template - IndexChain relative_chain(Index cell, const ChainRange& chain) const; - - template - IndexChain absolute_chain(const ChainRange& chain) const; - - Index abs_index(Index idx) const { return absolute_.left.find(idx)->second; } - Index rel_index(Index idx) const { return relative_.left.find(idx)->second; } - Index decode_pair(Index pair); - - private: - ZZP zzp_; // underlying (cone) implementation - boost::bimap absolute_; // bimap between our cells and zzp absolute cells - boost::bimap relative_; // bimap between our cells and zzp relative cells - std::unordered_map op_map_; // map from zzp_op to our op - Index op_ = 0, - zzp_op_ = 0, - cell_ = 0, - zzp_cell_ = 0; -}; - -} - -#include "relative-homology-zigzag.hpp" - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.hpp deleted file mode 100644 index 499807106c..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.hpp +++ /dev/null @@ -1,122 +0,0 @@ -template -template -void -dionysus::RelativeHomologyZigzag:: -add_both(const ChainRange& chain) -{ - zzp_.add(absolute_chain(chain)); - op_map_.insert( { zzp_op_++, op_ } ); - absolute_.left.insert( { cell_, zzp_cell_++ } ); - - zzp_.add(relative_chain(cell_, chain)); - op_map_.insert( { zzp_op_++, op_ } ); - relative_.left.insert( { cell_, zzp_cell_++ } ); - - cell_++; - op_++; -} - -template -void -dionysus::RelativeHomologyZigzag:: -remove_both(Index cell) -{ - Index abs_cell = absolute_.left.find(cell)->second; - Index rel_cell = relative_.left.find(cell)->second; - - zzp_.remove(rel_cell); - zzp_.remove(abs_cell); - - absolute_.left.erase(cell); - relative_.left.erase(cell); - - op_map_.insert( { zzp_op_++, op_ } ); - op_map_.insert( { zzp_op_++, op_ } ); - - op_++; -} - -template -template -typename dionysus::RelativeHomologyZigzag::Index -dionysus::RelativeHomologyZigzag:: -add(Index cell, const ChainRange& chain) -{ - Index pair = zzp_.add(relative_chain(cell, chain)); - op_map_.insert( { zzp_op_++, op_++ } ); - relative_.left.insert( { cell, zzp_cell_++ } ); - - return decode_pair(pair); -} - - -template -typename dionysus::RelativeHomologyZigzag::Index -dionysus::RelativeHomologyZigzag:: -decode_pair(Index pair) -{ - if (pair == unpaired()) - return pair; - - Index decoded = op_map_.find(pair)->second; - op_map_.erase(pair); - return decoded; -} - -template -template -typename dionysus::RelativeHomologyZigzag::IndexChain -dionysus::RelativeHomologyZigzag:: -absolute_chain(const ChainRange& chain) const -{ - IndexChain res; - for (const auto& e : chain) - res.push_back(ChainEntry(e.element(), abs_index(e.index()))); - return res; -} - -template -template -typename dionysus::RelativeHomologyZigzag::IndexChain -dionysus::RelativeHomologyZigzag:: -relative_chain(Index cell, const ChainRange& chain) const -{ - // NB: to compute the signs correctly, - // this assumes that the cone vertex w is the last vertex in some total order - - typedef typename IndexChain::value_type ChainEntry; - - IndexChain res; - if (!chain.empty()) - { - for (const auto& e : chain) - res.push_back(ChainEntry(e.element(), rel_index(e.index()))); - - FieldElement a = field().id(); - if (chain.size() % 2 == 0) // TODO: double-check - a = field().neg(a); - res.push_back(ChainEntry(a, abs_index(cell))); // add the base space cell - } else - { - res.reserve(2); - res.push_back(ChainEntry(field().id(), abs_index(cell))); - res.push_back(ChainEntry(field().neg(field().id()), 0)); - } - return res; -} - - -template -typename dionysus::RelativeHomologyZigzag::Index -dionysus::RelativeHomologyZigzag:: -remove(Index cell) -{ - Index rel_cell = rel_index(cell); - Index pair = zzp_.remove(rel_cell); - pair = decode_pair(pair); - - op_map_.insert( { zzp_op_++, op_++ } ); - relative_.left.erase(cell); - - return pair; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.h deleted file mode 100644 index c7ccb1189e..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.h +++ /dev/null @@ -1,147 +0,0 @@ -#ifndef DIONYSUS_RIPS_H -#define DIONYSUS_RIPS_H - -#include -#include - -#include - -#include "simplex.h" - -namespace dionysus -{ - -/** - * Rips class - * - * Class providing basic operations to work with Rips complexes. It implements Bron-Kerbosch algorithm, - * and provides simple wrappers for various functions. - * - * Distances_ is expected to define types IndexType and DistanceType as well as - * provide operator()(...) which given two IndexTypes should return - * the distance between them. There should be methods begin() and end() - * for iterating over IndexTypes as well as a method size(). - */ -template > -class Rips -{ - public: - typedef Distances_ Distances; - typedef typename Distances::IndexType IndexType; - typedef typename Distances::DistanceType DistanceType; - - typedef Simplex_ Simplex; - typedef typename Simplex::Vertex Vertex; // should be the same as IndexType - typedef std::vector VertexContainer; - - typedef short unsigned Dimension; - - class Evaluator; - class Comparison; - - public: - Rips(const Distances& distances): - distances_(distances) {} - - // Calls functor f on each simplex in the k-skeleton of the Rips complex - template - void generate(Dimension k, DistanceType max, const Functor& f, - Iterator candidates_begin, Iterator candidates_end) const; - - // Calls functor f on all the simplices of the Rips complex that contain the given vertex v - template - void vertex_cofaces(IndexType v, Dimension k, DistanceType max, const Functor& f, - Iterator candidates_begin, Iterator candidates_end) const; - - // Calls functor f on all the simplices of the Rips complex that contain the given edge [u,v] - template - void edge_cofaces(IndexType u, IndexType v, Dimension k, DistanceType max, const Functor& f, - Iterator candidates_begin, Iterator candidates_end) const; - - // Calls functor f on all the simplices of the Rips complex that contain the given Simplex s - // (unlike the previous methods it does not call the functor on the Simplex s itself) - template - void cofaces(const Simplex& s, Dimension k, DistanceType max, const Functor& f, - Iterator candidates_begin, Iterator candidates_end) const; - - - /* No Iterator argument means Iterator = IndexType and the range is [distances().begin(), distances().end()) */ - template - void generate(Dimension k, DistanceType max, const Functor& f) const - { generate(k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); } - - template - void vertex_cofaces(IndexType v, Dimension k, DistanceType max, const Functor& f) const - { vertex_cofaces(v, k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); } - - template - void edge_cofaces(IndexType u, IndexType v, Dimension k, DistanceType max, const Functor& f) const - { edge_cofaces(u, v, k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); } - - template - void cofaces(const Simplex& s, Dimension k, DistanceType max, const Functor& f) const - { cofaces(s, k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); } - - - const Distances& distances() const { return distances_; } - DistanceType max_distance() const; - - DistanceType distance(const Simplex& s1, const Simplex& s2) const; - - - template - static void bron_kerbosch(VertexContainer& current, - const VertexContainer& candidates, - typename VertexContainer::const_iterator excluded, - Dimension max_dim, - const NeighborTest& neighbor, - const Functor& functor, - bool check_initial = true); - - protected: - const Distances& distances_; -}; - -template -class Rips::Evaluator -{ - public: - typedef Simplex_ Simplex; - - Evaluator(const Distances& distances): - distances_(distances) {} - - DistanceType operator()(const Simplex& s) const; - - protected: - const Distances& distances_; -}; - -template -class Rips::Comparison -{ - public: - typedef Simplex_ Simplex; - - Comparison(const Distances& distances): - eval_(distances) {} - - bool operator()(const Simplex& s1, const Simplex& s2) const - { - DistanceType e1 = eval_(s1), - e2 = eval_(s2); - if (e1 == e2) - return s1.dimension() < s2.dimension(); - - return e1 < e2; - } - - protected: - Evaluator eval_; -}; - -} - -#include "rips.hpp" - -#endif // DIONYSUS_RIPS_H diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.hpp deleted file mode 100644 index 2fdda34a7a..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.hpp +++ /dev/null @@ -1,162 +0,0 @@ -#include -#include -#include -#include - -#include -#include - -template -template -void -dionysus::Rips:: -generate(Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const -{ - auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; }; - - // current = empty - // candidates = everything - VertexContainer current; - VertexContainer candidates(bg, end); - bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f); -} - -template -template -void -dionysus::Rips:: -vertex_cofaces(IndexType v, Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const -{ - auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; }; - - // current = [v] - // candidates = everything - [v] - VertexContainer current; current.push_back(v); - VertexContainer candidates; - for (Iterator cur = bg; cur != end; ++cur) - if (*cur != v && neighbor(v, *cur)) - candidates.push_back(*cur); - - bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f); -} - -template -template -void -dionysus::Rips:: -edge_cofaces(IndexType u, IndexType v, Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const -{ - auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; }; - - // current = [u,v] - // candidates = everything - [u,v] - VertexContainer current; current.push_back(u); current.push_back(v); - - VertexContainer candidates; - for (Iterator cur = bg; cur != end; ++cur) - if (*cur != u && *cur != v && neighbor(v,*cur) && neighbor(u,*cur)) - candidates.push_back(*cur); - - bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f); -} - -template -template -void -dionysus::Rips:: -cofaces(const Simplex& s, Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const -{ - namespace ba = boost::adaptors; - - auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; }; - - // current = s - VertexContainer current(s.begin(), s.end()); - - // candidates = everything - s that is a neighbor of every vertex in the simplex - VertexContainer candidates; - boost::set_difference(std::make_pair(bg, end) | - ba::filtered([this,&s,&neighbor](Vertex cur) - { for (auto& v : s) - if (!neighbor(v, cur)) - return false; - }), - s, - std::back_inserter(candidates)); - - bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f, false); -} - - -template -template -void -dionysus::Rips:: -bron_kerbosch(VertexContainer& current, - const VertexContainer& candidates, - typename VertexContainer::const_iterator excluded, - Dimension max_dim, - const NeighborTest& neighbor, - const Functor& functor, - bool check_initial) -{ - if (check_initial && !current.empty()) - functor(Simplex(current)); - - if (current.size() == static_cast(max_dim) + 1) - return; - - for (auto cur = std::next(excluded); cur != candidates.end(); ++cur) - { - current.push_back(*cur); - - VertexContainer new_candidates; - for (auto ccur = candidates.begin(); ccur != cur; ++ccur) - if (neighbor(*ccur, *cur)) - new_candidates.push_back(*ccur); - size_t ex = new_candidates.size(); - for (auto ccur = std::next(cur); ccur != candidates.end(); ++ccur) - if (neighbor(*ccur, *cur)) - new_candidates.push_back(*ccur); - excluded = new_candidates.begin() + (ex - 1); - - bron_kerbosch(current, new_candidates, excluded, max_dim, neighbor, functor); - current.pop_back(); - } -} - -template -typename dionysus::Rips::DistanceType -dionysus::Rips:: -distance(const Simplex& s1, const Simplex& s2) const -{ - DistanceType mx = 0; - for (auto a : s1) - for (auto b : s2) - mx = std::max(mx, distances_(a,b)); - return mx; -} - -template -typename dionysus::Rips::DistanceType -dionysus::Rips:: -max_distance() const -{ - DistanceType mx = 0; - for (IndexType a = distances_.begin(); a != distances_.end(); ++a) - for (IndexType b = std::next(a); b != distances_.end(); ++b) - mx = std::max(mx, distances_(a,b)); - return mx; -} - -template -typename dionysus::Rips::DistanceType -dionysus::Rips::Evaluator:: -operator()(const Simplex& s) const -{ - DistanceType mx = 0; - for (auto a = s.begin(); a != s.end(); ++a) - for (auto b = std::next(a); b != s.end(); ++b) - mx = std::max(mx, distances_(*a,*b)); - return mx; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.h deleted file mode 100644 index e2481ce080..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef DIONYSUS_ROW_REDUCTION_H -#define DIONYSUS_ROW_REDUCTION_H - -#include "reduced-matrix.h" - -namespace dionysus -{ - -// Mid-level interface -template, template class... Visitors> -class RowReduction -{ - public: - typedef Field_ Field; - typedef Index_ Index; - typedef Comparison_ Comparison; - - typedef ReducedMatrix Persistence; - - public: - RowReduction(const Field& field): - persistence_(field) {} - - RowReduction(const Field& field, - const Comparison& cmp, - const Visitors&... visitors): - persistence_(field, cmp, visitors...) {} - - template - void operator()(const Filtration& f, const Relative& relative, const ReportPair& report_pair, const Progress& progress); - - template - void operator()(const Filtration& f, const ReportPair& report_pair); - - template - void operator()(const Filtration& f) { return (*this)(f, &no_report_pair); } - - static void no_report_pair(int, Index, Index) {} - static void no_progress() {} - - const Persistence& - persistence() const { return persistence_; } - Persistence& persistence() { return persistence_; } - - private: - Persistence persistence_; -}; - -} - -#include "row-reduction.hpp" - -#endif - diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.hpp deleted file mode 100644 index edb1652872..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.hpp +++ /dev/null @@ -1,103 +0,0 @@ -#include -namespace ba = boost::adaptors; - -template class... V> -template -void -dionysus::RowReduction:: -operator()(const Filtration& filtration, const ReportPair& report_pair) -{ - using Cell = typename Filtration::Cell; - (*this)(filtration, [](const Cell&) { return false; }, report_pair, &no_progress); -} - -template class... V> -template -void -dionysus::RowReduction:: -operator()(const Filtration& filtration, const Relative& relative, const ReportPair& report_pair, const Progress& progress) -{ - persistence_.resize(filtration.size()); - - typedef typename Persistence::Index Index; - typedef typename Persistence::FieldElement Element; - typedef typename Persistence::Chain Chain; - typedef typename Filtration::Cell Cell; - typedef ChainEntry CellChainEntry; - typedef ChainEntry ChainEntry; - - std::vector rows(persistence_.size()); - - auto& field = persistence_.field(); - - // fill the matrix - Index i = 0; - for(auto& c : filtration) - { - progress(); - - if (relative(c)) - { - persistence_.set_skip(i); - ++i; - continue; - } - - persistence_.set(i, c.boundary(field) | - ba::filtered([relative](const CellChainEntry& e) { return !relative(e.index()); }) | - ba::transformed([this,&filtration](const CellChainEntry& e) - { return ChainEntry(e.element(), filtration.index(e.index())); })); - if (!persistence_[i].empty()) - { - auto& x = persistence_[i].back(); - rows[x.index()].emplace_back(x.element(),i); - } - ++i; - } - - auto entry_cmp = [this](const ChainEntry& e1, const ChainEntry& e2) { return this->persistence_.cmp()(e1.index(), e2.index()); }; - - // reduce the matrix from the bottom up - for (auto it = rows.rbegin(); it != rows.rend(); ++it) - { - auto& row = *it; - Index r = rows.rend() - it - 1; - - if (row.empty()) - continue; - - // add the first column to every other column - Index c = row.front().index(); - Element e = row.front().element(); - Chain& first = persistence_.column(c); - for (size_t i = 1; i < row.size(); ++i) - { - Index cur_idx = row[i].index(); - Element cur_elem = row[i].element(); - Chain& cur = persistence_.column(cur_idx); - if (cur.empty()) // zeroed out by the clearing optimization - continue; - - Element m = field.neg(field.div(cur_elem, e)); - // cur += m*first - ::dionysus::Chain::addto(cur, m, first, field, entry_cmp); - - // update row - if (!cur.empty()) - { - ChainEntry ce = cur.back(); - auto& new_row = rows[ce.index()]; - new_row.emplace_back(ce.element(), cur_idx); - if (entry_cmp(new_row.back(), new_row.front())) - std::swap(new_row.back(), new_row.front()); - } - } - - persistence_.set_pair(r,c); - report_pair(filtration[r].dimension(), r, c); - - // zero out the corresponding column (the clearing optimization) - persistence_.column(r).clear(); - } -} - diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/simplex.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/simplex.h deleted file mode 100644 index 4ac5cb6945..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/simplex.h +++ /dev/null @@ -1,280 +0,0 @@ -#ifndef DIONYSUS_SIMPLEX_H -#define DIONYSUS_SIMPLEX_H - -#include -#include - -//#include -#include -#include -#include - -#include "chain.h" - -namespace dionysus -{ - -struct Empty {}; - -template -class Simplex -{ - public: - typedef Vertex_ Vertex; - typedef T Data; - typedef std::unique_ptr Vertices; - - template - struct BoundaryChainIterator; - struct BoundaryIterator; - - template - using BoundaryChainRange = boost::iterator_range>; - using BoundaryRange = boost::iterator_range; - - template - using Entry = ChainEntry; - - public: - Simplex(const Data& d = Data()): - dim_(-1), data_(d) {} - - Simplex(const std::initializer_list& vertices, - Data&& d = Data()): - Simplex(vertices.size() - 1, vertices.begin(), vertices.end(), std::move(d)) - {} - - Simplex(const std::initializer_list& vertices, - const Data& d): - Simplex(vertices.size() - 1, vertices.begin(), vertices.end(), d) {} - - Simplex(short unsigned dim, Vertices&& vertices, Data&& data = Data()): - dim_(dim), vertices_(std::move(vertices)), data_(std::move(data)) { std::sort(begin(), end()); } - - template - Simplex(const VertexRange& vertices, - Data&& d = Data()): - Simplex(vertices.size() - 1, vertices.begin(), vertices.end(), std::move(d)) - {} - - template - Simplex(const VertexRange& vertices, - const Data& d): - Simplex(vertices.size() - 1, vertices.begin(), vertices.end(), d) {} - - Simplex(const Simplex& other): - Simplex(other.dim_, other.begin(), other.end(), other.data_) {} - Simplex& operator=(const Simplex& other) { dim_ = other.dim_; vertices_ = Vertices(new Vertex[dim_+1]); std::copy(other.begin(), other.end(), begin()); data_ = other.data_; return *this; } - - Simplex(Simplex&& other) noexcept: - dim_(other.dim_), - vertices_(std::move(other.vertices_)), - data_(std::move(other.data_)) {} - Simplex& operator=(Simplex&& other) = default; - - template - Simplex(short unsigned dim, - Iterator b, Iterator e, - Data&& d = Data()): - dim_(dim), - vertices_(new Vertex[dim_+1]), - data_(std::move(d)) { std::copy(b, e, begin()); std::sort(begin(), end()); } - - template - Simplex(short unsigned dim, - Iterator b, Iterator e, - const Data& d): - dim_(dim), - vertices_(new Vertex[dim_+1]), - data_(d) { std::copy(b, e, begin()); std::sort(begin(), end()); } - - short unsigned dimension() const { return dim_; } - - BoundaryRange boundary() const { return BoundaryRange(boundary_begin(), boundary_end()); } - BoundaryIterator boundary_begin() const; - BoundaryIterator boundary_end() const; - - template - BoundaryChainRange - boundary(const Field& field) const { return BoundaryChainRange(boundary_begin(field), boundary_end(field)); } - - template - BoundaryChainIterator - boundary_begin(const Field& field) const; - template - BoundaryChainIterator - boundary_end(const Field& field) const; - - const Vertex* begin() const { return vertices_.get(); } - const Vertex* end() const { return begin() + dim_ + 1; } - size_t size() const { return dim_ + 1; } - - std::pair - range() const { return std::make_pair(begin(), end()); } - - Simplex join(const Vertex& v) const { Vertices vertices(new Vertex[dim_+2]); std::copy(begin(), end(), vertices.get()); vertices[dim_+1] = v; return Simplex(dim_ + 1, std::move(vertices), Data(data_)); } - - bool operator==(const Simplex& other) const { return dim_ == other.dim_ && std::equal(begin(), end(), other.begin()); } - bool operator!=(const Simplex& other) const { return !operator==(other); } - bool operator<(const Simplex& other) const { return dim_ < other.dim_ || (dim_ == other.dim_ && std::lexicographical_compare(begin(), end(), other.begin(), other.end())); } - bool operator>(const Simplex& other) const { return other < (*this); } - - Vertex operator[](short unsigned i) const { return vertices_[i]; } - const Data& data() const { return data_; } - Data& data() { return data_; } - - friend - std::ostream& operator<<(std::ostream& out, const Simplex& s) - { out << '<' << *s.begin(); for (auto it = s.begin() + 1; it != s.end(); ++it) out << ',' << *it; out << '>'; return out; } - - private: - Vertex* begin() { return vertices_.get(); } - Vertex* end() { return begin() + dim_ + 1; } - - private: - short unsigned dim_; - //boost::compressed_pair vertices_data_; - Vertices vertices_; - Data data_; // TODO: optimize -}; - -template -size_t hash_value(const Simplex& s) { return boost::hash_range(s.begin(), s.end()); } - - -template -struct Simplex::BoundaryIterator: - public boost::iterator_adaptor, // Value - boost::use_default, - Simplex> // Reference -{ - public: - typedef const V* Iterator; - typedef Simplex Value; - - typedef boost::iterator_adaptor Parent; - - BoundaryIterator() {} - explicit BoundaryIterator(short unsigned dim, Iterator iter, Iterator bg, Iterator end): - Parent(iter), dim_(dim), bg_(bg), end_(end) {} - - Iterator begin() const { return bg_; } - - private: - friend class boost::iterator_core_access; - Value dereference() const - { - typedef std::not_equal_to NotEqualVertex; - - using std::placeholders::_1; - return Simplex(dim_ - 1, - boost::make_filter_iterator(std::bind(NotEqualVertex(), _1, *(this->base())), bg_, end_), - boost::make_filter_iterator(std::bind(NotEqualVertex(), _1, *(this->base())), end_, end_)); - } - - short unsigned dim_; - Iterator bg_; - Iterator end_; -}; - -template -template -struct Simplex::BoundaryChainIterator: - public boost::iterator_adaptor, // Derived - BoundaryIterator, - ChainEntry>, // Value - boost::use_default, - ChainEntry>> // Reference -{ - public: - typedef F Field; - typedef BoundaryIterator Iterator; - typedef ChainEntry> Value; - - typedef boost::iterator_adaptor Parent; - - BoundaryChainIterator() {} - explicit BoundaryChainIterator(const Field& field, Iterator iter): - Parent(iter), field_(&field) {} - - private: - friend class boost::iterator_core_access; - Value dereference() const - { - return Value(((this->base().base() - this->base().begin()) % 2 == 0)? field_->id() : field_->neg(field_->id()), - *(this->base())); - } - - const Field* field_ = nullptr; -}; - - -/* Simplex */ -template -typename Simplex::BoundaryIterator -Simplex:: -boundary_begin() const -{ - if (dimension() == 0) return boundary_end(); - return BoundaryIterator(dimension(), begin(), begin(), end()); -} - -template -typename Simplex::BoundaryIterator -Simplex:: -boundary_end() const -{ - return BoundaryIterator(dimension(), end(), begin(), end()); -} - -template -template -#if defined(_MSC_VER) -typename Simplex::BoundaryChainIterator -#else -typename Simplex::template BoundaryChainIterator -#endif -Simplex:: -boundary_begin(const F& field) const -{ - if (dimension() == 0) return boundary_end(field); - return BoundaryChainIterator(field, boundary_begin()); -} - -template -template -#if defined(_MSC_VER) -typename Simplex::BoundaryChainIterator -#else -typename Simplex::template BoundaryChainIterator -#endif -Simplex:: -boundary_end(const F& field) const -{ - return BoundaryChainIterator(field, boundary_end()); -} - -} // dionysus - -namespace std -{ - -template -struct hash> -{ - size_t operator()(const dionysus::Simplex& s) const { return hash_value(s); } -}; - -} // std - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.h deleted file mode 100644 index fb1e929e02..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.h +++ /dev/null @@ -1,184 +0,0 @@ -#ifndef DIONYSUS_SPARSE_ROW_MATRIX_H -#define DIONYSUS_SPARSE_ROW_MATRIX_H - -#include -#include -#include -#include // for debugging output - -#include - -#include "chain.h" -#include "reduction.h" - -namespace dionysus -{ - -namespace bi = boost::intrusive; - -namespace detail -{ - typedef bi::list_base_hook> auto_unlink_hook; - - template - struct SparseRowMatrixEntry: - public ChainEntry, auto_unlink_hook> - { - typedef I Index; - typedef typename F::Element FieldElement; - typedef std::tuple IndexPair; // (id, pair) - typedef ChainEntry Parent; - typedef SparseRowMatrixEntry Entry; - - SparseRowMatrixEntry(FieldElement e, const IndexPair& ip): - Parent(e,ip) {} - - SparseRowMatrixEntry(FieldElement e, const Index& r, const Index& c): - Parent(e,IndexPair(r,c)) {} - - SparseRowMatrixEntry(const Entry& other) = default; - SparseRowMatrixEntry(Entry&& other) = default; - Entry& operator=(Entry&& other) = default; - - void unlink() { auto_unlink_hook::unlink(); } - bool is_linked() const { return auto_unlink_hook::is_linked(); } - }; -} - -template, - template class Column_ = std::vector> -class SparseRowMatrix -{ - public: - typedef Field_ Field; - typedef Index_ Index; - typedef Comparison_ Comparison; - - typedef typename Field::Element FieldElement; - - typedef detail::SparseRowMatrixEntry Entry; - typedef Column_ Column; - typedef typename Entry::IndexPair IndexPair; - typedef bi::list> Row; - - typedef std::vector> IndexChain; - - typedef std::unordered_map Columns; - typedef std::unordered_map Rows; - typedef std::unordered_map LowMap; - - public: - SparseRowMatrix(const Field& field, - const Comparison& cmp = Comparison()): - field_(field), cmp_(cmp) {} - - SparseRowMatrix(SparseRowMatrix&& other) = default; - - - template - Column reduce(const ChainRange& chain, IndexChain& trail); - - Index set(Index i, Column&& chain); // returns previous column with this low - void fix(Index c, Column& column); - void fix(Index c) { fix(c, col(c)); } - - const Row& prepend_row(Index r, FieldElement m, const Row& chain); // could be horribly inefficient if Column is chosen poorly - - void drop_row(Index r) { rows_.erase(r); if (is_low(r)) lows_.erase(r); } - void drop_col(Index c) - { - auto cit = columns_.find(c); - Column& column = cit->second; - if (!column.empty()) - { - Index rlow = std::get<0>(column.back().index()); - auto it = lows_.find(rlow); - if (it != lows_.end() && it->second == c) - lows_.erase(it); - } - columns_.erase(cit); - } - void drop_low(Index r) { lows_.erase(r); } - - // accessors - Row& row(Index r) { return rows_[r]; } - Column& col(Index c) { assert(col_exists(c)); return columns_.find(c)->second; } - const Column& col(Index c) const { assert(col_exists(c)); return columns_.find(c)->second; } - Index low(Index r) const { return lows_.find(r)->second; } - bool is_low(Index r) const { return lows_.find(r) != lows_.end(); } - void update_low(Index c) { lows_[std::get<0>(col(c).back().index())] = c; } - - const Field& field() const { return field_; } - void reserve(size_t) {} // here for compatibility only - const Comparison& cmp() const { return cmp_; } - - // debug - bool col_exists(Index c) const { return columns_.find(c) != columns_.end(); } - const Columns& columns() const { return columns_; } - void check_columns() const - { - for (auto& x : columns_) - { - Index c = x.first; - if (x.second.empty()) - std::cout << "Warning: empty column " << c << std::endl; - Index rl = std::get<0>(x.second.back().index()); - if (!is_low(rl) || low(rl) != c) - { - std::cout << "Columns don't check out: lows don't match" << std::endl; - std::cout << " " << c << ' ' << rl << ' ' << ' ' << low(rl) << std::endl; - std::cout << "---\n"; - for (auto& x : col(c)) - std::cout << " " << x.element() << ' ' << std::get<0>(x.index()) << ' ' << std::get<1>(x.index()) << '\n'; - std::cout << "---\n"; - for (auto& x : col(low(rl))) - std::cout << " " << x.element() << ' ' << std::get<0>(x.index()) << ' ' << std::get<1>(x.index()) << '\n'; - assert(0); - } - - for (auto& x : lows_) - { - if (!col_exists(x.second)) - { - std::cout << "Still keeping low of a removed column" << std::endl; - assert(0); - } - else if (std::get<0>(col(x.second).back().index()) != x.first) - { - std::cout << "Low mismatch: " << x.second << ' ' << std::get<0>(col(x.second).back().index()) << ' ' << x.first << '\n'; - assert(0); - } - } - } - } - - private: - Field field_; - Comparison cmp_; - - Columns columns_; - Rows rows_; - LowMap lows_; // column that has this low -}; - - -namespace detail -{ - -template -struct Unpaired> -{ - static - constexpr std::tuple - value() - { return std::make_tuple(std::numeric_limits::max(), - std::numeric_limits::max()); } -}; - -} - -} - -#include "sparse-row-matrix.hpp" - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.hpp deleted file mode 100644 index 10f4808c17..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.hpp +++ /dev/null @@ -1,103 +0,0 @@ -template class Col> -template -typename dionysus::SparseRowMatrix::Column -dionysus::SparseRowMatrix:: -reduce(const ChainRange& chain_, IndexChain& trail) -{ - auto row_cmp = [this](const Entry& e1, const Entry& e2) - { return this->cmp_(std::get<0>(e1.index()), std::get<0>(e2.index())); }; - -#define __DIONYSUS_USE_VECTOR_CHAINS 1 - -#if !(__DIONYSUS_USE_VECTOR_CHAINS) - std::set chain(row_cmp); - for (auto x : chain_) - chain.insert(Entry(x.element(), IndexPair(x.index(), 0))); -#else - Column chain; - for (auto x : chain_) - chain.emplace_back(x.element(), IndexPair(x.index(), 0)); - std::sort(chain.begin(), chain.end(), row_cmp); -#endif - - typedef Reduction ReductionIP; - - auto chains = [this](const IndexPair& rc) -> const Column& { return this->col(std::get<1>(rc)); }; - auto lows = [this](const IndexPair& rc) -> IndexPair - { - Index r = std::get<0>(rc); - auto it = this->lows_.find(r); - if (it == this->lows_.end()) - return ReductionIP::unpaired; - else - { - Index rr = std::get<0>(col(it->second).back().index()); - if (rr != r) - std::cout << "Mismatch: " << rr << ' ' << r << std::endl; - return IndexPair(r, it->second); - } - }; - - auto addto = [&trail](FieldElement m, const IndexPair& rc) { trail.emplace_back(m, std::get<1>(rc)); }; - - ReductionIP::reduce(chain, - chains, lows, - field_, addto, row_cmp); - -#if !(__DIONYSUS_USE_VECTOR_CHAINS) - return Column(std::begin(chain), std::end(chain)); -#else - return chain; -#endif -} - -template class Col> -typename dionysus::SparseRowMatrix::Index -dionysus::SparseRowMatrix:: -set(Index col, Column&& chain) -{ - Column& column = columns_.emplace(col, std::move(chain)).first->second; - - fix(col, column); - - Index r = std::get<0>(column.back().index()); - Index res; - if (is_low(r)) - res = low(r); - else - res = col; - lows_[r] = col; - - return res; -} - -template class Col> -void -dionysus::SparseRowMatrix:: -fix(Index col, Column& column) -{ - for (auto& x : column) - { - std::get<1>(x.index()) = col; - Index r = std::get<0>(x.index()); - row(r).push_back(x); - } -} - -template class Col> -const typename dionysus::SparseRowMatrix::Row& -dionysus::SparseRowMatrix:: -prepend_row(Index r, FieldElement m, const Row& chain) -{ - Row& new_row = row(r); - - for (auto& x : chain) - { - Index c = std::get<1>(x.index()); - Column& column = col(c); - auto it = column.emplace(column.begin(), field().mul(x.element(), m), r, c); - new_row.push_back(*it); - } - - return new_row; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.h deleted file mode 100644 index 0477d4683f..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef DIONYSUS_STANDARD_REDUCTION_H -#define DIONYSUS_STANDARD_REDUCTION_H - -namespace dionysus -{ - -// Mid-level interface -template -class StandardReduction -{ - public: - typedef Persistence_ Persistence; - typedef typename Persistence::Field Field; - typedef typename Persistence::Index Index; - - public: - StandardReduction(Persistence& persistence): - persistence_(persistence) {} - - template - void operator()(const Filtration& f, const Relative& relative, const ReportPair& report_pair, const Progress& progress); - - template - void operator()(const Filtration& f, const ReportPair& report_pair); - - template - void operator()(const Filtration& f) { return (*this)(f, &no_report_pair); } - - static void no_report_pair(int, Index, Index) {} - static void no_progress() {} - - const Persistence& - persistence() const { return persistence_; } - Persistence& persistence() { return persistence_; } - - private: - Persistence& persistence_; -}; - -} - -#include "standard-reduction.hpp" - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.hpp deleted file mode 100644 index 9aa3396a8c..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#include -namespace ba = boost::adaptors; - -template -template -void -dionysus::StandardReduction

:: -operator()(const Filtration& filtration, const ReportPair& report_pair) -{ - using Cell = typename Filtration::Cell; - (*this)(filtration, [](const Cell&) { return false; }, report_pair, no_progress); -} - -template -template -void -dionysus::StandardReduction

:: -operator()(const Filtration& filtration, const Relative& relative, const ReportPair& report_pair, const Progress& progress) -{ - persistence_.reserve(filtration.size()); - - typedef typename Filtration::Cell Cell; - typedef ChainEntry CellChainEntry; - typedef ChainEntry ChainEntry; - - unsigned i = 0; - for(auto& c : filtration) - { - progress(); - - if (relative(c)) - { - ++i; - persistence_.add_skip(); - continue; - } - - //std::cout << "Adding: " << c << " : " << boost::distance(c.boundary(persistence_.field())) << std::endl; - Index pair = persistence_.add(c.boundary(persistence_.field()) | - ba::filtered([relative](const CellChainEntry& e) { return !relative(e.index()); }) | - ba::transformed([this,&filtration](const CellChainEntry& e) - { return ChainEntry(e.element(), filtration.index(e.index())); })); - if (pair != persistence_.unpaired()) - report_pair(c.dimension(), pair, i); - ++i; - } -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/trails-chains.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/trails-chains.h deleted file mode 100644 index f18ff897e4..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/trails-chains.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef DIONYSUS_TRAILS_CHAINS_H -#define DIONYSUS_TRAILS_CHAINS_H - -#include "ordinary-persistence.h" - -template -struct ChainsVisitor: public EmptyVisitor -{ - template - void chain_initialized(Chain& c) { } - - void addto(typename Field::Element m, Index cl) {} - void reduction_finished() {} -}; - - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.h deleted file mode 100644 index e9423099aa..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.h +++ /dev/null @@ -1,142 +0,0 @@ -#ifndef DIONYSUS_ZIGZAG_PERSISTENCE_H -#define DIONYSUS_ZIGZAG_PERSISTENCE_H - -#include -#include -#include - -#include -#include - -#include "sparse-row-matrix.h" - -namespace dionysus -{ - -namespace ba = boost::adaptors; - -template> -class ZigzagPersistence -{ - static_assert(std::is_signed::value, "Index type used in ZigzagPersistence must be a *signed* integer"); - - public: - typedef Field_ Field; - typedef Index_ Index; - typedef Comparison_ Comparison; - - typedef SparseRowMatrix RowMatrix; - typedef SparseRowMatrix DequeRowMatrix; - typedef typename RowMatrix::IndexPair IndexPair; - typedef typename RowMatrix::FieldElement FieldElement; - typedef typename RowMatrix::IndexChain IndexChain; - typedef typename RowMatrix::Column Column; - typedef typename RowMatrix::Row Row; - typedef typename DequeRowMatrix::Column DequeColumn; - typedef typename DequeRowMatrix::Row DequeRow; - - typedef std::unordered_map BirthIndexMap; - - - ZigzagPersistence(const Field& field, - const Comparison& cmp = Comparison()): - Z(field, cmp), C(field, cmp), B(field, cmp), - operations(0), - cell_indices(0), - z_indicies_last(0), - z_indicies_first(-1), - b_indices(0) {} - - template - Index add(const ChainRange& chain) // returns the id of the dying cycle (or unpaired) - { - Index res = add_impl(chain); -#ifdef DIONYSUS_ZIGZAG_DEBUG - check_sorted(); - check_b_cols(); - Z.check_columns(); -#endif - return res; - } - Index remove(Index cell) - { - Index res = remove_impl(cell); -#ifdef DIONYSUS_ZIGZAG_DEBUG - check_sorted(); - check_b_cols(); - Z.check_columns(); -#endif - return res; - } - - struct IsAlive - { - IsAlive(const ZigzagPersistence& zz_): zz(&zz_) {} - bool operator()(const std::pair& x) const { return zz->is_alive(x.first); } - const ZigzagPersistence* zz; - }; - - bool is_alive(Index x) const { return !B.is_low(x); } - - auto alive_ops() const -> decltype(BirthIndexMap() | ba::filtered(IsAlive(*this)) | ba::map_values) - { return birth_index | ba::filtered(IsAlive(*this)) | ba::map_values; } - - auto alive_cycles() const -> decltype(BirthIndexMap() | ba::filtered(IsAlive(*this)) | ba::map_keys) - { return birth_index | ba::filtered(IsAlive(*this)) | ba::map_keys; } - - size_t alive_size() const { return Z.columns().size() - B.columns().size(); } - - void reserve(size_t) {} // here for compatibility only - const Field& field() const { return Z.field(); } - const Comparison& cmp() const { return Z.cmp(); } - - template - static Index row(const Entry& e) { return std::get<0>(e.index()); } - template - static Index col(const Entry& e) { return std::get<1>(e.index()); } - - static - const Index unpaired() { return Reduction::unpaired; } - - const Column& cycle(Index i) const { return Z.col(i); } - - // debug - void check_b_cols() const; - - template - void check_boundaries(const SimplexToIndex& s2i, const IndexToSimplex& i2s) const; - template - void check_cycles(const SimplexToIndex& s2i, const IndexToSimplex& i2s) const; - - Column zb_dot(Index c) const; - - template - Column dc_dot(Index c, const SimplexToIndex& s2i, const IndexToSimplex& i2s) const; - - template - Column boundary(Index i, const SimplexToIndex& s2i, const IndexToSimplex& i2s) const; - - void check_sorted() const; - - private: - template - Index add_impl(const ChainRange& chain); - Index remove_impl(Index cell); - - private: - RowMatrix Z, C; - DequeRowMatrix B; - - BirthIndexMap birth_index; - Index operations; - Index cell_indices; - Index z_indicies_last, z_indicies_first; - Index b_indices; -}; - -} - -#include "zigzag-persistence.hpp" - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.hpp deleted file mode 100644 index 96331a3b15..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.hpp +++ /dev/null @@ -1,541 +0,0 @@ -#include - -template -template -typename dionysus::ZigzagPersistence::Index -dionysus::ZigzagPersistence:: -add_impl(const ChainRange& chain_) -{ - // std::cout << "add(" << cell_indices << ")" << std::endl; - Index op = operations++; - - IndexChain cycles; // chain_ -> Z*cycles - Column z_remainder = Z.reduce(chain_, cycles); - // std::cout << "cycle: "; - // for (auto& v : cycles){ - // std::cout << v.index() << " "; - // } - // std::cout << "\n"; - assert(z_remainder.empty()); - - IndexChain boundaries; - DequeColumn b_remainder = B.reduce(cycles, boundaries); - - // add up columns of C indexed by boundaries - typedef typename Column::value_type Entry; - auto row_cmp = [this](const Entry& e1, const Entry& e2) - { return this->cmp()(row(e1), row(e2)); }; - Column chain; - for (auto& x : boundaries) - Chain::addto(chain, x.element(), C.col(x.index()), field(), row_cmp); - chain.push_back(Entry(field().neg(field().id()), IndexPair(cell_indices++,0))); - - if (b_remainder.empty()) // birth - { - // std::cout << " birth" << std::endl; - Index z_col = z_indicies_last++; - Z.set(z_col, std::move(chain)); - birth_index[z_col] = op; - return unpaired(); - } - else // death - { - // std::cout << " death" << std::endl; - Index b_col = b_indices++; - Index pair = row(b_remainder.back()); - B.set(b_col, std::move(b_remainder)); - C.set(b_col, std::move(chain)); - return birth_index[pair]; - } -} - -template -typename dionysus::ZigzagPersistence::Index -dionysus::ZigzagPersistence:: -remove_impl(Index cell) -{ - //std::cout << "remove(" << cell << ")" << std::endl; - - Index op = operations++; - - typedef typename Column::value_type Entry; - auto row_cmp = [this](const Entry& e1, const Entry& e2) - { return this->cmp()(row(e1), row(e2)); }; - typedef typename DequeColumn::value_type DequeEntry; - auto b_row_cmp = [this](const DequeEntry& e1, const DequeEntry& e2) - { return this->cmp()(row(e1), row(e2)); }; - - IndexChain z_row; - for (auto& x : Z.row(cell)) - z_row.emplace_back(x.element(), col(x)); - - if (z_row.empty()) // birth - { - //std::cout << " birth" << std::endl; - Row& c_row = C.row(cell); - // c_row.front() may not be the first column in order, but that doesn't really matter, does it? (TODO) - auto& c_front = c_row.front(); - - Index j = col(c_front); - Index l = row(B.col(j).back()); - - //std::cout << j << ' ' << l << std::endl; - - // cycle = ZB[j] = DC[j] - Column cycle; - for (auto& x : B.col(j)) - Chain::addto(cycle, x.element(), Z.col(row(x)), field(), row_cmp); - - //std::cout << "Cycle:" << std::endl; - //for (auto& x : cycle) - // std::cout << x.element() << ' ' << row(x) << std::endl; - - // 1: prepend the cycle - Index znew = z_indicies_first--; - Index oth = Z.set(znew, std::move(cycle)); // oth records our collision (used in step 6) - birth_index[znew] = op; - - //std::cout << "znew oth: " << znew << ' ' << oth << std::endl; - //std::cout << "oth column:" << std::endl; - //for (auto& x : Z.col(oth)) - // std::cout << x.element() << ' ' << row(x) << std::endl; - - // 2: prepend the row to B - FieldElement m = field().neg(field().inv(c_front.element())); // m = -1/c - const DequeRow& b_row = B.prepend_row(znew, m, c_row); - //std::cout << "Prepended row with multiplier: " << m << " (" << b_row.size() << ")" << std::endl; - - // 3: subtract C[j] from every C[k] - const Column& Cj = C.col(j); - - // use the copy of c_row in B, since c_row will be modified in the following loop - for (auto it = std::next(b_row.begin()); it != b_row.end(); ++it) - { - Index c = col(*it); - assert(c != j); - //std::cout << "adding to " << c << " in C" << std::endl; - Chain::addto(C.col(c), it->element(), Cj, field(), row_cmp); // using it->element() since b_row = m*c_row - C.fix(c); // old elements got removed via auto_unlink_hook - // we don't need lows in C, so not updating them - } - //std::cout << "Done with step 3" << std::endl; - - // 4: subtract B[j] from every B[k] that has l - // (we don't need to update C because ZB[j] = 0 after step 2) - DequeColumn& Bj = B.col(j); - FieldElement bm = field().neg(field().inv(Bj.back().element())); // bm = -1/B[l,j] - IndexChain Bl_row; // make a copy of Bl_row, since it will be changing - for (auto& x : B.row(l)) - { - if (col(x) == j) - continue; - Bl_row.emplace_back(x.element(), col(x)); - } - for (auto& x : Bl_row) - { - Index c = x.index(); - assert(c != j); - Chain::addto(B.col(c), field().mul(bm, x.element()), Bj, field(), b_row_cmp); - B.fix(c); // old elements got removed via auto_unlink_hook - // l cannot be the low in c, so no need to update lows - } - //std::cout << "Done with step 4" << std::endl; - - // 5: drop row l and column j from B; drop column l from Z; drop column j from C - B.drop_col(j); - assert(B.row(l).empty()); - B.drop_row(l); - Index Zl_low = row(Z.col(l).back()); - Z.drop_col(l); - birth_index.erase(l); - C.drop_col(j); - assert(Z.row(cell).empty()); - assert(C.row(cell).empty()); - C.drop_row(cell); - Z.drop_row(cell); - //std::cout << "Done with step 5" << std::endl; - if (oth == l) // we just dropped our collision in Z - oth = znew; - else - Z.drop_low(Zl_low); - - // 6: reduce Z - std::unordered_map b_changes; // the columns to add in B to apply row changes - Index cur = znew; - while (oth != cur) - { - Column& cur_col = Z.col(cur); - Column& oth_col = Z.col(oth); - assert(row(cur_col.back()) == row(oth_col.back())); - //std::cout << "--- " << cur << " (" << cur_col.size() << ") " << oth << " (" << oth_col.size() << ")" << std::endl; - FieldElement m1 = cur_col.back().element(); - FieldElement m2 = oth_col.back().element(); - FieldElement m2_div_m1 = field().div(m2, m1); - Chain::addto(oth_col, field().neg(m2_div_m1), cur_col, field(), row_cmp); - Z.fix(oth, oth_col); - - // record the changes we need to make in B; - // because there is only one collision in the matrix during the reduction, - // once we use a row as the source, we never revisit it. This means once the row is updated in B, - // we never touch it again, so below record is fine. - for (auto& x : this->B.row(oth)) - b_changes[col(x)].emplace_back(field().mul(x.element(), m2_div_m1), cur, col(x)); - - cur = oth; - Index low = row(oth_col.back()); - if (Z.is_low(low)) - oth = Z.low(low); - //std::cout << "--- -- new low: " << low << ' ' << cur << ' ' << oth << std::endl; - - if (cmp()(oth, cur)) - std::swap(oth, cur); - else - Z.update_low(cur); - } - - // apply changes in B (the complexity here could get ugly) - for (auto& bx : b_changes) - { - std::sort(bx.second.begin(), bx.second.end(), b_row_cmp); - Chain::addto(B.col(bx.first), field().id(), bx.second, field(), b_row_cmp); - B.fix(bx.first); - // no need to update low (additions from bottom up) - } - //std::cout << "Done with step 6" << std::endl; - - return unpaired(); - } - else // death - { - //std::cout << " death" << std::endl; - - auto index_chain_cmp = [this](const typename IndexChain::value_type& e1, const typename IndexChain::value_type& e2) - { return this->cmp()(e1.index(), e2.index()); }; - - // 1: change basis to clear z_row - std::sort(z_row.begin(), z_row.end(), index_chain_cmp); // this adds a log factor, but it makes life easier - Index j = z_row.front().index(); - FieldElement e = z_row.front().element(); - - if (z_row.size() > 1) - { - // figure out the columns we use for reduction - typedef typename IndexChain::const_iterator RowIterator; - std::vector reducers; - reducers.push_back(z_row.begin()); - for (RowIterator it = std::next(z_row.begin()); it != z_row.end(); ++it) - { - Index c = it->index(); - - assert(Z.col_exists(c)); - assert(Z.col_exists(reducers.back()->index())); - if (cmp()(row(Z.col(c).back()), - row(Z.col(reducers.back()->index()).back()))) - reducers.push_back(it); - } - reducers.push_back(z_row.end()); - //std::cout << "reducers.size(): " << reducers.size() << std::endl; - //std::cout << "z_row.size(): " << z_row.size() << std::endl; - - - std::map b_changes; // the rows to add to B - auto add_in_z = [this,&b_changes,&row_cmp,&index_chain_cmp](Index to, Index from, FieldElement m, FieldElement e) - { - //std::cout << " add_in_z: " << from << ' ' << to << std::endl; - - FieldElement mult = this->field().mul(m, e); - assert(Z.col_exists(to)); - assert(Z.col_exists(from)); - Chain::addto(Z.col(to), mult, Z.col(from), this->field(), row_cmp); - assert(!Z.col(to).empty()); - this->Z.fix(to); // NB: rows will be linked in the back, so the iterators are Ok - this->Z.update_low(to); - - // subtract B.row(to) from B.row(from) - IndexChain Bto_row; - for (auto& x : this->B.row(to)) - Bto_row.emplace_back(x.element(), col(x)); - std::sort(Bto_row.begin(), Bto_row.end(), index_chain_cmp); - -#if 0 - for (auto& x : this->B.row(to)) - std::cout << x.element() << ' ' << row(x) << ' ' << col(x) << std::endl; - - std::cout << "---\n"; - - for (auto& x : this->B.row(from)) - std::cout << x.element() << ' ' << row(x) << ' ' << col(x) << std::endl; -#endif - - Chain::addto(b_changes[from], this->field().neg(mult), Bto_row, this->field(), index_chain_cmp); - - // if there is b_changes[to] add it, too - auto it = b_changes.find(to); - if (it != b_changes.end()) - Chain::addto(b_changes[from], this->field().neg(mult), it->second, this->field(), index_chain_cmp); - }; - Index last_low = row(Z.col(reducers[reducers.size() - 2]->index()).back()); - for (int i = reducers.size() - 2; i >= 0; --i) - { - auto rit = reducers[i]; - FieldElement m = field().neg(field().inv(rit->element())); - - for (auto it = std::next(rit); it != reducers[i+1]; ++it) - add_in_z(it->index(), rit->index(), m, it->element()); - - if (static_cast(i + 1) != reducers.size() - 1) - { - auto it = reducers[i+1]; - add_in_z(it->index(), rit->index(), m, it->element()); - } - } - if (reducers.size() > 2) - Z.drop_low(last_low); - - // apply changes in b (the complexity here could get ugly) - // Specifically, transpose b_changes and add it in - std::unordered_map b_changes_transposed; - for (auto& b_row : b_changes) - for (auto& bx : b_row.second) - b_changes_transposed[bx.index()].emplace_back(bx.element(), b_row.first, bx.index()); - - for (auto& b_col : b_changes_transposed) - { -#if 0 - std::cout << "Adding:" << std::endl; - for (auto& x : b_col.second) - std::cout << x.element() << ' ' << row(x) << ' ' << col(x) << std::endl; -#endif - Chain::addto(B.col(b_col.first), field().id(), b_col.second, field(), b_row_cmp); - assert(!B.col(b_col.first).empty()); - B.fix(b_col.first); - // no need to update low (additions from bottom up) - } - } // z_row.size() > 1 - - // 2: subtract cycle from every chain in C - const Column& Zj = Z.col(j); - //std::cout << "Zj:" << std::endl; - //for (auto& x : Zj) - // std::cout << x.element() << " * " << row(x) << std::endl; - - IndexChain Ccols; // save the columns in C, we'll be modifying C.row(cell) - for (auto& x : C.row(cell)) - Ccols.emplace_back(x.element(), col(x)); - - for (auto& x : Ccols) - { - Index c = x.index(); - FieldElement m = field().neg(field().div(x.element(), e)); // m = -C[k][cell]/Z[j][cell] - //std::cout << "Adding to C: " << c << std::endl; - Chain::addto(C.col(c), m, Zj, field(), row_cmp); - C.fix(c); - // we don't care about lows in C, so don't update them - } - - // 3: drop - assert(Z.row(cell).size() == 1); - Z.drop_col(j); - assert(Z.row(cell).empty()); - assert(C.row(cell).empty()); - Z.drop_row(cell); - C.drop_row(cell); - assert(B.row(j).empty()); - B.drop_row(j); - - Index birth = birth_index[j]; - birth_index.erase(j); - - return birth; - } -} - - -/* debug routines */ -template -void -dionysus::ZigzagPersistence:: -check_b_cols() const -{ - // check that entries in B refer to existing Z columns - bool stop = false; - for (auto& b : B.columns()) - for (auto& x : b.second) - if (!Z.col_exists(row(x))) - { - std::cout << "B refers to a non-existent column in Z: " << row(x) << std::endl; - stop = true; - } - if (stop) - assert(0); -} - -template -template -void -dionysus::ZigzagPersistence:: -check_cycles(const SimplexToIndex& s2i, const IndexToSimplex& i2s) const -{ - typedef typename Column::value_type Entry; - auto row_cmp = [this](const Entry& e1, const Entry& e2) - { return this->cmp()(row(e1), row(e2)); }; - - for (auto& z : Z.columns()) - { - Column res; - for (auto& x : z.second) - { - Column bdry = boundary(row(x), s2i, i2s); - Chain::addto(res, x.element(), bdry, field(), row_cmp); - } - assert(res.empty()); - } -} - -template -template -void -dionysus::ZigzagPersistence:: -check_boundaries(const SimplexToIndex& s2i, const IndexToSimplex& i2s) const -{ - check_cycles(s2i, i2s); - - for (auto& x : B.columns()) - if (!C.col_exists(x.first)) - { - std::cout << x.first << " in B, but not in C" << std::endl; - assert(0); - } - - for (auto& x : C.columns()) - if (!B.col_exists(x.first)) - { - std::cout << x.first << " in B, but not in C" << std::endl; - assert(0); - } - - for (auto& x : B.columns()) - { - auto zb = zb_dot(x.first); - auto dc = dc_dot(x.first, s2i, i2s); - - auto it_zb = zb.begin(), - it_dc = dc.begin(); - for (; it_zb != zb.end(); ++it_zb, ++it_dc) - { - if (it_zb->element() != it_dc->element() || row(*it_zb) != row(*it_dc)) - { - std::cout << "Boundary mismatch: " << x.first << std::endl; - std::cout << "===" << std::endl; - for (auto& x : zb) - std::cout << " " << x.element() << ' ' << row(x) << std::endl; - for (auto& y : B.col(x.first)) - { - std::cout << " " << y.element() << " * " << row(y) << std::endl; - for (auto& z : Z.col(row(y))) - std::cout << " " << z.element() << ' ' << row(z) << std::endl; - std::cout << " ---" << std::endl; - } - std::cout << "===" << std::endl; - for (auto& x : dc) - std::cout << " " << x.element() << ' ' << row(x) << std::endl; - for (auto& y : C.col(x.first)) - { - std::cout << " " << y.element() << " * " << row(y) << std::endl; - for (auto& z : boundary(row(y), s2i, i2s)) - std::cout << " " << z.element() << ' ' << row(z) << std::endl; - std::cout << " ---" << std::endl; - } - assert(0); - } - } - if (it_zb != zb.end() || it_dc != dc.end()) - { - std::cout << "zb.end() doesn't match dc.end()" << std::endl; - assert(0); - } - } -} - -template -typename dionysus::ZigzagPersistence::Column -dionysus::ZigzagPersistence:: -zb_dot(Index c) const -{ - typedef typename Column::value_type Entry; - auto row_cmp = [this](const Entry& e1, const Entry& e2) - { return this->cmp()(row(e1), row(e2)); }; - Column res; - for (auto& x : B.col(c)) - Chain::addto(res, x.element(), Z.col(row(x)), field(), row_cmp); - - return res; -} - -template -template -typename dionysus::ZigzagPersistence::Column -dionysus::ZigzagPersistence:: -dc_dot(Index c, const SimplexToIndex& s2i, const IndexToSimplex& i2s) const -{ - typedef typename Column::value_type Entry; - auto row_cmp = [this](const Entry& e1, const Entry& e2) - { return this->cmp()(row(e1), row(e2)); }; - Column res; - for (auto& x : C.col(c)) - { - Column bdry = boundary(row(x), s2i, i2s); - Chain::addto(res, x.element(), bdry, field(), row_cmp); - } - return res; -} - -template -template -typename dionysus::ZigzagPersistence::Column -dionysus::ZigzagPersistence:: -boundary(Index i, const SimplexToIndex& s2i, const IndexToSimplex& i2s) const -{ - typedef typename Column::value_type Entry; - auto row_cmp = [this](const Entry& e1, const Entry& e2) - { return this->cmp()(row(e1), row(e2)); }; - Column bdry; - auto s = i2s(i); - for (auto y : s.boundary(field())) - bdry.emplace_back(y.element(), s2i(y.index()), 0); - std::sort(bdry.begin(), bdry.end(), row_cmp); - return bdry; -} - -template -void -dionysus::ZigzagPersistence:: -check_sorted() const -{ - typedef typename Column::value_type Entry; - auto row_cmp = [this](const Entry& e1, const Entry& e2) - { return this->cmp()(row(e1), row(e2)); }; - typedef typename DequeColumn::value_type DequeEntry; - auto b_row_cmp = [this](const DequeEntry& e1, const DequeEntry& e2) - { return this->cmp()(row(e1), row(e2)); }; - - for (auto& x : Z.columns()) - if (!std::is_sorted(x.second.begin(), x.second.end(), row_cmp)) - { - std::cout << "Z column not sorted: " << x.first << std::endl; - assert(0); - } - for (auto& x : C.columns()) - if (!std::is_sorted(x.second.begin(), x.second.end(), row_cmp)) - { - std::cout << "C column not sorted: " << x.first << std::endl; - assert(0); - } - for (auto& x : B.columns()) - if (!std::is_sorted(x.second.begin(), x.second.end(), b_row_cmp)) - { - std::cout << "B column not sorted: " << x.first << std::endl; - assert(0); - } -} - diff --git a/src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.cpp b/src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.cpp deleted file mode 100644 index 5217a4f38c..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.cpp +++ /dev/null @@ -1,206 +0,0 @@ -#include "fzz.h" - -#include - -// phat headers -// wrapper algorithm that computes the persistence pairs of a given boundary matrix using a specified algorithm -#include - -// main data structure (choice affects performance) -#include -#include - -// algorithm (choice affects performance) -#include -#include -#include -#include - -namespace FZZ { - -template -class VecHash { -public: - size_t operator()(const std::vector& v) const; -}; - -template -size_t VecHash - ::operator()(const std::vector& v) const { - - std::size_t seed = 0; - - for (auto e : v) { boost::hash_combine(seed, e); } - - return seed; -} - -template -class VecEqual { -public: - bool operator()(const std::vector& v1, - const std::vector& v2) const; -}; - -template -bool VecEqual - ::operator()(const std::vector& v1, - const std::vector& v2) const { - - if (v1.size() != v2.size()) { return false; } - - for (unsigned int i = 0; i < v1.size(); i ++) { - if (v1[i] != v2[i]) { - return false; - } - } - - return true; -} - -typedef std::unordered_map< Simplex, Integer, - VecHash, VecEqual > SimplexIdMap; - -void getBoundaryChainPhat(const std::vector &id_maps, - const Simplex &simp, std::vector &bound_c) { - - bound_c.clear(); - - if (simp.size() <= 1) { return; } - - bound_c.reserve(simp.size()); - - Simplex bound_simp(simp.begin()+1, simp.end()); - bound_c.push_back(id_maps.at(bound_simp.size() - 1).at(bound_simp)); - - for (unsigned int i = 0; i < simp.size()-1; ++i) { - bound_simp[i] = simp[i]; - bound_c.push_back(id_maps.at(bound_simp.size() - 1).at(bound_simp)); - } - - std::sort(bound_c.begin(), bound_c.end()); -} - -inline Integer getDim(const std::vector &bound_c) { - if (bound_c.size() == 0) { return 0; } - return bound_c.size() - 1; -} - -void FastZigzag::compute(const std::vector &filt_simp, - const std::vector &filt_op, - std::vector< std::tuple > *persistence) { - - orig_f_add_id.clear(); - orig_f_del_id.clear(); - persistence->clear(); - - simp_num = 0; - Integer max_dim = 0; - for (unsigned int i = 0; i < filt_op.size(); ++i) { - if (filt_op[i]) { - ++simp_num; - if (static_cast(filt_simp[i].size()) - 1 > max_dim) { max_dim = filt_simp[i].size() - 1; } - } - } - - std::vector bound_c; - // phat::boundary_matrix< phat::vector_vector > bound_chains; - phat::boundary_matrix< phat::bit_tree_pivot_column > bound_chains; - bound_chains.set_num_cols(simp_num * 2 + 1); - - // Add the Omega vertex for the coning - bound_chains.set_col(0, bound_c); - bound_chains.set_dim(0, 0); - - orig_f_add_id.reserve(simp_num); - orig_f_del_id.reserve(simp_num); - - std::vector del_ids; - del_ids.reserve(simp_num); - - std::vector *p_id_maps = new std::vector(max_dim+1); - std::vector &id_maps = *p_id_maps; - - Integer orig_f_id = 0; - Integer s_id = 1; - - for (unsigned int i = 0; i < filt_simp.size(); ++i) { - const Simplex &simp = filt_simp[i]; - - if (filt_op[i]) { - getBoundaryChainPhat(id_maps, simp, bound_c); - bound_chains.set_col(s_id, bound_c); - bound_chains.set_dim(s_id, getDim(bound_c)); - - // assert(s_id == bound_chains.size()-1); - id_maps.at(simp.size() - 1)[simp] = s_id; - orig_f_add_id.push_back(orig_f_id); - s_id ++; - } else { - del_ids.push_back(id_maps.at(simp.size() - 1)[simp]); - id_maps.at(simp.size() - 1).erase(simp); - orig_f_del_id.push_back(orig_f_id); - } - - orig_f_id ++; - } - - for (Integer i = id_maps.size() - 1; i >= 0; -- i) { - for (const auto &it : id_maps.at(i)) { - del_ids.push_back(it.second); - orig_f_del_id.push_back(orig_f_id); - orig_f_id ++; - } - } - - assert(del_ids.size() == s_id-1); - delete p_id_maps; - - assert(simp_num == del_ids.size()); - - std::vector cone_sid(simp_num+1); - - for (auto del_id_it = del_ids.rbegin(); del_id_it != del_ids.rend(); ++del_id_it) { - bound_c.clear(); - bound_c.push_back(*del_id_it); - - std::vector orig_bound_c; - bound_chains.get_col(*del_id_it, orig_bound_c); - - if (orig_bound_c.size() == 0) { - bound_c.push_back(0); - } else { - for (auto bsimp : orig_bound_c) { - // assert(cone_sid[bsimp] >= 0); - bound_c.push_back(cone_sid[bsimp]); - } - } - - std::sort(bound_c.begin(), bound_c.end()); - - bound_chains.set_col(s_id, bound_c); - bound_chains.set_dim(s_id, getDim(bound_c)); - - cone_sid[*del_id_it] = s_id; - - s_id ++; - } - - phat::persistence_pairs pairs; - phat::compute_persistence_pairs< phat::twist_reduction >( pairs, bound_chains ); - - for (phat::index idx = 0; idx < pairs.get_num_pairs(); idx++) { - Integer b = pairs.get_pair(idx).first; - Integer d = pairs.get_pair(idx).second - 1; - Integer p = bound_chains.get_dim(b); - - if (d < simp_num) { mapOrdIntv(b, d); } - else { mapRelExtIntv(p, b, d); } - - if (b > static_cast(filt_simp.size())) { continue; } - if (d > static_cast(filt_simp.size())) { d = filt_simp.size(); } - persistence->emplace_back(b, d, p); - } -} - -} // namespace FZZ { diff --git a/src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.h b/src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.h deleted file mode 100644 index 8613bc3793..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef _FZZ_H_ -#define _FZZ_H_ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace FZZ { - -typedef int Integer; -typedef std::vector Simplex; - -class FastZigzag { -public: - /* - 'filt_simp' and 'filt_op' should have the same length which altogether - specify the input zigzag filtration. 'filt_simp' specifies the simplices - being added or deleted (following the order of the filtration) and - 'filt_op' specifies whether it's an addition (true) or deletion (false). - 'persistence' returns the barcode, with the first element of the tuple - being the birth, the second element being the death, and the third - being the dimension. - */ - void compute( - const std::vector &filt_simp, - const std::vector &filt_op, - std::vector< std::tuple > *persistence); - -private: - void mapOrdIntv(Integer &b, Integer &d) { - // assert(b-1 > 0); - // assert(d < orig_f_add_id.size()); - - // Up-down interval is same, - // so directly map to interval of input filtration - b = orig_f_add_id[b-1] + 1; - d = orig_f_add_id[d]; - } - - void mapRelExtIntv(Integer &p, Integer &b, Integer &d) { - // assert(d >= simp_num); - - if (b > simp_num) { // Open-closed - // Map to up-down interval - std::swap(b, d); - b = 3*simp_num - b; - d = 3*simp_num - d; - p --; - - // Map to interval of input filtration - b = orig_f_del_id[b-1-simp_num] + 1; - d = orig_f_del_id[d-simp_num]; - } else { // Closed-closed - // Map to up-down interval - d = 3*simp_num - d-1; - - // Map to interval of input filtration - b = orig_f_add_id[b-1]; - d = orig_f_del_id[d-simp_num]; - - if (b < d) { - b = b+1; - } else { - std::swap(b, d); - b = b+1; - p = p-1; - } - } - } - -private: - // 'orig_f_add_id' and 'orig_f_del_id' form a mapping - // from the up-down filtration to the original filtration - std::vector orig_f_add_id; - std::vector orig_f_del_id; - - Integer simp_num; -}; - -} - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/chunk_reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/chunk_reduction.h deleted file mode 100644 index 179702312f..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/chunk_reduction.h +++ /dev/null @@ -1,223 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - class chunk_reduction { - public: - enum column_type { GLOBAL - , LOCAL_POSITIVE - , LOCAL_NEGATIVE }; - - public: - template< typename Representation > - void operator() ( boundary_matrix< Representation >& boundary_matrix ) { - - - const index nr_columns = boundary_matrix.get_num_cols(); - if( omp_get_max_threads( ) > nr_columns ) - omp_set_num_threads( 1 ); - - const dimension max_dim = boundary_matrix.get_max_dim(); - - std::vector< index > lowest_one_lookup( nr_columns, -1 ); - std::vector < column_type > column_type( nr_columns, GLOBAL ); - std::vector< char > is_active( nr_columns, false ); - - const index chunk_size = omp_get_max_threads() == 1 ? (index)sqrt( (double)nr_columns ) : nr_columns / omp_get_max_threads(); - - std::vector< index > chunk_boundaries; - for( index cur_boundary = 0; cur_boundary < nr_columns; cur_boundary += chunk_size ) - chunk_boundaries.push_back( cur_boundary ); - chunk_boundaries.push_back( nr_columns ); - - for( dimension cur_dim = max_dim; cur_dim >= 1; cur_dim-- ) { - // Phase 1: Reduce chunks locally -- 1st pass - #pragma omp parallel for schedule( guided, 1 ) - for( index chunk_id = 0; chunk_id < (index)chunk_boundaries.size() - 1; chunk_id++ ) - _local_chunk_reduction( boundary_matrix, lowest_one_lookup, column_type, cur_dim, - chunk_boundaries[ chunk_id ], chunk_boundaries[ chunk_id + 1 ], chunk_boundaries[ chunk_id ] ); - boundary_matrix.sync(); - - // Phase 1: Reduce chunks locally -- 2nd pass - #pragma omp parallel for schedule( guided, 1 ) - for( index chunk_id = 1; chunk_id < (index)chunk_boundaries.size( ) - 1; chunk_id++ ) - _local_chunk_reduction( boundary_matrix, lowest_one_lookup, column_type, cur_dim, - chunk_boundaries[ chunk_id ], chunk_boundaries[ chunk_id + 1 ], chunk_boundaries[ chunk_id - 1 ] ); - boundary_matrix.sync( ); - } - - // get global columns - std::vector< index > global_columns; - for( index cur_col_idx = 0; cur_col_idx < nr_columns; cur_col_idx++ ) - if( column_type[ cur_col_idx ] == GLOBAL ) - global_columns.push_back( cur_col_idx ); - - // get active columns - #pragma omp parallel for - for( index idx = 0; idx < (index)global_columns.size(); idx++ ) - is_active[ global_columns[ idx ] ] = true; - _get_active_columns( boundary_matrix, lowest_one_lookup, column_type, global_columns, is_active ); - - // Phase 2+3: Simplify columns and reduce them - for( dimension cur_dim = max_dim; cur_dim >= 1; cur_dim-- ) { - // Phase 2: Simplify columns - std::vector< index > temp_col; - #pragma omp parallel for schedule( guided, 1 ), private( temp_col ) - for( index idx = 0; idx < (index)global_columns.size(); idx++ ) - if( boundary_matrix.get_dim( global_columns[ idx ] ) == cur_dim ) - _global_column_simplification( global_columns[ idx ], boundary_matrix, lowest_one_lookup, column_type, is_active, temp_col ); - boundary_matrix.sync(); - - // Phase 3: Reduce columns - for( index idx = 0; idx < (index)global_columns.size(); idx++ ) { - index cur_col = global_columns[ idx ]; - if( boundary_matrix.get_dim( cur_col ) == cur_dim && column_type[ cur_col ] == GLOBAL ) { - index lowest_one = boundary_matrix.get_max_index( cur_col ); - while( lowest_one != -1 && lowest_one_lookup[ lowest_one ] != -1 ) { - boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); - lowest_one = boundary_matrix.get_max_index( cur_col ); - } - if( lowest_one != -1 ) { - lowest_one_lookup[ lowest_one ] = cur_col; - boundary_matrix.clear( lowest_one ); - } - boundary_matrix.finalize( cur_col ); - } - } - } - - boundary_matrix.sync(); - } - - protected: - template< typename Representation > - void _local_chunk_reduction( boundary_matrix< Representation >& boundary_matrix - , std::vector& lowest_one_lookup - , std::vector< column_type >& column_type - , const dimension cur_dim - , const index chunk_begin - , const index chunk_end - , const index row_begin ) { - - for( index cur_col = chunk_begin; cur_col < chunk_end; cur_col++ ) { - if( column_type[ cur_col ] == GLOBAL && boundary_matrix.get_dim( cur_col ) == cur_dim ) { - index lowest_one = boundary_matrix.get_max_index( cur_col ); - while( lowest_one != -1 && lowest_one >= row_begin && lowest_one_lookup[ lowest_one ] != -1 ) { - boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); - lowest_one = boundary_matrix.get_max_index( cur_col ); - } - if( lowest_one >= row_begin ) { - lowest_one_lookup[ lowest_one ] = cur_col; - column_type[ cur_col ] = LOCAL_NEGATIVE; - column_type[ lowest_one ] = LOCAL_POSITIVE; - boundary_matrix.clear( lowest_one ); - boundary_matrix.finalize( cur_col ); - } - } - } - } - - template< typename Representation > - void _get_active_columns( const boundary_matrix< Representation >& boundary_matrix - , const std::vector< index >& lowest_one_lookup - , const std::vector< column_type >& column_type - , const std::vector< index >& global_columns - , std::vector< char >& is_active ) { - - const index nr_columns = boundary_matrix.get_num_cols(); - std::vector< char > finished( nr_columns, false ); - - std::vector< std::pair < index, index > > stack; - std::vector< index > cur_col_values; - #pragma omp parallel for schedule( guided, 1 ), private( stack, cur_col_values ) - for( index idx = 0; idx < (index)global_columns.size(); idx++ ) { - bool pop_next = false; - index start_col = global_columns[ idx ]; - stack.push_back( std::pair< index, index >( start_col, -1 ) ); - while( !stack.empty() ) { - index cur_col = stack.back().first; - index prev_col = stack.back().second; - if( pop_next ) { - stack.pop_back(); - pop_next = false; - if( prev_col != -1 ) { - if( is_active[ cur_col ] ) { - is_active[ prev_col ] = true; - } - if( prev_col == stack.back().first ) { - finished[ prev_col ] = true; - pop_next = true; - } - } - } else { - pop_next = true; - boundary_matrix.get_col( cur_col, cur_col_values ); - for( index idx = 0; idx < (index) cur_col_values.size(); idx++ ) { - index cur_row = cur_col_values[ idx ]; - if( ( column_type[ cur_row ] == GLOBAL ) ) { - is_active[ cur_col ] = true; - } else if( column_type[ cur_row ] == LOCAL_POSITIVE ) { - index next_col = lowest_one_lookup[ cur_row ]; - if( next_col != cur_col && !finished[ cur_col ] ) { - stack.push_back( std::make_pair( next_col, cur_col ) ); - pop_next = false; - } - } - } - } - } - } - } - - template< typename Representation > - void _global_column_simplification( const index col_idx - , boundary_matrix< Representation >& boundary_matrix - , const std::vector< index >& lowest_one_lookup - , const std::vector< column_type >& column_type - , const std::vector< char >& is_active - , std::vector< index >& temp_col ) - { - temp_col.clear(); - while( !boundary_matrix.is_empty( col_idx ) ) { - index cur_row = boundary_matrix.get_max_index( col_idx ); - switch( column_type[ cur_row ] ) { - case GLOBAL: - temp_col.push_back( cur_row ); - boundary_matrix.remove_max( col_idx ); - break; - case LOCAL_NEGATIVE: - boundary_matrix.remove_max( col_idx ); - break; - case LOCAL_POSITIVE: - if( is_active[ lowest_one_lookup[ cur_row ] ] ) - boundary_matrix.add_to( lowest_one_lookup[ cur_row ], col_idx ); - else - boundary_matrix.remove_max( col_idx ); - break; - } - } - std::reverse( temp_col.begin(), temp_col.end() ); - boundary_matrix.set_col( col_idx, temp_col ); - } - }; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/row_reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/row_reduction.h deleted file mode 100644 index cdd1a8fd18..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/row_reduction.h +++ /dev/null @@ -1,56 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - class row_reduction { - public: - template< typename Representation > - void operator() ( boundary_matrix< Representation >& boundary_matrix ) { - - const index nr_columns = boundary_matrix.get_num_cols(); - std::vector< std::vector< index > > lowest_one_lookup( nr_columns ); - - for( index cur_col = nr_columns - 1; cur_col >= 0; cur_col-- ) { - if( !boundary_matrix.is_empty( cur_col ) ) - lowest_one_lookup[ boundary_matrix.get_max_index( cur_col ) ].push_back( cur_col ); - - if( !lowest_one_lookup[ cur_col ].empty() ) { - boundary_matrix.clear( cur_col ); - boundary_matrix.finalize( cur_col ); - std::vector< index >& cols_with_cur_lowest = lowest_one_lookup[ cur_col ]; - index source = *min_element( cols_with_cur_lowest.begin(), cols_with_cur_lowest.end() ); - for( index idx = 0; idx < (index)cols_with_cur_lowest.size(); idx++ ) { - index target = cols_with_cur_lowest[ idx ]; - if( target != source && !boundary_matrix.is_empty( target ) ) { - boundary_matrix.add_to( source, target ); - if( !boundary_matrix.is_empty( target ) ) { - index lowest_one_of_target = boundary_matrix.get_max_index( target ); - lowest_one_lookup[ lowest_one_of_target ].push_back( target ); - } - } - } - } - } - } - }; -} \ No newline at end of file diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/spectral_sequence_reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/spectral_sequence_reduction.h deleted file mode 100644 index bf442e6089..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/spectral_sequence_reduction.h +++ /dev/null @@ -1,80 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - class spectral_sequence_reduction { - public: - template< typename Representation > - void operator () ( boundary_matrix< Representation >& boundary_matrix ) { - - const index nr_columns = boundary_matrix.get_num_cols(); - std::vector< index > lowest_one_lookup( nr_columns, -1 ); - - //const index num_stripes = (index) sqrt( (double)nr_columns ); - const index num_stripes = omp_get_max_threads(); - - index block_size = ( nr_columns % num_stripes == 0 ) ? nr_columns / num_stripes : block_size = nr_columns / num_stripes + 1; - - std::vector< std::vector< index > > unreduced_cols_cur_pass( num_stripes ); - std::vector< std::vector< index > > unreduced_cols_next_pass( num_stripes ); - - for( index cur_dim = boundary_matrix.get_max_dim(); cur_dim >= 1 ; cur_dim-- ) { - #pragma omp parallel for schedule( guided, 1 ) - for( index cur_stripe = 0; cur_stripe < num_stripes; cur_stripe++ ) { - index col_begin = cur_stripe * block_size; - index col_end = std::min( (cur_stripe+1) * block_size, nr_columns ); - for( index cur_col = col_begin; cur_col < col_end; cur_col++ ) - if( boundary_matrix.get_dim( cur_col ) == cur_dim && boundary_matrix.get_max_index( cur_col ) != -1 ) - unreduced_cols_cur_pass[ cur_stripe ].push_back( cur_col ); - } - for( index cur_pass = 0; cur_pass < num_stripes; cur_pass++ ) { - boundary_matrix.sync(); - #pragma omp parallel for schedule( guided, 1 ) - for( int cur_stripe = 0; cur_stripe < num_stripes; cur_stripe++ ) { - index row_begin = (cur_stripe - cur_pass) * block_size; - index row_end = row_begin + block_size; - unreduced_cols_next_pass[ cur_stripe ].clear(); - for( index idx = 0; idx < (index)unreduced_cols_cur_pass[ cur_stripe ].size(); idx++ ) { - index cur_col = unreduced_cols_cur_pass[ cur_stripe ][ idx ]; - index lowest_one = boundary_matrix.get_max_index( cur_col ); - while( lowest_one != -1 && lowest_one >= row_begin && lowest_one < row_end && lowest_one_lookup[ lowest_one ] != -1 ) { - boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); - lowest_one = boundary_matrix.get_max_index( cur_col ); - } - if( lowest_one != -1 ) { - if( lowest_one >= row_begin && lowest_one < row_end ) { - lowest_one_lookup[ lowest_one ] = cur_col; - boundary_matrix.clear( lowest_one ); - boundary_matrix.finalize( cur_col ); - } else { - unreduced_cols_next_pass[ cur_stripe ].push_back( cur_col ); - } - } - } - unreduced_cols_next_pass[ cur_stripe ].swap( unreduced_cols_cur_pass[ cur_stripe ] ); - } - } - } - } - }; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/standard_reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/standard_reduction.h deleted file mode 100644 index e490a5e0d1..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/standard_reduction.h +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - class standard_reduction { - public: - template< typename Representation > - void operator() ( boundary_matrix< Representation >& boundary_matrix ) { - - const index nr_columns = boundary_matrix.get_num_cols(); - std::vector< index > lowest_one_lookup( nr_columns, -1 ); - - for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { - index lowest_one = boundary_matrix.get_max_index( cur_col ); - while( lowest_one != -1 && lowest_one_lookup[ lowest_one ] != -1 ) { - boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); - lowest_one = boundary_matrix.get_max_index( cur_col ); - } - if( lowest_one != -1 ) { - lowest_one_lookup[ lowest_one ] = cur_col; - } - boundary_matrix.finalize( cur_col ); - } - } - }; -} - diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/twist_reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/twist_reduction.h deleted file mode 100644 index 2357df0256..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/twist_reduction.h +++ /dev/null @@ -1,51 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - class twist_reduction { - public: - template< typename Representation > - void operator () ( boundary_matrix< Representation >& boundary_matrix ) { - - const index nr_columns = boundary_matrix.get_num_cols(); - std::vector< index > lowest_one_lookup( nr_columns, -1 ); - - for( index cur_dim = boundary_matrix.get_max_dim(); cur_dim >= 1 ; cur_dim-- ) { - for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { - if( boundary_matrix.get_dim( cur_col ) == cur_dim ) { - index lowest_one = boundary_matrix.get_max_index( cur_col ); - while( lowest_one != -1 && lowest_one_lookup[ lowest_one ] != -1 ) { - boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); - lowest_one = boundary_matrix.get_max_index( cur_col ); - } - if( lowest_one != -1 ) { - lowest_one_lookup[ lowest_one ] = cur_col; - boundary_matrix.clear( lowest_one ); - } - boundary_matrix.finalize( cur_col ); - } - } - } - } - }; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/boundary_matrix.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/boundary_matrix.h deleted file mode 100644 index 10c66cca13..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/boundary_matrix.h +++ /dev/null @@ -1,343 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -// interface class for the main data structure -- implementations of the interface can be found in ./representations -namespace phat { - template< class Representation = bit_tree_pivot_column > - class boundary_matrix - { - - protected: - Representation rep; - - // interface functions -- actual implementation and complexity depends on chosen @Representation template - public: - // get overall number of columns in boundary_matrix - index get_num_cols() const { return rep._get_num_cols(); } - - // set overall number of columns in boundary_matrix - void set_num_cols( index nr_of_columns ) { rep._set_num_cols( nr_of_columns ); } - - // get dimension of given index - dimension get_dim( index idx ) const { return rep._get_dim( idx ); } - - // set dimension of given index - void set_dim( index idx, dimension dim ) { rep._set_dim( idx, dim ); } - - // replaces content of @col with boundary of given index - void get_col( index idx, column& col ) const { col.clear(); rep._get_col( idx, col ); } - - // set column @idx to the values contained in @col - void set_col( index idx, const column& col ) { rep._set_col( idx, col ); } - - // true iff boundary of given column is empty - bool is_empty( index idx ) const { return rep._is_empty( idx ); } - - // largest index of given column (new name for lowestOne()) -- NOT thread-safe - index get_max_index( index idx ) const { return rep._get_max_index( idx ); } - - // removes maximal index from given column - void remove_max( index idx ) { rep._remove_max( idx ); } - - // adds column @source to column @target' - void add_to( index source, index target ) { rep._add_to( source, target ); } - - // clears given column - void clear( index idx ) { rep._clear( idx ); } - - // finalizes given column - void finalize( index idx ) { rep._finalize( idx ); } - - // syncronizes all internal data structures -- has to be called before and after any multithreaded access! - void sync() { rep._sync(); } - - // info functions -- independent of chosen 'Representation' - public: - // maximal dimension - dimension get_max_dim() const { - dimension cur_max_dim = 0; - for( index idx = 0; idx < get_num_cols(); idx++ ) - cur_max_dim = get_dim( idx ) > cur_max_dim ? get_dim( idx ) : cur_max_dim; - return cur_max_dim; - } - - // number of nonzero rows for given column @idx - index get_num_rows( index idx ) const { - column cur_col; - get_col( idx, cur_col ); - return cur_col.size(); - } - - // maximal number of nonzero rows of all columns - index get_max_col_entries() const { - index max_col_entries = -1; - const index nr_of_columns = get_num_cols(); - for( index idx = 0; idx < nr_of_columns; idx++ ) - max_col_entries = get_num_rows( idx ) > max_col_entries ? get_num_rows( idx ) : max_col_entries; - return max_col_entries; - } - - // maximal number of nonzero cols of all rows - index get_max_row_entries() const { - size_t max_row_entries = 0; - const index nr_of_columns = get_num_cols(); - std::vector< std::vector< index > > transposed_matrix( nr_of_columns ); - column temp_col; - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { - get_col( cur_col, temp_col ); - for( index idx = 0; idx < (index)temp_col.size(); idx++) - transposed_matrix[ temp_col[ idx ] ].push_back( cur_col ); - } - for( index idx = 0; idx < nr_of_columns; idx++ ) - max_row_entries = transposed_matrix[ idx ].size() > max_row_entries ? transposed_matrix[ idx ].size() : max_row_entries; - return max_row_entries; - } - - // overall number of entries in the matrix - index get_num_entries() const { - index number_of_nonzero_entries = 0; - const index nr_of_columns = get_num_cols(); - for( index idx = 0; idx < nr_of_columns; idx++ ) - number_of_nonzero_entries += get_num_rows( idx ); - return number_of_nonzero_entries; - } - - // operators / constructors - public: - boundary_matrix() {}; - - template< class OtherRepresentation > - boundary_matrix( const boundary_matrix< OtherRepresentation >& other ) { - *this = other; - } - - template< typename OtherRepresentation > - bool operator==( const boundary_matrix< OtherRepresentation >& other_boundary_matrix ) const { - const index number_of_columns = this->get_num_cols(); - - if( number_of_columns != other_boundary_matrix.get_num_cols() ) - return false; - - column temp_col; - column other_temp_col; - for( index idx = 0; idx < number_of_columns; idx++ ) { - this->get_col( idx, temp_col ); - other_boundary_matrix.get_col( idx, other_temp_col ); - if( temp_col != other_temp_col || this->get_dim( idx ) != other_boundary_matrix.get_dim( idx ) ) - return false; - } - return true; - } - - template< typename OtherRepresentation > - bool operator!=( const boundary_matrix< OtherRepresentation >& other_boundary_matrix ) const { - return !( *this == other_boundary_matrix ); - } - - template< typename OtherRepresentation > - boundary_matrix< Representation >& operator=( const boundary_matrix< OtherRepresentation >& other ) - { - const index nr_of_columns = other.get_num_cols(); - this->set_num_cols( nr_of_columns ); - column temp_col; - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { - this->set_dim( cur_col, other.get_dim( cur_col ) ); - other.get_col( cur_col, temp_col ); - this->set_col( cur_col, temp_col ); - } - - // by convention, always return *this - return *this; - } - - // I/O -- independent of chosen 'Representation' - public: - - // initializes boundary_matrix from (vector, vector) pair -- untested - template< typename index_type, typename dimemsion_type > - void load_vector_vector( const std::vector< std::vector< index_type > >& input_matrix, const std::vector< dimemsion_type >& input_dims ) { - const index nr_of_columns = (index)input_matrix.size(); - this->set_num_cols( nr_of_columns ); - column temp_col; - #pragma omp parallel for private( temp_col ) - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { - this->set_dim( cur_col, (dimension)input_dims[ cur_col ] ); - - index num_rows = input_matrix[ cur_col ].size(); - temp_col.resize( num_rows ); - for( index cur_row = 0; cur_row < num_rows; cur_row++ ) - temp_col[ cur_row ] = (index)input_matrix[ cur_col ][ cur_row ]; - this->set_col( cur_col, temp_col ); - } - } - - template< typename index_type, typename dimemsion_type > - void save_vector_vector( std::vector< std::vector< index_type > >& output_matrix, std::vector< dimemsion_type >& output_dims ) { - const index nr_of_columns = get_num_cols(); - output_matrix.resize( nr_of_columns ); - output_dims.resize( nr_of_columns ); - column temp_col; - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { - output_dims[ cur_col ] = (dimemsion_type)get_dim( cur_col ); - get_col( cur_col, temp_col ); - index num_rows = temp_col.size(); - output_matrix[ cur_col ].clear(); - output_matrix[ cur_col ].resize( num_rows ); - for( index cur_row = 0; cur_row < num_rows; cur_row++ ) - output_matrix[ cur_col ][ cur_row ] = (index_type)temp_col[ cur_row ]; - } - } - - - // Loads the boundary_matrix from given file in ascii format - // Format: each line represents a column, first number is dimension, other numbers are the content of the column. - // Ignores empty lines and lines starting with a '#'. - bool load_ascii( std::string filename ) { - // first count number of columns: - std::string cur_line; - std::ifstream dummy( filename .c_str() ); - if( dummy.fail() ) - return false; - - index number_of_columns = 0; - while( getline( dummy, cur_line ) ) { - cur_line.erase(cur_line.find_last_not_of(" \t\n\r\f\v") + 1); - if( cur_line != "" && cur_line[ 0 ] != '#' ) - number_of_columns++; - - } - this->set_num_cols( number_of_columns ); - dummy.close(); - - std::ifstream input_stream( filename.c_str() ); - if( input_stream.fail() ) - return false; - - column temp_col; - index cur_col = -1; - while( getline( input_stream, cur_line ) ) { - cur_line.erase(cur_line.find_last_not_of(" \t\n\r\f\v") + 1); - if( cur_line != "" && cur_line[ 0 ] != '#' ) { - cur_col++; - std::stringstream ss( cur_line ); - - int64_t temp_dim; - ss >> temp_dim; - this->set_dim( cur_col, (dimension) temp_dim ); - - int64_t temp_index; - temp_col.clear(); - while( ss.good() ) { - ss >> temp_index; - temp_col.push_back( (index)temp_index ); - } - std::sort( temp_col.begin(), temp_col.end() ); - this->set_col( cur_col, temp_col ); - } - } - - input_stream.close(); - return true; - } - - // Saves the boundary_matrix to given file in ascii format - // Format: each line represents a column, first number is dimension, other numbers are the content of the column - bool save_ascii( std::string filename ) { - std::ofstream output_stream( filename.c_str() ); - if( output_stream.fail() ) - return false; - - const index nr_columns = this->get_num_cols(); - column tempCol; - for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { - output_stream << (int64_t)this->get_dim( cur_col ); - this->get_col( cur_col, tempCol ); - for( index cur_row_idx = 0; cur_row_idx < (index)tempCol.size(); cur_row_idx++ ) - output_stream << " " << tempCol[ cur_row_idx ]; - output_stream << std::endl; - } - - output_stream.close(); - return true; - } - - // Loads boundary_matrix from given file - // Format: nr_columns % dim1 % N1 % row1 row2 % ...% rowN1 % dim2 % N2 % ... - bool load_binary( std::string filename ) - { - std::ifstream input_stream( filename.c_str( ), std::ios_base::binary | std::ios_base::in ); - if( input_stream.fail( ) ) - return false; - - int64_t nr_columns; - input_stream.read( (char*)&nr_columns, sizeof( int64_t ) ); - this->set_num_cols( (index)nr_columns ); - - column temp_col; - for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { - int64_t cur_dim; - input_stream.read( (char*)&cur_dim, sizeof( int64_t ) ); - this->set_dim( cur_col, (dimension)cur_dim ); - int64_t nr_rows; - input_stream.read( (char*)&nr_rows, sizeof( int64_t ) ); - temp_col.resize( ( std::size_t )nr_rows ); - for( index idx = 0; idx < nr_rows; idx++ ) { - int64_t cur_row; - input_stream.read( (char*)&cur_row, sizeof( int64_t ) ); - temp_col[ idx ] = (index)cur_row; - } - this->set_col( cur_col, temp_col ); - } - - input_stream.close( ); - return true; - } - - // Saves the boundary_matrix to given file in binary format - // Format: nr_columns % dim1 % N1 % row1 row2 % ...% rowN1 % dim2 % N2 % ... - bool save_binary( std::string filename ) - { - std::ofstream output_stream( filename.c_str( ), std::ios_base::binary | std::ios_base::out ); - if( output_stream.fail( ) ) - return false; - - const int64_t nr_columns = this->get_num_cols( ); - output_stream.write( (char*)&nr_columns, sizeof( int64_t ) ); - column tempCol; - for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { - int64_t cur_dim = this->get_dim( cur_col ); - output_stream.write( (char*)&cur_dim, sizeof( int64_t ) ); - this->get_col( cur_col, tempCol ); - int64_t cur_nr_rows = tempCol.size( ); - output_stream.write( (char*)&cur_nr_rows, sizeof( int64_t ) ); - for( index cur_row_idx = 0; cur_row_idx < (index)tempCol.size( ); cur_row_idx++ ) { - int64_t cur_row = tempCol[ cur_row_idx ]; - output_stream.write( (char*)&cur_row, sizeof( int64_t ) ); - } - } - - output_stream.close( ); - return true; - } - }; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/compute_persistence_pairs.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/compute_persistence_pairs.h deleted file mode 100644 index 48be65c28e..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/compute_persistence_pairs.h +++ /dev/null @@ -1,128 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include -#include -#include - -namespace phat { - // Extracts persistence pairs in separate dimensions from a reduced - // boundary matrix representing ``double`` filtration. The pairs - // give persistent relative homology of the pair of filtrations. - // TODO: Use it with standard reduction algorithm (no template option). - template< typename ReductionAlgorithm, typename Representation > - void compute_relative_persistence_pairs(std::vector& pairs, boundary_matrix& boundary_matrix, const std::map& L) { - ReductionAlgorithm reduce; - reduce(boundary_matrix); - std::map free; - std::map invL; - for (std::map::const_iterator it = L.begin(); it != L.end(); ++it) { invL[it->second] = it->first; } - for (std::vector::iterator it = pairs.begin(); it != pairs.end(); ++it) { it->clear(); } - for (index idx = 0; idx < boundary_matrix.get_num_cols(); ++idx) { - int dimension = boundary_matrix.get_dim(idx); - if (L.find(idx) != L.end()) { ++dimension; } - free[idx] = true; - if (!boundary_matrix.is_empty(idx)) { - index birth = boundary_matrix.get_max_index(idx); - index death = idx; - pairs[dimension-1].append_pair(birth, death); - free[birth] = false; - free[death] = false; - } else { - // This is an L-simplex and a (dimension+1)-dimensional cycle - if (L.find(idx) != L.end()) { - assert(dimension < pairs.size()); - pairs[dimension].append_pair(idx, -1); - } - } - } - for (std::map::iterator it = free.begin(); it != free.end(); ++it) { - if (it->second) { - int dimension = boundary_matrix.get_dim(it->first); - if (invL.find(it->first) == invL.end() && L.find(it->first) == L.end()) { - assert(dimension < pairs.size()); - pairs[dimension].append_pair(it->first, -1); - } - } - } - } - - // Extracts persistence pairs in separate dimensions; expects a d-dimensional vector of persistent_pairs - template< typename ReductionAlgorithm, typename Representation > - void compute_persistence_pairs(std::vector& pairs, boundary_matrix& boundary_matrix) { - ReductionAlgorithm reduce; - reduce(boundary_matrix); - std::map free; - for (std::vector::iterator it = pairs.begin(); it != pairs.end(); ++it) { it->clear(); } - for (index idx = 0; idx < boundary_matrix.get_num_cols(); ++idx) { - int dimension = boundary_matrix.get_dim(idx); - free[idx] = true; - if (!boundary_matrix.is_empty(idx)) { - index birth = boundary_matrix.get_max_index(idx); - index death = idx; - pairs[dimension-1].append_pair(birth, death); - // Cannot be of the form (a, infinity) - free[birth] = false; - free[death] = false; - } - } - for (std::map::iterator it = free.begin(); it != free.end(); ++it) { - if (it->second) { - int dimension = boundary_matrix.get_dim(it->first); - pairs[dimension].append_pair(it->first, -1); - } - } - } - - template< typename ReductionAlgorithm, typename Representation > - void compute_persistence_pairs( persistence_pairs& pairs, boundary_matrix< Representation >& boundary_matrix ) { - ReductionAlgorithm reduce; - reduce( boundary_matrix ); - pairs.clear(); - for( index idx = 0; idx < boundary_matrix.get_num_cols(); idx++ ) { - if( !boundary_matrix.is_empty( idx ) ) { - index birth = boundary_matrix.get_max_index( idx ); - index death = idx; - pairs.append_pair( birth, death ); - } - } - } - - template< typename ReductionAlgorithm, typename Representation > - void compute_persistence_pairs_dualized( persistence_pairs& pairs, boundary_matrix< Representation >& boundary_matrix ) { - - dualize( boundary_matrix ); - compute_persistence_pairs< ReductionAlgorithm >( pairs, boundary_matrix ); - dualize_persistence_pairs( pairs, boundary_matrix.get_num_cols() ); - } - - template< typename Representation > - void compute_persistence_pairs( persistence_pairs& pairs, boundary_matrix< Representation >& boundary_matrix ) { - phat::compute_persistence_pairs< twist_reduction >( pairs, boundary_matrix ); - } - - - template< typename Representation > - void compute_persistence_pairs_dualized( persistence_pairs& pairs, boundary_matrix< Representation >& boundary_matrix ) { - compute_persistence_pairs_dualized< twist_reduction >( pairs, boundary_matrix ); - } - -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/dualize.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/dualize.h deleted file mode 100644 index 3ffedf875f..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/dualize.h +++ /dev/null @@ -1,74 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include -#include - - -namespace phat { - template< typename Representation > - void dualize( boundary_matrix< Representation >& boundary_matrix ) { - - std::vector< dimension > dual_dims; - std::vector< std::vector< index > > dual_matrix; - - index nr_of_columns = boundary_matrix.get_num_cols(); - dual_matrix.resize( nr_of_columns ); - dual_dims.resize( nr_of_columns ); - - std::vector< index > dual_sizes( nr_of_columns, 0 ); - - column temp_col; - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { - boundary_matrix.get_col( cur_col, temp_col ); - for( index idx = 0; idx < (index)temp_col.size(); idx++) - dual_sizes[ nr_of_columns - 1 - temp_col[ idx ] ]++; - } - - #pragma omp parallel for - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) - dual_matrix[cur_col].reserve(dual_sizes[cur_col]); - - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { - boundary_matrix.get_col( cur_col, temp_col ); - for( index idx = 0; idx < (index)temp_col.size(); idx++) - dual_matrix[ nr_of_columns - 1 - temp_col[ idx ] ].push_back( nr_of_columns - 1 - cur_col ); - } - - const dimension max_dim = boundary_matrix.get_max_dim(); - #pragma omp parallel for - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) - dual_dims[ nr_of_columns - 1 - cur_col ] = max_dim - boundary_matrix.get_dim( cur_col ); - - #pragma omp parallel for - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) - std::reverse( dual_matrix[ cur_col ].begin(), dual_matrix[ cur_col ].end() ); - - boundary_matrix.load_vector_vector( dual_matrix, dual_dims ); - } - - void dualize_persistence_pairs( persistence_pairs& pairs, const index n ) { - for (index i = 0; i < pairs.get_num_pairs(); ++i) { - std::pair< index, index > pair = pairs.get_pair( i ); - pairs.set_pair( i , n - 1 - pair.second, n - 1 - pair.first); - } - } -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/misc.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/misc.h deleted file mode 100644 index fb5c07acb0..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/misc.h +++ /dev/null @@ -1,75 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -// STL includes -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// VS2008 and below unfortunately do not support stdint.h -#if defined(_MSC_VER)&& _MSC_VER < 1600 - typedef __int8 int8_t; - typedef unsigned __int8 uint8_t; - typedef __int16 int16_t; - typedef unsigned __int16 uint16_t; - typedef __int32 int32_t; - typedef unsigned __int32 uint32_t; - typedef __int64 int64_t; - typedef unsigned __int64 uint64_t; -#else - #include -#endif - -// basic types. index can be changed to int32_t to save memory on small instances -namespace phat { - typedef int64_t index; - typedef int8_t dimension; - typedef std::vector< index > column; -} - -// OpenMP (proxy) functions -#if defined _OPENMP - #include -#else - #define omp_get_thread_num() 0 - #define omp_get_max_threads() 1 - #define omp_get_num_threads() 1 - void omp_set_num_threads( int ) {}; - #include - #define omp_get_wtime() (float)clock() / (float)CLOCKS_PER_SEC -#endif - -#include - - - diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/thread_local_storage.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/thread_local_storage.h deleted file mode 100644 index d0b5332bc1..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/thread_local_storage.h +++ /dev/null @@ -1,52 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include - -// should ideally be equal to the cache line size of the CPU -#define PHAT_TLS_SPACING_FACTOR 64 - -// ThreadLocalStorage with some spacing to avoid "false sharing" (see wikipedia) -template< typename T > -class thread_local_storage -{ -public: - - thread_local_storage() : per_thread_storage( omp_get_max_threads() * PHAT_TLS_SPACING_FACTOR ) {}; - - T& operator()() { - return per_thread_storage[ omp_get_thread_num() * PHAT_TLS_SPACING_FACTOR ]; - } - - const T& operator()() const { - return per_thread_storage[ omp_get_thread_num() * PHAT_TLS_SPACING_FACTOR ]; - } - - T& operator[]( int tid ) { - return per_thread_storage[ tid * PHAT_TLS_SPACING_FACTOR ]; - } - - const T& operator[]( int tid ) const { - return per_thread_storage[ tid * PHAT_TLS_SPACING_FACTOR ]; - } - -protected: - std::vector< T > per_thread_storage; -}; diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/persistence_pairs.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/persistence_pairs.h deleted file mode 100644 index eafc6389e2..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/persistence_pairs.h +++ /dev/null @@ -1,155 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include - -namespace phat { - class persistence_pairs { - - protected: - std::vector< std::pair< index, index > > pairs; - - public: - index get_num_pairs() const { - return (index)pairs.size(); - } - - void append_pair( index birth, index death ) { - pairs.push_back( std::make_pair( birth, death ) ); - } - - std::pair< index, index > get_pair( index idx ) const { - return pairs[ idx ]; - } - - void set_pair( index idx, index birth, index death ) { - pairs[ idx ] = std::make_pair( birth, death ); - } - - void clear() { - pairs.clear(); - } - - void sort() { - std::sort( pairs.begin(), pairs.end() ); - } - - // Loads the persistence pairs from given file in asci format - // Format: nr_pairs % newline % birth1 % death1 % newline % birth2 % death2 % newline ... - bool load_ascii( std::string filename ) { - std::ifstream input_stream( filename.c_str() ); - if( input_stream.fail() ) - return false; - - int64_t nr_pairs; - input_stream >> nr_pairs; - pairs.clear(); - for( index idx = 0; idx < nr_pairs; idx++ ) { - int64_t birth; - input_stream >> birth; - int64_t death; - input_stream >> death; - append_pair( (index)birth, (index)death ); - } - - input_stream.close(); - return true; - } - - // Saves the persistence pairs to given file in binary format - // Format: nr_pairs % newline % birth1 % death1 % newline % birth2 % death2 % newline ... - bool save_ascii( std::string filename ) { - std::ofstream output_stream( filename.c_str() ); - if( output_stream.fail() ) - return false; - - this->sort(); - output_stream << get_num_pairs() << std::endl; - for( std::size_t idx = 0; idx < pairs.size(); idx++ ) { - output_stream << pairs[idx].first << " " << pairs[idx].second << std::endl; - } - - output_stream.close(); - return true; - } - - // Loads the persistence pairs from given file in binary format - // Format: nr_pairs % birth1 % death1 % birth2 % death2 ... - bool load_binary( std::string filename ) { - std::ifstream input_stream( filename.c_str(), std::ios_base::binary | std::ios_base::in ); - if( input_stream.fail() ) - return false; - - int64_t nr_pairs; - input_stream.read( (char*)&nr_pairs, sizeof( int64_t ) ); - for( index idx = 0; idx < nr_pairs; idx++ ) { - int64_t birth; - input_stream.read( (char*)&birth, sizeof( int64_t ) ); - int64_t death; - input_stream.read( (char*)&death, sizeof( int64_t ) ); - append_pair( (index)birth, (index)death ); - } - - input_stream.close(); - return true; - } - - // Saves the persistence pairs to given file in binary format - // Format: nr_pairs % birth1 % death1 % birth2 % death2 ... - bool save_binary( std::string filename ) { - std::ofstream output_stream( filename.c_str(), std::ios_base::binary | std::ios_base::out ); - if( output_stream.fail() ) - return false; - - this->sort(); - int64_t nr_pairs = get_num_pairs(); - output_stream.write( (char*)&nr_pairs, sizeof( int64_t ) ); - for( std::size_t idx = 0; idx < pairs.size(); idx++ ) { - int64_t birth = pairs[ idx ].first; - output_stream.write( (char*)&birth, sizeof( int64_t ) ); - int64_t death = pairs[ idx ].second; - output_stream.write( (char*)&death, sizeof( int64_t ) ); - } - - output_stream.close(); - return true; - } - - bool operator==( persistence_pairs& other_pairs ) { - this->sort(); - other_pairs.sort(); - if( pairs.size() != (std::size_t)other_pairs.get_num_pairs() ) - return false; - - for( index idx = 0; idx < (index)pairs.size(); idx++ ) - if( get_pair( idx ) != other_pairs.get_pair( idx ) ) - return false; - - return true; - } - - bool operator!=( persistence_pairs& other_pairs ) { - return !( *this == other_pairs ); - } - }; - - - -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/abstract_pivot_column.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/abstract_pivot_column.h deleted file mode 100644 index e16d7a5d13..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/abstract_pivot_column.h +++ /dev/null @@ -1,102 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - - // Note: We could even make the rep generic in the underlying Const representation - // But I cannot imagine that anything else than vector> would - // make sense - template< typename PivotColumn > - class abstract_pivot_column : public vector_vector { - - protected: - typedef vector_vector Base; - typedef PivotColumn pivot_col; - - // For parallization purposes, it could be more than one full column - mutable thread_local_storage< pivot_col > pivot_cols; - mutable thread_local_storage< index > idx_of_pivot_cols; - - pivot_col& get_pivot_col() const { - return pivot_cols(); - } - - bool is_pivot_col( index idx ) const { - return idx_of_pivot_cols() == idx; - } - - void release_pivot_col() { - index idx = idx_of_pivot_cols(); - if( idx != -1 ) { - this->matrix[ idx ].clear(); - pivot_cols().get_col_and_clear( this->matrix[ idx ] ); - } - idx_of_pivot_cols() = -1; - } - - void make_pivot_col( index idx ) { - release_pivot_col(); - idx_of_pivot_cols() = idx; - get_pivot_col().add_col( matrix[ idx ] ); - } - - public: - - void _set_num_cols( index nr_of_cols ) { - #pragma omp parallel for - for( int tid = 0; tid < omp_get_num_threads(); tid++ ) { - pivot_cols[ tid ].init( nr_of_cols ); - idx_of_pivot_cols[ tid ] = -1; - } - Base::_set_num_cols( nr_of_cols ); - } - - void _add_to( index source, index target ) { - if( !is_pivot_col( target ) ) - make_pivot_col( target ); - get_pivot_col().add_col( matrix[source] ); - } - - void _sync() { - #pragma omp parallel for - for( int tid = 0; tid < omp_get_num_threads(); tid++ ) - release_pivot_col(); - } - - void _get_col( index idx, column& col ) const { is_pivot_col( idx ) ? get_pivot_col().get_col( col ) : Base::_get_col( idx, col ); } - - bool _is_empty( index idx ) const { return is_pivot_col( idx ) ? get_pivot_col().is_empty() : Base::_is_empty( idx ); } - - index _get_max_index( index idx ) const { return is_pivot_col( idx ) ? get_pivot_col().get_max_index() : Base::_get_max_index( idx ); } - - void _clear( index idx ) { is_pivot_col( idx ) ? get_pivot_col().clear() : Base::_clear( idx ); } - - void _set_col( index idx, const column& col ) { is_pivot_col( idx ) ? get_pivot_col().set_col( col ) : Base::_set_col( idx, col ); } - - void _remove_max( index idx ) { is_pivot_col( idx ) ? get_pivot_col().remove_max() : Base::_remove_max( idx ); } - - void finalize( index idx ) { Base::_finalize( idx ); } - }; -} - - diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/bit_tree_pivot_column.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/bit_tree_pivot_column.h deleted file mode 100644 index 4d48e8853d..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/bit_tree_pivot_column.h +++ /dev/null @@ -1,165 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Hubert Wagner - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - - // This is a bitset indexed with a 64-ary tree. Each node in the index - // has 64 bits; i-th bit says that the i-th subtree is non-empty. - // Supports practically O(1), inplace, zero-allocation: insert, remove, max_element - // and clear in O(number of ones in the bitset). - // 'add_index' is still the real bottleneck in practice. - class bit_tree_column - { - protected: - - size_t offset; // data[i + offset] = ith block of the data-bitset - typedef uint64_t block_type; - std::vector< block_type > data; - - - size_t debrujin_magic_table[ 64 ]; - - enum { block_size_in_bits = 64 }; - enum { block_shift = 6 }; - - // Some magic: http://graphics.stanford.edu/~seander/bithacks.html - // Gets the position of the rightmost bit of 'x'. 0 means the most significant bit. - // (-x)&x isolates the rightmost bit. - // The whole method is much faster than calling log2i, and very comparable to using ScanBitForward/Reverse intrinsic, - // which should be one CPU instruction, but is not portable. - size_t rightmost_pos( const block_type value ) const { - return 64 - 1 - debrujin_magic_table[ ( (value & (-(int64_t)value) ) * 0x07EDD5E59A4E28C2 ) >> 58 ]; - } - - public: - - void init( index num_cols ) { - int64_t n = 1; // in case of overflow - int64_t bottom_blocks_needed = ( num_cols + block_size_in_bits - 1 ) / block_size_in_bits; - int64_t upper_blocks = 1; - - // How many blocks/nodes of index needed to index the whole bitset? - while( n * block_size_in_bits < bottom_blocks_needed ) { - n *= block_size_in_bits; - upper_blocks += n; - } - - offset = upper_blocks; - data.resize( upper_blocks + bottom_blocks_needed, 0 ); - - std::size_t temp_array[ 64 ] = { - 63, 0, 58, 1, 59, 47, 53, 2, - 60, 39, 48, 27, 54, 33, 42, 3, - 61, 51, 37, 40, 49, 18, 28, 20, - 55, 30, 34, 11, 43, 14, 22, 4, - 62, 57, 46, 52, 38, 26, 32, 41, - 50, 36, 17, 19, 29, 10, 13, 21, - 56, 45, 25, 31, 35, 16, 9, 12, - 44, 24, 15, 8, 23, 7, 6, 5 }; - - std::copy( &temp_array[ 0 ], &temp_array[ 64 ], &debrujin_magic_table[ 0 ] ); - } - - index get_max_index() const { - if( !data[ 0 ] ) - return -1; - - size_t n = 0; - size_t newn = 0; - size_t index = 0; - while( newn < data.size() ) { - n = newn; - index = rightmost_pos( data[ n ] ); - newn = ( n << block_shift ) + index + 1; - } - - return ( ( n - offset ) << block_shift ) + index; - } - - bool is_empty() const { - return data[ 0 ] == 0; - } - - void add_index( const size_t entry ) { - const block_type ONE = 1; - const block_type block_modulo_mask = ( ONE << block_shift ) - 1; - size_t index_in_level = entry >> block_shift; - size_t address = index_in_level + offset; - size_t index_in_block = entry & block_modulo_mask; - - block_type mask = ( ONE << ( block_size_in_bits - index_in_block - 1 ) ); - - data[ address ] ^= mask; - - // Check if we reached the root. Also, if anyone else was in this block, we don't need to update the path up. - while( address && !( data[ address ] & ~mask ) ) { - index_in_block = index_in_level & block_modulo_mask; - index_in_level >>= block_shift; - --address; - address >>= block_shift; - mask = ( ONE << ( block_size_in_bits - index_in_block - 1 ) ); - data[ address ] ^= mask; - } - } - - void get_col_and_clear( column &out ) { - index mx = this->get_max_index(); - while( mx != -1 ) { - out.push_back( mx ); - add_index( mx ); - mx = this->get_max_index(); - } - - std::reverse( out.begin(), out.end() ); - } - - void add_col(const column &col) { - for( size_t i = 0; i < col.size(); ++i ) - add_index(col[i]); - } - - void clear() { - index mx = this->get_max_index(); - while( mx != -1 ) { - add_index( mx ); - mx = this->get_max_index(); - } - } - - void remove_max() { - add_index( get_max_index() ); - } - - void set_col( const column& col ) { - clear(); - add_col( col ); - } - - void get_col( column& col ) { - get_col_and_clear( col ); - add_col( col ); - } - }; - - typedef abstract_pivot_column bit_tree_pivot_column; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/full_pivot_column.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/full_pivot_column.h deleted file mode 100644 index c2e9e3c574..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/full_pivot_column.h +++ /dev/null @@ -1,100 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - class full_column { - - protected: - std::priority_queue< index > history; - std::vector< char > is_in_history; - std::vector< char > col_bit_field; - - public: - void init( const index total_size ) { - col_bit_field.resize( total_size, false ); - is_in_history.resize( total_size, false ); - } - - void add_col( const column& col ) { - for( index idx = 0; idx < (index) col.size(); idx++ ) { - add_index( col[ idx ] ); - } - } - - void add_index( const index idx ) { - if( !is_in_history[ idx ] ) { - history.push( idx ); - is_in_history[ idx ] = true; - } - - col_bit_field[ idx ] = !col_bit_field[ idx ]; - } - - index get_max_index() { - while( history.size() > 0 ) { - index topIndex = history.top(); - if( col_bit_field[ topIndex ] ) { - return topIndex; - } else { - history.pop(); - is_in_history[ topIndex ] = false; - } - } - - return -1; - } - - void get_col_and_clear( column& col ) { - while( !is_empty() ) { - col.push_back( get_max_index() ); - add_index( get_max_index() ); - } - std::reverse( col.begin(), col.end() ); - } - - bool is_empty() { - return (get_max_index() == -1); - } - - void clear() { - while( !is_empty() ) - add_index( get_max_index() ); - } - - void remove_max() { - add_index( get_max_index() ); - } - - void set_col( const column& col ) { - clear(); - add_col( col ); - } - - void get_col( column& col ) { - get_col_and_clear( col ); - add_col( col ); - } - }; - - typedef abstract_pivot_column< full_column > full_pivot_column; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/heap_pivot_column.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/heap_pivot_column.h deleted file mode 100644 index 33cd07b40d..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/heap_pivot_column.h +++ /dev/null @@ -1,126 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - class heap_column { - - protected: - std::priority_queue< index > data; - - column temp_col; - index inserts_since_last_prune; - - void prune() - { - temp_col.clear( ); - index max_index = pop_max_index( ); - while( max_index != -1 ) { - temp_col.push_back( max_index ); - max_index = pop_max_index( ); - } - - for( index idx = 0; idx < (index)temp_col.size( ); idx++ ) - data.push( temp_col[ idx ] ); - - inserts_since_last_prune = 0; - } - - index pop_max_index() - { - if( data.empty( ) ) - return -1; - else { - index max_element = data.top( ); - data.pop(); - while( !data.empty( ) && data.top( ) == max_element ) { - data.pop( ); - if( data.empty( ) ) - return -1; - else { - max_element = data.top( ); - data.pop( ); - } - } - return max_element; - } - } - - public: - void init( const index total_size ) { - inserts_since_last_prune = 0; - clear(); - } - - void add_col( const column& col ) { - for( index idx = 0; idx < (index) col.size(); idx++ ) - data.push( col[ idx ] ); - inserts_since_last_prune += col.size( ); - if( 2 * inserts_since_last_prune >( index ) data.size( ) ) - prune(); - } - - index get_max_index() { - index max_element = pop_max_index( ); - if( max_element == -1 ) - return -1; - else { - data.push( max_element ); - return max_element; - } - } - - void get_col_and_clear( column& col ) { - col.clear(); - index max_index = pop_max_index( ); - while( max_index != -1 ) { - col.push_back( max_index ); - max_index = pop_max_index( ); - } - std::reverse( col.begin(), col.end() ); - } - - bool is_empty() { - return get_max_index() == -1; - } - - void clear() { - data = std::priority_queue< index >(); - } - - void remove_max() { - pop_max_index(); - } - - void set_col( const column& col ) { - clear(); - add_col( col ); - } - - void get_col( column& col ) { - get_col_and_clear( col ); - add_col( col ); - } - }; - - typedef abstract_pivot_column< heap_column > heap_pivot_column; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/sparse_pivot_column.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/sparse_pivot_column.h deleted file mode 100644 index 390fd91a99..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/sparse_pivot_column.h +++ /dev/null @@ -1,79 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - class sparse_column { - - protected: - std::set< index > data; - - void add_index( const index idx ) { - std::pair< std::set< index >::iterator, bool > result = data.insert( idx ); - if( result.second == false ) - data.erase( result.first ); - } - - public: - void init( const index total_size ) { - data.clear(); - } - - void add_col( const column& col ) { - for( index idx = 0; idx < (index) col.size(); idx++ ) - add_index( col[ idx ] ); - } - - index get_max_index() { - return data.empty() ? -1 : *data.rbegin(); - } - - void get_col_and_clear( column& col ) { - col.assign( data.begin(), data.end() ); - data.clear(); - } - - bool is_empty() { - return data.empty(); - } - - void clear() { - data.clear(); - } - - void remove_max() { - add_index( get_max_index() ); - } - - void set_col( const column& col ) { - clear(); - add_col( col ); - } - - void get_col( column& col ) { - get_col_and_clear( col ); - add_col( col ); - } - }; - - typedef abstract_pivot_column< sparse_column > sparse_pivot_column; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_heap.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_heap.h deleted file mode 100644 index db0420ff23..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_heap.h +++ /dev/null @@ -1,170 +0,0 @@ -/* Copyright 2013 IST Austria -Contributed by: Jan Reininghaus - -This file is part of PHAT. - -PHAT is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -PHAT 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 Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with PHAT. If not, see . */ - -#pragma once - -#include - -namespace phat { - class vector_heap { - - protected: - std::vector< dimension > dims; - std::vector< column > matrix; - - std::vector< index > inserts_since_last_prune; - - mutable thread_local_storage< column > temp_column_buffer; - - protected: - void _prune( index idx ) - { - column& col = matrix[ idx ]; - column& temp_col = temp_column_buffer(); - temp_col.clear(); - index max_index = _pop_max_index( col ); - while( max_index != -1 ) { - temp_col.push_back( max_index ); - max_index = _pop_max_index( col ); - } - col = temp_col; - std::reverse( col.begin( ), col.end( ) ); - std::make_heap( col.begin( ), col.end( ) ); - inserts_since_last_prune[ idx ] = 0; - } - - index _pop_max_index( index idx ) - { - return _pop_max_index( matrix[ idx ] ); - } - - index _pop_max_index( column& col ) const - { - if( col.empty( ) ) - return -1; - else { - index max_element = col.front( ); - std::pop_heap( col.begin( ), col.end( ) ); - col.pop_back( ); - while( !col.empty( ) && col.front( ) == max_element ) { - std::pop_heap( col.begin( ), col.end( ) ); - col.pop_back( ); - if( col.empty( ) ) - return -1; - else { - max_element = col.front( ); - std::pop_heap( col.begin( ), col.end( ) ); - col.pop_back( ); - } - } - return max_element; - } - } - - public: - // overall number of cells in boundary_matrix - index _get_num_cols( ) const - { - return (index)matrix.size( ); - } - void _set_num_cols( index nr_of_columns ) - { - dims.resize( nr_of_columns ); - matrix.resize( nr_of_columns ); - inserts_since_last_prune.assign( nr_of_columns, 0 ); - } - - // dimension of given index - dimension _get_dim( index idx ) const - { - return dims[ idx ]; - } - void _set_dim( index idx, dimension dim ) - { - dims[ idx ] = dim; - } - - // replaces(!) content of 'col' with boundary of given index - void _get_col( index idx, column& col ) const - { - temp_column_buffer( ) = matrix[ idx ]; - - index max_index = _pop_max_index( temp_column_buffer() ); - while( max_index != -1 ) { - col.push_back( max_index ); - max_index = _pop_max_index( temp_column_buffer( ) ); - } - std::reverse( col.begin( ), col.end( ) ); - } - void _set_col( index idx, const column& col ) - { - matrix[ idx ] = col; - std::make_heap( matrix[ idx ].begin( ), matrix[ idx ].end( ) ); - } - - // true iff boundary of given idx is empty - bool _is_empty( index idx ) const - { - return _get_max_index( idx ) == -1; - } - - // largest row index of given column idx (new name for lowestOne()) - index _get_max_index( index idx ) const - { - column& col = const_cast< column& >( matrix[ idx ] ); - index max_element = _pop_max_index( col ); - col.push_back( max_element ); - std::push_heap( col.begin( ), col.end( ) ); - return max_element; - } - - // removes the maximal index of a column - void _remove_max( index idx ) - { - _pop_max_index( idx ); - } - - // clears given column - void _clear( index idx ) - { - matrix[ idx ].clear( ); - } - - // syncronizes all data structures (essential for openmp stuff) - void _sync( ) {} - - // adds column 'source' to column 'target' - void _add_to( index source, index target ) - { - for( index idx = 0; idx < (index)matrix[ source ].size( ); idx++ ) { - matrix[ target ].push_back( matrix[ source ][ idx ] ); - std::push_heap( matrix[ target ].begin(), matrix[ target ].end() ); - } - inserts_since_last_prune[ target ] += matrix[ source ].size(); - - if( 2 * inserts_since_last_prune[ target ] > ( index )matrix[ target ].size() ) - _prune( target ); - } - - // finalizes given column - void _finalize( index idx ) { - _prune( idx ); - } - - }; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_list.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_list.h deleted file mode 100644 index ca0b5b8e79..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_list.h +++ /dev/null @@ -1,101 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include - -namespace phat { - class vector_list { - - protected: - std::vector< dimension > dims; - std::vector< std::list< index > > matrix; - - public: - // overall number of cells in boundary_matrix - index _get_num_cols() const { - return (index)matrix.size(); - } - void _set_num_cols( index nr_of_columns ) { - dims.resize( nr_of_columns ); - matrix.resize( nr_of_columns ); - } - - // dimension of given index - dimension _get_dim( index idx ) const { - return dims[ idx ]; - } - void _set_dim( index idx, dimension dim ) { - dims[ idx ] = dim; - } - - // replaces(!) content of 'col' with boundary of given index - void _get_col( index idx, column& col ) const { - col.clear(); - col.reserve( matrix[idx].size() ); - std::copy (matrix[idx].begin(), matrix[idx].end(), std::back_inserter(col) ); - } - - void _set_col( index idx, const column& col ) { - matrix[ idx ].clear(); - matrix[ idx ].resize( col.size() ); - std::copy (col.begin(), col.end(), matrix[ idx ].begin() ); - } - - // true iff boundary of given idx is empty - bool _is_empty( index idx ) const { - return matrix[ idx ].empty(); - } - - // largest row index of given column idx (new name for lowestOne()) - index _get_max_index( index idx ) const { - return matrix[ idx ].empty() ? -1 : *matrix[ idx ].rbegin(); - } - - // removes the maximal index of a column - void _remove_max( index idx ) { - std::list< index >::iterator it = matrix[ idx ].end(); - it--; - matrix[ idx ].erase( it ); - } - - // clears given column - void _clear( index idx ) { - matrix[ idx ].clear(); - } - - // syncronizes all data structures (essential for openmp stuff) - void _sync() {} - - // adds column 'source' to column 'target' - void _add_to( index source, index target ) { - std::list< index >& source_col = matrix[ source ]; - std::list< index >& target_col = matrix[ target ]; - std::list< index > temp_col; - target_col.swap( temp_col ); - std::set_symmetric_difference( temp_col.begin(), temp_col.end(), - source_col.begin(), source_col.end(), - std::back_inserter( target_col ) ); - } - - // finalizes given column - void _finalize( index idx ) { - } - }; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_set.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_set.h deleted file mode 100644 index 6878a270c0..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_set.h +++ /dev/null @@ -1,99 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include - -namespace phat { - class vector_set { - - protected: - std::vector< dimension > dims; - std::vector< std::set< index > > matrix; - - public: - // overall number of cells in boundary_matrix - index _get_num_cols() const { - return (index)matrix.size(); - } - void _set_num_cols( index nr_of_columns ) { - dims.resize( nr_of_columns ); - matrix.resize( nr_of_columns ); - } - - // dimension of given index - dimension _get_dim( index idx ) const { - return dims[ idx ]; - } - void _set_dim( index idx, dimension dim ) { - dims[ idx ] = dim; - } - - // replaces(!) content of 'col' with boundary of given index - void _get_col( index idx, column& col ) const { - col.clear(); - col.reserve( matrix[idx].size() ); - std::copy (matrix[idx].begin(), matrix[idx].end(), std::back_inserter(col) ); - } - void _set_col( index idx, const column& col ) { - matrix[ idx ].clear(); - matrix[ idx ].insert( col.begin(), col.end() ); - } - - // true iff boundary of given idx is empty - bool _is_empty( index idx ) const { - return matrix[ idx ].empty(); - } - - // largest row index of given column idx (new name for lowestOne()) - index _get_max_index( index idx ) const { - return matrix[ idx ].empty() ? -1 : *matrix[ idx ].rbegin(); - } - - // removes the maximal index of a column - void _remove_max( index idx ) { - std::set< index >::iterator it = matrix[ idx ].end(); - it--; - matrix[ idx ].erase( it ); - } - - // clears given column - void _clear( index idx ) { - matrix[ idx ].clear(); - } - - // syncronizes all data structures (essential for openmp stuff) - void _sync() {} - - // adds column 'source' to column 'target' - void _add_to( index source, index target ) { - for( std::set< index >::iterator it = matrix[ source ].begin(); it != matrix[ source ].end(); it++ ) { - std::set< index >& col = matrix[ target ]; - std::pair< std::set< index >::iterator, bool > result = col.insert( *it ); - if( !result.second ) - col.erase( result.first ); - } - } - - // finalizes given column - void _finalize( index idx ) { - } - - }; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_vector.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_vector.h deleted file mode 100644 index f111d6b572..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_vector.h +++ /dev/null @@ -1,107 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include - -namespace phat { - class vector_vector { - - protected: - std::vector< dimension > dims; - std::vector< column > matrix; - - thread_local_storage< column > temp_column_buffer; - - public: - // overall number of cells in boundary_matrix - index _get_num_cols() const { - return (index)matrix.size(); - } - void _set_num_cols( index nr_of_columns ) { - dims.resize( nr_of_columns ); - matrix.resize( nr_of_columns ); - } - - // dimension of given index - dimension _get_dim( index idx ) const { - return dims[ idx ]; - } - void _set_dim( index idx, dimension dim ) { - dims[ idx ] = dim; - } - - // replaces(!) content of 'col' with boundary of given index - void _get_col( index idx, column& col ) const { - col = matrix[ idx ]; - } - void _set_col( index idx, const column& col ) { - matrix[ idx ] = col; - } - - // true iff boundary of given idx is empty - bool _is_empty( index idx ) const { - return matrix[ idx ].empty(); - } - - // largest row index of given column idx (new name for lowestOne()) - index _get_max_index( index idx ) const { - return matrix[ idx ].empty() ? -1 : matrix[ idx ].back(); - } - - // removes the maximal index of a column - void _remove_max( index idx ) { - matrix[ idx ].pop_back(); - } - - // clears given column - void _clear( index idx ) { - matrix[ idx ].clear(); - } - - // syncronizes all data structures (essential for openmp stuff) - void _sync() {} - - // adds column 'source' to column 'target' - void _add_to( index source, index target ) { - column& source_col = matrix[ source ]; - column& target_col = matrix[ target ]; - column& temp_col = temp_column_buffer(); - - - size_t new_size = source_col.size() + target_col.size(); - - if (new_size > temp_col.size()) temp_col.resize(new_size); - - std::vector::iterator col_end = std::set_symmetric_difference( target_col.begin(), target_col.end(), - source_col.begin(), source_col.end(), - temp_col.begin() ); - temp_col.erase(col_end, temp_col.end()); - - - target_col.swap(temp_col); - } - - // finalizes given column - void _finalize( index idx ) { - column& col = matrix[ idx ]; - column(col.begin(), col.end()).swap(col); - } - }; -} diff --git a/src/Zigzag_persistence/example/CMakeLists.txt b/src/Zigzag_persistence/example/CMakeLists.txt index 446456e0e5..5f50fbfdbe 100644 --- a/src/Zigzag_persistence/example/CMakeLists.txt +++ b/src/Zigzag_persistence/example/CMakeLists.txt @@ -19,10 +19,6 @@ if(TARGET TBB::tbb) endif() add_test(NAME Zigzag_persistence_example_oscillating_rips_persistence COMMAND $ "2" "3" "5" "10") -add_executable ( comp comparison_for_tests.cpp ) -if(TARGET TBB::tbb) - target_link_libraries(comp TBB::tbb) -endif() diff --git a/src/Zigzag_persistence/example/comparison_for_tests.cpp b/src/Zigzag_persistence/example/comparison_for_tests.cpp deleted file mode 100644 index 2b55bdd648..0000000000 --- a/src/Zigzag_persistence/example/comparison_for_tests.cpp +++ /dev/null @@ -1,392 +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): Hannah Schreiber - * - * Copyright (C) 2023 Inria - * - * Modification(s): - * - YYYY/MM Author: Description of the modification - */ - -#include -#include -#include -#include -#include -#include // for pair -#include -#include - -#include - -#include -#include -#include -#include -#include - -using ST = Gudhi::Simplex_tree; -using Filtration_value = ST::Filtration_value; -using Simplex_handle = ST::Simplex_handle; -using Square = Gudhi::zigzag_persistence::Square_root_edge_modifier; -using ZE = Gudhi::zigzag_persistence::Zigzag_edge; -using ORE = Gudhi::zigzag_persistence::Oscillating_rips_edge_range; -using OR = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range; -using ORv = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range::iterator>; -using Point = std::vector; - -void print_points(const std::vector& points) { - std::cout << "Number of points: " << points.size() << "\n"; - for (const Point& p : points) { - std::cout << "(" << p[0] << ", " << p[1] << ")\n"; - } - std::cout << "\n"; -} - -void print_res(const std::tuple& t, ST& st){ - for (auto v : st.simplex_vertex_range(std::get<0>(t))) std::cout << v << " "; - std::cout << " -- " << std::get<1>(t) << ", " << std::get<2>(t) << "\n"; -} - -std::vector build_point_cloud(unsigned int numberOfPoints, int seed) { - std::vector finalPoints; - std::set points; - std::random_device dev; - std::mt19937 rng(dev()); - if (seed > -1) rng.seed(seed); - std::uniform_real_distribution dist(0, 10); - - for (unsigned int i = 0; i < numberOfPoints; ++i) { - auto res = points.insert({dist(rng), dist(rng)}); - while (!res.second) { - res = points.insert({dist(rng), dist(rng)}); - } - finalPoints.push_back(*res.first); - } - - // print_points(finalPoints); - - return finalPoints; -} - -void canonical_sort_edge(std::vector& edges) { - // canonical sort of the edges: as much as possible, edges should be removed in - // the reverse order of their insertion. We decide to insert shorted edges first, - // with increasing lexicographical order, and remove larger edges first, with - // decreasing lexicographic order. - - // filtration then dimension, then lex order for insertion - auto edge_cmp = [](const ZE& e1, const ZE& e2) { - if (e1.get_filtration_value() != e2.get_filtration_value()) { - return e1.get_filtration_value() < e2.get_filtration_value(); - } // lower fil first - - if (e1.get_smallest_vertex() == e1.get_biggest_vertex()) { // e1 is a vertex, -> put vertices first - if (e2.get_smallest_vertex() == e2.get_biggest_vertex()) { - return e1.get_smallest_vertex() < e2.get_smallest_vertex(); - } //-> vertex of lower label - else { - return true; - } //-> always vertices before edges - } else { // e1 is an edge - if (e2.get_smallest_vertex() == e2.get_biggest_vertex()) { - return false; - } // e2 vertex, -> put it first - else { // both are edges, lexigraphic compare - if (e1.get_smallest_vertex() != e2.get_smallest_vertex()) { - return e1.get_smallest_vertex() < e2.get_smallest_vertex(); - } // lex order - if (e1.get_biggest_vertex() != e2.get_biggest_vertex()) { - return e1.get_biggest_vertex() < e2.get_biggest_vertex(); - } - return false; // equality - } - } - }; - // the inverse ordering for deletions - auto inv_edge_cmp = [&](const ZE& e1, const ZE& e2) { - if (e1.get_smallest_vertex() == e2.get_smallest_vertex() && e1.get_biggest_vertex() == e2.get_biggest_vertex()) { - return false; - } //== => false - return !(edge_cmp(e1, e2)); // reverse order - }; - // sort sequences of inclusions of same filtration with edge_cmp - // sort sequences of removals of same filtration with inv_edge_cmp - auto beg = edges.begin(); - auto end = edges.begin(); - auto curr_fil = beg->get_filtration_value(); - auto curr_type = beg->get_direction(); - while (beg != edges.end()) { - while (end != edges.end() && end->get_filtration_value() == curr_fil && end->get_direction() == curr_type) { - ++end; - } - if (curr_type) { - sort(beg, end, edge_cmp); - } // sequence of insertions - else { - sort(beg, end, inv_edge_cmp); - } // sequence of removals - beg = end; - curr_fil = beg->get_filtration_value(); - curr_type = beg->get_direction(); - } -} - -void test_edges_comp(const std::vector& points, double nu, double mu, ORE::Order_policy p) -{ - std::vector edges_v1 = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); - - unsigned int i = 0; - for (const auto& e : ORE::get_iterator_range(nu, mu, points, Gudhi::Euclidean_distance(), p)) { - if (i < edges_v1.size()) { - if (!(edges_v1[i] == e)) { - std::cout << "[" << i << "] different:\n"; - std::cout << edges_v1[i].get_smallest_vertex() << ", " << edges_v1[i].get_biggest_vertex() << ", " - << edges_v1[i].get_filtration_value() << ", " << edges_v1[i].get_direction() << "\n"; - std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() - << ", " << e.get_direction() << "\n"; - }/* else { - std::cout << "[" << i << "] same:\n"; - std::cout << edges_v1[i].get_smallest_vertex() << ", " << edges_v1[i].get_biggest_vertex() << ", " - << edges_v1[i].get_filtration_value() << ", " << edges_v1[i].get_direction() << "\n"; - std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() - << ", " << e.get_direction() << "\n"; - } */ - } else { - std::cout << "[" << i << "] too long:\n"; - std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() << ", " - << e.get_direction() << "\n"; - } - ++i; - } -} - -void test_edges_canonical_sort(const std::vector& points, double nu, double mu, ORE::Order_policy p) { - std::vector edges_v1 = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); - std::vector ordered_edges_v1(edges_v1); - canonical_sort_edge(ordered_edges_v1); - - for (unsigned int i = 0; i < edges_v1.size(); ++i) { - if (!(edges_v1[i] == ordered_edges_v1[i])) { - std::cout << "[" << i << "] different:\n"; - std::cout << edges_v1[i].get_smallest_vertex() << ", " << edges_v1[i].get_biggest_vertex() << ", " - << edges_v1[i].get_filtration_value() << ", " << edges_v1[i].get_direction() << "\n"; - std::cout << ordered_edges_v1[i].get_smallest_vertex() << ", " << ordered_edges_v1[i].get_biggest_vertex() << ", " - << ordered_edges_v1[i].get_filtration_value() << ", " << ordered_edges_v1[i].get_direction() << "\n"; - } - } -} - -void test_edges_asymetry(const std::vector& points, double nu, double mu, ORE::Order_policy p) { - auto comp = [](const ZE& e1, const ZE& e2) { - if (e1.get_smallest_vertex() != e2.get_smallest_vertex()) - return e1.get_smallest_vertex() < e2.get_smallest_vertex(); - return e1.get_biggest_vertex() < e2.get_biggest_vertex(); - }; - std::set > curedges(comp); - for (const auto& e : ORE::get_iterator_range(nu, mu, points, Gudhi::Euclidean_distance(), p)) { - if (e.get_direction()) - curedges.insert(e); - else - curedges.erase(e); - } - std::cout << "final state: " << curedges.size() << "\n"; - for (const auto& e : curedges) { - std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() << ", " - << e.get_direction() << "\n"; - } -} - -void test_edges_timings(const std::vector& points, double nu, double mu, ORE::Order_policy p) { -// { -// Gudhi::Clock time1("Vector version"); -// std::vector edges_v1 = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); -// std::cout << edges_v1.size() << "\n"; -// time1.end(); -// std::cout << time1; -// } - - { - Gudhi::Clock time2("Iterator version"); - unsigned int i = 0; - for ([[maybe_unused]] const auto& e : ORE::get_iterator_range(nu, mu, points, Gudhi::Euclidean_distance(), p)) { - ++i; - } - std::cout << i << "\n"; - time2.end(); - std::cout << time2; - } - - { - Gudhi::Clock time1("Vector version"); - std::vector edges_v1 = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); - std::cout << edges_v1.size() << "\n"; - time1.end(); - std::cout << time1; - } -} - -void test_edges(const std::vector& points, double nu, double mu) { - ORE::Order_policy p = ORE::Order_policy::FARTHEST_POINT_ORDERING; - // ORE::Order_policy p = ORE::Order_policy::ALREADY_ORDERED; -// ORE::Order_policy p = ORE::Order_policy::RANDOM_POINT_ORDERING; - - // test_edges_comp(points, nu, mu, p); - // test_edges_canonical_sort(points, nu, mu, p); - // test_edges_asymetry(points, nu, mu, p); - test_edges_timings(points, nu, mu, p); -} - -void test_simplices_print(const std::vector& points, double nu, double mu, int maxDim, ORE::Order_policy p) { - ST st; - - auto start = ORE::begin(nu, mu, points, Gudhi::Euclidean_distance(), p); - auto end = ORE::end(); - for (auto& t : OR::get_iterator_range(start, end, st, maxDim)) { - print_res(t, st); - } -} - -void test_simplices_comp(const std::vector& points, double nu, double mu, int maxDim, ORE::Order_policy p) { - ST stIt; - ST stVec; - - auto startEIt = ORE::begin(nu, mu, points, Gudhi::Euclidean_distance(), p); - auto endEIt = ORE::end(); - auto vec = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); - auto startEVec = vec.begin(); - auto endEVec = vec.end(); - - auto startIt = OR::begin(startEIt, endEIt, stIt, maxDim); - auto endIt = OR::end(); - auto rangeVec = ORv::get_iterator_range(startEVec, endEVec, stVec, maxDim); - auto startVec = rangeVec.begin(); - auto endVec = rangeVec.end(); - unsigned int i = 0; - for (; startIt != endIt && startVec != endVec; ++startIt, ++startVec) { - const auto& tIt = *startIt; - const auto& tVec = *startVec; - if (std::get<1>(tIt) != std::get<1>(tVec) || std::get<2>(tIt) != std::get<2>(tVec)) { - std::cout << "[" << i << "] Different:\n"; - print_res(tIt, stIt); - print_res(tVec, stVec); - return; - } - auto verIt = stIt.simplex_vertex_range(std::get<0>(tIt)); - auto verVec = stVec.simplex_vertex_range(std::get<0>(tVec)); - auto itIt = verIt.begin(); - auto itVec = verVec.begin(); - while (itIt != verIt.end() && itVec != verVec.end()) { - if (*itIt != *itVec) { - std::cout << "[" << i << "] Different:\n"; - print_res(tIt, stIt); - print_res(tVec, stVec); - return; - } - ++itIt; - ++itVec; - } - if (itIt != verIt.end() || itVec != verVec.end()) { - std::cout << "[" << i << "] Different:\n"; - print_res(tIt, stIt); - print_res(tVec, stVec); - return; - } - ++i; - } -} - -void test_simplices_timings(const std::vector& points, double nu, double mu, int maxDim, ORE::Order_policy p) { -// bool dir = false; - { - Gudhi::Clock time1("Vector version"); - ST st; - unsigned int i = 0; - auto vec = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); - // std::cout << vec.size() << "\n"; - auto start = vec.begin(); - auto end = vec.end(); - for ([[maybe_unused]] const auto& t : ORv::get_iterator_range(start, end, st, maxDim)) { - // if (dir != std::get<2>(t)){ - // dir = !dir; - // std::cout << st.num_simplices() << "\n"; - // } - ++i; - } - std::cout << "Number of iterations: " << i << "\n"; - time1.end(); - std::cout << time1; - } - - { - Gudhi::Clock time2("Iterator version"); - ST st; - unsigned int i = 0; - auto start = ORE::begin(nu, mu, points, Gudhi::Euclidean_distance(), p); - auto end = ORE::end(); - for ([[maybe_unused]] const auto& t : OR::get_iterator_range(start, end, st, maxDim)) { - // if (dir != std::get<2>(t)){ - // dir = !dir; - // std::cout << st.num_simplices() << "\n"; - // } - ++i; - } - std::cout << "Number of iterations: " << i << "\n"; - time2.end(); - std::cout << time2; - } - -// { -// Gudhi::Clock time1("Vector version"); -// ST st; -// unsigned int i = 0; -// auto vec = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); -// auto start = vec.begin(); -// auto end = vec.end(); -// for ([[maybe_unused]] const auto& t : ORv::get_iterator_range(start, end, st, maxDim)) { -// ++i; -// } -// std::cout << i << "\n"; -// time1.end(); -// std::cout << time1; -// } -} - -void test_simplices(const std::vector& points, double nu, double mu, int maxDim) { - ORE::Order_policy p = ORE::Order_policy::FARTHEST_POINT_ORDERING; - // ORE::Order_policy p = ORE::Order_policy::ALREADY_ORDERED; -// ORE::Order_policy p = ORE::Order_policy::RANDOM_POINT_ORDERING; - -// test_simplices_print(points, nu, mu, maxDim, p); -// test_simplices_comp(points, nu, mu, maxDim, p); - test_simplices_timings(points, nu, mu, maxDim, p); -} - -int main(int argc, char* const argv[]) { - if (argc != 5 && argc != 6) { - std::cout << "Usage: ./comp nu mu max_dim nomberOfPoints [seed]\n"; - return 0; - } - - double nu = std::stod(argv[1]); - double mu = std::stod(argv[2]); - int maxDim = std::stoi(argv[3]); - unsigned int numberOfPoints = std::stoi(argv[4]); - int seed = -1; - - if (argc == 6) seed = std::stoi(argv[5]); - - std::cout << "nu, mu: " << nu << ", " << mu << "\n"; - std::cout << "max dimension: " << maxDim << "\n"; - std::cout << "number of points: " << numberOfPoints << "\n"; - std::cout << "seed: " << seed << "\n"; - - std::vector points = build_point_cloud(numberOfPoints, seed); - -// test_edges(points, nu, mu); -// test_simplices(points, nu, mu, maxDim); - - Gudhi::zigzag_persistence::compute_oscillating_rips_persistence(points, nu, mu, maxDim); - - return 0; -} From 0c7d3e6e2be4f36df8b01c807abfc26b7abecc8c Mon Sep 17 00:00:00 2001 From: hschreiber Date: Fri, 31 May 2024 15:18:07 +0200 Subject: [PATCH 19/51] generalized to faces instead of simplices --- .../example_simple_zigzag_filtration.cpp | 138 ++-- .../example_zzfiltration_from_file.cpp | 37 +- .../example/zigzag_filtration_example.txt | 57 +- .../include/gudhi/Zigzag_persistence.h | 676 ++++++++---------- .../test/zigzag_persistence_unit_test.cpp | 359 ++-------- 5 files changed, 453 insertions(+), 814 deletions(-) diff --git a/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp b/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp index c58276b7f3..a13b998906 100644 --- a/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp +++ b/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp @@ -12,34 +12,17 @@ #include #include -#include -struct Simplex_tree_options_zigzag_persistence : Gudhi::Simplex_tree_options_minimal { - static const bool store_key = true; -}; - -using ST = Gudhi::Simplex_tree; -using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; -using Vertex_handle = ST::Vertex_handle; -using Filtration_value = ST::Filtration_value; -using interval_filtration = ZP::filtration_value_interval; - -// void print_complex(ZP& zp) { -// std::clog << std::endl << "Current complex:" << std::endl; -// const auto& cpx = zp.get_complex(); -// for (const auto& sh : cpx.complex_simplex_range()) { -// for (auto v : cpx.simplex_vertex_range(sh)) { -// std::cout << v << " "; -// } -// std::cout << " - " << cpx.filtration(sh) << "" << std::endl; -// } -// } +using ZP = Gudhi::zigzag_persistence::Zigzag_persistence<>; +using face_handle = ZP::face_key; +using filtration_value = ZP::filtration_value; +using Interval_filtration = ZP::Filtration_value_interval; void print_barcode(ZP& zp) { std::clog << std::endl << "Current barcode:" << std::endl; for (auto& bar : zp.get_persistence_diagram(0, true)) { std::clog << std::floor(bar.birth()) << " - "; - if (bar.death() == std::numeric_limits::infinity()) { + if (bar.death() == std::numeric_limits::infinity()) { std::clog << "inf"; } else { std::clog << std::floor(bar.death()); @@ -57,39 +40,39 @@ void print_indices(ZP& zp) { } } -std::vector > get_simplices() { - return {{0}, - {1}, - {2}, +std::vector > get_boundaries() { + return {{}, + {}, + {}, {0, 1}, {0, 2}, - {3}, + {}, {1, 2}, - {4}, - {3, 4}, - {5}, - {0, 1, 2}, - {4, 5}, - {3, 5}, - {3, 4, 5}, - {0, 1, 2}, // remove - {3, 4, 5}, // remove - {1, 4}, - {0, 1, 2}, - {2, 4}, - {3, 4, 5}, - {0, 4}, - {0, 2, 4}, - {1, 2, 4}, - {0, 1, 4}, - {3, 4, 5}, // remove - {3, 4}, // remove - {3, 5}, // remove - {0, 1, 2, 4}, - {0, 1, 2, 4}}; // remove + {}, + {5, 7}, + {}, + {3, 4, 6}, + {7, 9}, + {5, 9}, + {8, 11, 12}, + {10}, // remove + {13}, // remove + {1, 7}, + {3, 4, 6}, + {2, 7}, + {8, 11, 12}, + {0, 7}, + {4, 18, 20}, + {6, 16, 18}, + {3, 16, 20}, + {19}, // remove + {8}, // remove + {12}, // remove + {17, 21, 22, 23}, + {27}}; // remove } -std::vector get_filtration_values() { +std::vector get_filtration_values() { return {0, 0, 0, 1, 1, 1, 2, 2, 2, @@ -116,65 +99,32 @@ std::vector get_batch_sizes() { return {14, 2, 8, 3, 1, 1}; } -void one_by_one() { +int main(int argc, char* const argv[]) { + std::clog << "********** Example **********" << std::endl; + ZP zp; - std::vector > simplices = get_simplices(); - std::vector fils = get_filtration_values(); + std::vector > simplices = get_boundaries(); + std::vector fils = get_filtration_values(); std::vector dirs = get_directions(); for (unsigned int i = 0; i < simplices.size(); ++i) { if (i > 0 && dirs[i] != dirs[i - 1]) { - // print_complex(zp); print_barcode(zp); print_indices(zp); } if (dirs[i]) { - zp.insert_simplex(simplices[i], fils[i]); + int dim = simplices[i].size() == 0 ? 0 : simplices[i].size() - 1; + zp.insert_face(i, simplices[i], dim, fils[i]); } else { - zp.remove_simplex(simplices[i], fils[i]); + auto id = simplices[i][0]; + int dim = simplices[id].size() == 0 ? 0 : simplices[id].size() - 1; + zp.remove_face(id, dim, fils[i]); } } - // print_complex(zp); + print_barcode(zp); print_indices(zp); -} - -void in_batches() { - ZP zp; - - std::vector > simplices = get_simplices(); - std::vector fils = get_filtration_values(); - std::vector sizes = get_batch_sizes(); - - unsigned int start; - unsigned int end = 0; - bool dir = true; //first operation has to be an insertion - for (auto s : sizes){ - start = end; - end += s; - if (dir){ - zp.insert_simplices_contiguously(simplices.begin() + start, - simplices.begin() + end, - fils.begin() + start); - } else { - zp.remove_simplices_contiguously(simplices.begin() + start, - simplices.begin() + end, - fils.begin() + start); - } - dir = !dir; - // print_complex(zp); - print_barcode(zp); - print_indices(zp); - } -} - -int main(int argc, char* const argv[]) { - std::clog << "********** Example one_by_one **********" << std::endl; - one_by_one(); - - std::clog << std::endl << "********** Example in_batches **********" << std::endl; - in_batches(); return 0; } diff --git a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp index ac5c1817c8..37ebf7185f 100644 --- a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp +++ b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp @@ -10,20 +10,15 @@ #include #include +#include #include #include -#include -struct Simplex_tree_options_zigzag_persistence : Gudhi::Simplex_tree_options_minimal { - static const bool store_key = true; -}; - -using ST = Gudhi::Simplex_tree; -using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; -using Vertex_handle = ST::Vertex_handle; -using Filtration_value = ST::Filtration_value; -using interval_filtration = ZP::filtration_value_interval; +using ZP = Gudhi::zigzag_persistence::Zigzag_persistence<>; +using id_handle = ZP::face_key; +using filtration_value = ZP::filtration_value; +using Interval_filtration = ZP::Filtration_value_interval; enum lineType : int { INCLUSION, REMOVAL, COMMENT }; @@ -31,7 +26,7 @@ void print_barcode(ZP& zp) { std::clog << std::endl << "Current barcode:" << std::endl; for (auto& bar : zp.get_persistence_diagram(0, true)) { std::clog << std::floor(bar.birth()) << " - "; - if (bar.death() == std::numeric_limits::infinity()) { + if (bar.death() == std::numeric_limits::infinity()) { std::clog << "inf"; } else { std::clog << std::floor(bar.death()); @@ -41,10 +36,10 @@ void print_barcode(ZP& zp) { std::clog << std::endl; } -lineType read_operation(std::string& line, std::vector& vertices, double& timestamp) { +lineType read_operation(std::string& line, std::vector& vertices, double& timestamp) { lineType type; vertices.clear(); - Vertex_handle num; + id_handle num; size_t current = line.find_first_not_of(' ', 0); if (current == std::string::npos) return COMMENT; @@ -98,18 +93,19 @@ int main(int argc, char* const argv[]) { ZP zp; if (file.is_open()) { - std::vector vertices; + std::vector data; + unsigned int id = 0; double timestamp; lineType type; - while (getline(file, line, '\n') && read_operation(line, vertices, timestamp) == COMMENT); + while (getline(file, line, '\n') && read_operation(line, data, timestamp) == COMMENT); double lastTimestamp = timestamp; // first operation has to be an insertion. - zp.insert_simplex(vertices, timestamp); + zp.insert_face(id, data, 0, timestamp); std::cout << line << std::endl; while (getline(file, line, '\n')) { - type = read_operation(line, vertices, timestamp); + type = read_operation(line, data, timestamp); if (type != COMMENT && lastTimestamp != timestamp) { print_barcode(zp); lastTimestamp = timestamp; @@ -117,9 +113,12 @@ int main(int argc, char* const argv[]) { if (type != COMMENT) std::cout << line << std::endl; if (type == INCLUSION) { - zp.insert_simplex(vertices, timestamp); + ++id; + int dim = data.size() == 0 ? 0 : data.size() - 1; + zp.insert_face(id, data, dim, timestamp); } else if (type == REMOVAL) { - zp.remove_simplex(vertices, timestamp); + ++id; + zp.remove_face(data[0], data[1], timestamp); } } print_barcode(zp); diff --git a/src/Zigzag_persistence/example/zigzag_filtration_example.txt b/src/Zigzag_persistence/example/zigzag_filtration_example.txt index db02d5b2d8..e0fd923d9a 100644 --- a/src/Zigzag_persistence/example/zigzag_filtration_example.txt +++ b/src/Zigzag_persistence/example/zigzag_filtration_example.txt @@ -2,45 +2,48 @@ # i: inclusion # r: removal # first value: filtration value -# remaining values: vertices of the simplex to include/remove in ascending order +# remaining values: if inclusion, boundary of the simplex to include in ascending order, +# a number corresponds to the id of the simplex in the boundary +# the ids start at 0 and corresponds to their "arrow number" +# If removal: id of the simplex to remove + dimension # #: comment line -i 0 0 -i 0 1 -i 0 2 +i 0 +i 0 +i 0 i 1 0 1 i 1 0 2 -i 1 3 +i 1 i 2 1 2 -i 2 4 -i 2 3 4 +i 2 +i 2 5 7 -i 3 5 -i 3 0 1 2 -i 3 4 5 -i 3 3 5 +i 3 +i 3 3 4 6 +i 3 7 9 +i 3 5 9 -i 4 3 4 5 +i 4 8 11 12 -r 5 0 1 2 +r 5 10 2 -r 6 3 4 5 -i 6 1 4 -i 6 0 1 2 +r 6 13 2 +i 6 1 7 +i 6 3 4 6 -i 7 2 4 -i 7 3 4 5 -i 7 0 4 -i 7 0 2 4 -i 7 1 2 4 -i 7 0 1 4 +i 7 2 7 +i 7 8 11 12 +i 7 0 7 +i 7 4 18 20 +i 7 6 16 18 +i 7 3 16 20 -r 8 3 4 5 +r 8 19 2 -r 9 3 4 -r 9 3 5 -i 9 0 1 2 4 +r 9 8 1 +r 9 12 1 +i 9 17 21 22 23 -r 10 0 1 2 4 \ No newline at end of file +r 10 27 3 \ No newline at end of file diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h index 00e2d0f0bf..b9a686e0a8 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h @@ -19,24 +19,13 @@ #ifndef ZIGZAG_PERSISTENCE_H_ #define ZIGZAG_PERSISTENCE_H_ -#include -#include -#include -#include -#include -#include - #include -#include #include -#include #include -#include #include -#include +#include #include #include -#include #include #include @@ -44,32 +33,56 @@ namespace Gudhi { namespace zigzag_persistence { +/** + * @brief Options for the internal matrix of @ref Zigzag_persistence. + * + * @tparam column_type Column type of the matrix. + */ +template +struct Zigzag_matrix_options : Gudhi::persistence_matrix::Default_options { + static const bool has_row_access = true; + static const bool has_column_pairings = false; + static const bool has_vine_update = true; + static const bool is_of_boundary_type = false; + static const bool has_map_column_container = true; + static const bool has_removable_columns = true; + static const bool has_removable_rows = true; +}; + +/** + * @brief Default options for @ref Zigzag_persistence. + */ +struct Default_zigzag_options { + using internal_key = int; /**< Face ID used internaly, must be signed. */ + using face_key = int; /**< Face ID used in the given boundaries. */ + using filtration_value = double; /**< Filtration value type. */ + using dimension_type = int; /**< Dimension value type. */ + /** + * @brief Column type use by the internal matrix. + */ + static const Gudhi::persistence_matrix::Column_types column_type = + Gudhi::persistence_matrix::Column_types::INTRUSIVE_LIST; +}; + +//TODO: add the possibility of something else than Z2. Which means that the possibility of vineyards without Z2 +//also needs to be implemented. The theory needs to be done first. /** \class Zigzag_persistence Zigzag_persistence.h gudhi/Zigzag_persistence.h * \brief Class computating the zigzag persistent homology of a zigzag * filtration. Algorithm based on \cite zigzag_reflection. * * \ingroup zigzag_persistence * - * \details The type ZigzagComplex::Simplex_key counts the number of - * insertions and deletions of simplices, which may be large in zigzag persistence and require - * more than 32 bits of storage. The type used (int, long, etc) should be chosen in - * consequence. Simplex_key must be signed. - * - * \tparam ZigzagComplex Complex storing the current simplices. - * \tparam ZigzagPersistenceOptions Options for the matrix used to compute the persistence. + * \tparam ZigzagOptions TODO: concept */ -template > -class Zigzag_persistence -{ +template +class Zigzag_persistence { public: - using Complex = ZigzagComplex; /**< Complex type. */ - using Options = ZigzagPersistenceOptions; /**< Matrix options */ - /*** Types defined in the complex ***/ - using Simplex_key = typename Complex::Simplex_key; /**< Key type, must be signed. */ - using Simplex_handle = typename Complex::Simplex_handle; /**< Simplex ID type in the complex. */ - using Vertex_handle = typename Complex::Vertex_handle; /**< Vertex ID type in the complex. */ - using Filtration_value = typename Complex::Filtration_value; /**< Filtration value type. */ + using Options = ZigzagOptions; + using Matrix_options = Zigzag_matrix_options; /**< Matrix options */ + using internal_key = typename Options::internal_key; + using face_key = typename Options::face_key; + using filtration_value = typename Options::filtration_value; + using dimension_type = typename Options::dimension_type; /** \brief Structure to store persistence intervals by their index values. * @@ -77,24 +90,23 @@ class Zigzag_persistence * closed for finite indices b and d, and open for left-infinite and/or * right-infinite endpoints. */ - template - struct interval { - interval() {} - interval(int dim, value_type b, value_type d) : dim_(dim), b_(b), d_(d) {} - /** Returns the dimension of the homological feature corresponding to the - * interval. */ - int dim() const { return dim_; } // return the homological dimension of the interval + template + struct Interval { + Interval() {} + Interval(int dim, value_type b, value_type d) : dim_(dim), b_(b), d_(d) {} + /** Returns the dimension of the homological feature corresponding to the interval. */ + int dim() const { return dim_; } /** Returns the birth index of the interval.*/ - value_type birth() const { return b_; } // return the birth value + value_type birth() const { return b_; } /** Returns the death index of the interval.*/ - value_type death() const { return d_; } // return the death value + value_type death() const { return d_; } - protected: // note that we don't assume b_ <= d_ - int dim_; // homological dimension + protected: + int dim_; // homological dimension value_type b_; // filtration value associated to birth index value_type d_; // filtration value associated to death index }; - using index_interval = interval; + using Index_interval = Interval; /** \brief Structure to store persistence intervals by their filtration values. * @@ -102,16 +114,15 @@ class Zigzag_persistence * closed for finite indices b and d, and open for left-infinite and/or * right-infinite endpoints. */ - struct filtration_value_interval : interval - { + struct Filtration_value_interval : Interval { private: - using Base = interval; + using Base = Interval; public: /** * @brief Default constructor */ - filtration_value_interval() : Base() {} + Filtration_value_interval() : Base() {} /** * @brief Construct a new interval with given parameters * @@ -119,13 +130,12 @@ class Zigzag_persistence * @param b Start value of the interval. * @param d End value of the interval. */ - filtration_value_interval(int dim, Filtration_value b, Filtration_value d) - : Base(dim, b, d) {} + Filtration_value_interval(int dim, filtration_value b, filtration_value d) : Base(dim, b, d) {} /** * @brief Returns the absolute length of the interval \f$|d-b|\f$. */ - Filtration_value length() const { + filtration_value length() const { if (Base::b_ == Base::d_) { return 0; } // otherwise inf - inf would return nan. @@ -134,7 +144,7 @@ class Zigzag_persistence /** * @brief Returns the absolute length of the log values of birth and death, i.e. \f$|\log d - \log b|\f$. */ - Filtration_value log_length() const { + filtration_value log_length() const { if (Base::b_ == Base::d_) { return 0; } // otherwise inf - inf would return nan. @@ -150,10 +160,10 @@ class Zigzag_persistence * zigzag persistence. * * By construction, we maintain the map satisfying - * 'birth_to_pos_[i] < birth_to_pos_[j]', with \f$0 <= i,j <= k\f$ indices in the quiver - * '\f$0 \leftrightarrow ... \leftrightarrow i \leftrightarrow ... \leftrightarrow k\f$' visited at time - * \f$k\f$ of the algorithm (prefix of length \f$k\f$ of the full zigzag filtration - * '\f$0 \leftrightarrow ... \leftrightarrow i \leftrightarrow ... + * 'birthToPos_[i] < birthToPos_[j]', with \f$0 <= i,j <= k\f$ indices in the quiver + * '\f$0 \leftrightarrow ... \leftrightarrow i \leftrightarrow ... \leftrightarrow k\f$' visited at time + * \f$k\f$ of the algorithm (prefix of length \f$k\f$ of the full zigzag filtration + * '\f$0 \leftrightarrow ... \leftrightarrow i \leftrightarrow ... * \leftrightarrow k \leftrightarrow ... \leftrightarrow n\f$' * that is studied), iff \f$i <_b j\f$ for the birth ordering. * @@ -162,11 +172,11 @@ class Zigzag_persistence * - if \f$k \rightarrow k+1\f$ forward, then \f$j <_b k+1\f$ for all indices \f$j < k+1\f$, otherwise * - if \f$k \leftarrow k+1\f$ backward, then \f$k+1 <_b j\f$ for all indices \f$j < k+1\f$. */ - struct birth_ordering { + struct Birth_ordering { /** * @brief Default constructor */ - birth_ordering() : birth_to_pos_(), max_birth_pos_(0), min_birth_pos_(-1) {} + Birth_ordering() : birthToPos_(), maxBirthPos_(0), minBirthPos_(-1) {} /** * @brief Inserts arrow number in the ordering after an insertion. @@ -175,9 +185,9 @@ class Zigzag_persistence * * @param arrow_number Forward arrow number. */ - void add_birth_forward(Simplex_key arrow_number) { // amortized constant time - birth_to_pos_.emplace_hint(birth_to_pos_.end(), arrow_number, max_birth_pos_); - ++max_birth_pos_; + void add_birth_forward(internal_key arrow_number) { // amortized constant time + birthToPos_.emplace_hint(birthToPos_.end(), arrow_number, maxBirthPos_); + ++maxBirthPos_; } /** * @brief Inserts arrow number in the ordering after a removal. @@ -186,19 +196,19 @@ class Zigzag_persistence * * @param arrow_number Backward arrow number. */ - void add_birth_backward(Simplex_key arrow_number) { // amortized constant time - birth_to_pos_.emplace_hint(birth_to_pos_.end(), arrow_number, min_birth_pos_); - --min_birth_pos_; + void add_birth_backward(internal_key arrow_number) { // amortized constant time + birthToPos_.emplace_hint(birthToPos_.end(), arrow_number, minBirthPos_); + --minBirthPos_; } /** * @brief Removes the birth from the ordering. - * When the row at index @a birth is removed from the homology matrix, we do not need + * When the row at index @p birth is removed from the homology matrix, we do not need * to maintain its position in b k2. * @@ -214,248 +224,173 @@ class Zigzag_persistence * @param k2 * @return true if k1 >b k2, false otherwise. */ - bool reverse_birth_order(Simplex_key k1, Simplex_key k2) const { return birth_to_pos_.at(k1) > birth_to_pos_.at(k2); } + bool reverse_birth_order(internal_key k1, internal_key k2) const { + return birthToPos_.at(k1) > birthToPos_.at(k2); + } private: - std::unordered_map birth_to_pos_; /**< birth_to_pos_[i] < birth_to_pos_[j] iff i birthToPos_; /**< birth_to_pos_[i] < birth_to_pos_[j] iff i ; - using index = typename matrix_type::index; + using Matrix_type = Gudhi::persistence_matrix::Matrix; + using index = typename Matrix_type::index; public: /** * @brief Constructor of the Zigzag_persistence class. - * @details After construction of the class, the zigzag filtration should be given in a streaming like way, i.e., - * call @ref insert_simplex or @ref remove_simplex for each step of the filtration in order of the filtration. - * If simplices are added (resp. removed) continuously, they can be inserted (resp. removed) in batches by using - * @ref insert_simplices_contiguously (resp. @ref remove_simplices_contiguously). - * To retrieve the current persistence diagram at any moment of the filtration, + * @details After construction of the class, the zigzag filtration should be given in a streaming like way, i.e., + * call @ref insert_face or @ref remove_face for each step of the filtration in order of the filtration. + * To retrieve the current persistence diagram at any moment of the filtration, * use @ref get_persistence_diagram or @ref get_index_persistence_diagram. * - * @param min_number_of_simplices Minimum number of simplices that will be inserted at some point in the filtration. - * If the total number of simplices is known in advance, the memory allocation can be better optimized. + * @param min_number_of_faces Minimum number of faces that will be inserted at some point in the filtration. + * If the total number of faces is known in advance, the memory allocation can be better optimized. * Default value: 0. - * @param ignore_cycles_above_dim Ignores cycles in dimension larger or equal in the final diagram. + * @param ignore_cycles_above_dim Ignores cycles in dimension larger or equal in the final diagram. * If -1, no cycles are ignored. Default value: -1. */ - Zigzag_persistence(unsigned int min_number_of_simplices = 0, int ignore_cycles_above_dim = -1) - : dim_max_(ignore_cycles_above_dim), - matrix_(min_number_of_simplices, - [this](index columnIndex1, index columnIndex2)->bool { - if (matrix_.get_column(columnIndex1).is_paired()){ - return matrix_.get_pivot(columnIndex1) < matrix_.get_pivot(columnIndex2); - } - return birth_ordering_.birth_order(births_.at(columnIndex1), births_.at(columnIndex2)); - }, - [this](index columnIndex1, index columnIndex2)->bool { - return false; - }), - num_arrow_(-1), - previous_filtration_value_(std::numeric_limits::infinity()) {} + Zigzag_persistence(unsigned int minNumberOfFaces = 0, int ignoreCyclesAboveDim = -1) + : dimMax_(ignoreCyclesAboveDim), + matrix_( + minNumberOfFaces, + [this](index columnIndex1, index columnIndex2) -> bool { + if (matrix_.get_column(columnIndex1).is_paired()) { + return matrix_.get_pivot(columnIndex1) < matrix_.get_pivot(columnIndex2); + } + return birthOrdering_.birth_order(births_.at(columnIndex1), births_.at(columnIndex2)); + }, + [this](index columnIndex1, index columnIndex2) -> bool { return false; }), + numArrow_(-1), + previousFiltrationValue_(std::numeric_limits::infinity()) {} /** - * @brief Updates the zigzag persistence diagram after the insertion of the given simplex. - * - * @tparam VertexRange Range type needing begin and end members. - * @param simplex Simplex to insert, represented by its vertices. - * @param filtration_value Filtration value associated to the simplex. + * @brief Updates the zigzag persistence diagram after the insertion of the given face. + * + * @tparam BoundaryRange Range type needing begin and end members. + * @param faceID ID representing the inserted face. + * @param boundary Boundary of the inserted face. The range should be composed of the IDs of all faces contained in + * the boundary (i.e. with non-zero coefficients), using the ID specified as `faceID` when the corresponding face + * was previously inserted (recall that the faces should be inserted in order of filtration). + * @param dimension Dimension of the inserted face. + * @param filtrationValue Filtration value associated to the face. * Assumed to be larger or equal to previously used filtration values. */ - template > - void insert_simplex(const VertexRange& simplex, Filtration_value filtration_value) { - if (dim_max_ != -1 && simplex.size() > static_cast(dim_max_) + 1) return; - - ++num_arrow_; - - if (filtration_value != previous_filtration_value_) // check whether the filt value has changed - { // consecutive pairs (i,f), (j,f') mean simplices of index k in [i,j-1] have - previous_filtration_value_ = filtration_value; // filtration value f - filtration_values_.emplace_back(num_arrow_, previous_filtration_value_); + template > + void insert_face(face_key faceID, + const BoundaryRange& boundary, + dimension_type dimension, + filtration_value filtrationValue) { + if (dimMax_ != -1 && dimension > dimMax_) return; + + ++numArrow_; + + //TODO: to make it really stream like, we should stream out finished bars and remove unnecessary filtration values + //from memory. + if (filtrationValue != previousFiltrationValue_) // check whether the filt value has changed + { // consecutive pairs (i,f), (j,f') mean faces of index k in [i,j-1] have + previousFiltrationValue_ = filtrationValue; // filtration value f + filtrationValues_.emplace_back(numArrow_, previousFiltrationValue_); } - std::pair res = cpx_.insert_simplex(simplex); - GUDHI_CHECK(res.second, "Zigzag_persistence::insert_simplex - insertion of a simplex already in the complex"); - cpx_.assign_key(res.first, num_arrow_); - _process_forward_arrow(res.first); - } + [[maybe_unused]] auto res = handleToKey_.try_emplace(faceID, numArrow_); - /** - * @brief Updates the zigzag persistence diagram after the removal of the given simplex. - * - * @tparam VertexRange Range type needing begin and end members. - * @param simplex Simplex to remove, represented by its vertices. - * @param filtration_value Filtration value associated to the removal. - * Assumed to be larger or equal to previously used filtration values. - */ - template > - void remove_simplex(const VertexRange& simplex, Filtration_value filtration_value) { - if (dim_max_ != -1 && simplex.size() > static_cast(dim_max_) + 1) return; - - ++num_arrow_; + GUDHI_CHECK(res.second, "Zigzag_persistence::insert_face - face already in the complex"); - Simplex_handle sh = cpx_.find(simplex); - GUDHI_CHECK(sh != cpx_.null_simplex(), - "Zigzag_persistence::remove_simplex - removal of a simplex not in the complex"); - - if (filtration_value != previous_filtration_value_) // check whether the filt value has changed - { // consecutive pairs (i,f), (j,f') mean simplices of index k in [i,j-1] have - previous_filtration_value_ = filtration_value; // filtration value f - filtration_values_.emplace_back(num_arrow_, previous_filtration_value_); + // Reduce the boundary of zzsh in the basis of cycles. + // Compute the keys of the faces of the boundary of zzsh. + std::set col_bsh; // set maintains the natural order on indices + for (auto b_sh : boundary) { + col_bsh.insert(handleToKey_.at(b_sh)); // TODO: add possibilities of coefficients } - _process_backward_arrow(sh); - cpx_.remove_maximal_simplex(sh); + _process_forward_arrow(col_bsh, dimension); } /** - * @brief Updates the zigzag persistence diagram after the insertion of the given simplices. - * - * @tparam SimplexRange Range type needing begin and end members. - * @tparam FiltrationRange Range type needing begin and end members. - * @param simplices Simplices which are inserted, represented by their vertices. They have to be in the order they - * are inserted in the filtration and ``contiguous'' in the filtration, that is, no other simplex - * which is not in the range is inserted or removed between two simplices in the range. - * @param filtration_values Filtration values associated to the insertion of the given simplices. - * The order has to correspond to the order in @a simplices. Their values have to ascending in this order and - * they are assumed to be larger or equal to previously used filtration values. + * @brief Updates the zigzag persistence diagram after the removal of the given face. + * + * @param faceID ID representing the face to remove. Should be the same than the one used to insert it. + * @param dimension Dimension of the face. + * @param filtrationValue Filtration value associated to the removal. + * Assumed to be larger or equal to previously used filtration values. */ - template >, - class FiltrationRange = std::initializer_list> - void insert_simplices_contiguously(const SimplexRange& simplices, const FiltrationRange& filtration_values) { - auto simplexIt = simplices.begin(); - auto filIt = filtration_values.begin(); - for (; simplexIt != simplices.end(); ++simplexIt, ++filIt) { - insert_simplex(*simplexIt, *filIt); - } - } + void remove_face(face_key faceID, dimension_type dimension, filtration_value filtrationValue) { + if (dimMax_ != -1 && dimension > dimMax_) return; - /** - * @brief Updates the zigzag persistence diagram after the removal of the given simplices. - * - * @tparam SimplexRange Range type needing begin and end members. - * @tparam FiltrationRange Range type needing begin and end members. - * @param simplices Simplices which are removed, represented by their vertices. They have to be in the order they - * are removed in the filtration and ``contiguous'' in the filtration, that is, no other simplex - * which is not in the range is inserted or removed between two simplices in the range. - * @param filtration_values Filtration values associated to the removal of the given simplices. Has therefore the - * same size as @a simplices. The order has to correspond to the order in @a simplices. Their values have to - * ascending in this order and they are assumed to be larger or equal to previously used filtration values. - */ - template >, - class FiltrationRange = std::initializer_list> - void remove_simplices_contiguously(const SimplexRange& simplices, const FiltrationRange& filtration_values) { - auto simplexIt = simplices.begin(); - auto filIt = filtration_values.begin(); - for (; simplexIt != simplices.end(); ++simplexIt, ++filIt) { - remove_simplex(*simplexIt, *filIt); - } - } + ++numArrow_; - /** - * @brief Updates the zigzag persistence diagram after the insertion of the given simplices. - * - * @tparam SimplexRangeIterators Forward iterator of a range. - * @tparam FiltrationRangeIterators Forward iterator of a range. - * @param simplex_range_start Iterator pointing to the begining of the range of simplices to insert. - * The simplices should be represented by their vertices. They have to be in the order they - * are inserted in the filtration and ``contiguous'' in the filtration, that is, no other simplex - * which is not in the range is inserted or removed between two simplices in the range. - * @param simplex_range_end Iterator pointing to the end of the range of simplices to insert. - * @param filtration_range_start Iterator pointing to the begining of the range of filtration values. The range is - * assumed to end at the same time than the simplices range and has the same order. The filtration values should be - * ascending in this order and they are assumed to be larger or equal to previously used filtration values. - */ - template - void insert_simplices_contiguously(SimplexRangeIterators simplex_range_start, SimplexRangeIterators simplex_range_end, - FiltrationRangeIterators filtration_range_start) { - for (; simplex_range_start != simplex_range_end; ++simplex_range_start, ++filtration_range_start) { - insert_simplex(*simplex_range_start, *filtration_range_start); - } - } + auto it = handleToKey_.find(faceID); + GUDHI_CHECK(it != handleToKey_.end(), "Zigzag_persistence::remove_face - face not in the complex"); - /** - * @brief Updates the zigzag persistence diagram after the removal of the given simplices. - * - * @tparam SimplexRangeIterators Forward iterator of a range. - * @tparam FiltrationRangeIterators Forward iterator of a range. - * @param simplex_range_start Iterator pointing to the begining of the range of simplices to remove. - * The simplices should be represented by their vertices. They have to be in the order they - * are removed in the filtration and ``contiguous'' in the filtration, that is, no other simplex - * which is not in the range is inserted or removed between two simplices in the range. - * @param simplex_range_end Iterator pointing to the end of the range of simplices to remove. - * @param filtration_range_start Iterator pointing to the begining of the range of filtration values. The range is - * assumed to end at the same time than the simplices range and has the same order. The filtration values should be - * ascending in this order and they are assumed to be larger or equal to previously used filtration values. - */ - template - void remove_simplices_contiguously(SimplexRangeIterators simplex_range_start, SimplexRangeIterators simplex_range_end, - FiltrationRangeIterators filtration_range_start) { - for (; simplex_range_start != simplex_range_end; ++simplex_range_start, ++filtration_range_start) { - remove_simplex(*simplex_range_start, *filtration_range_start); + if (filtrationValue != previousFiltrationValue_) // check whether the filt value has changed + { // consecutive pairs (i,f), (j,f') mean faces of index k in [i,j-1] have + previousFiltrationValue_ = filtrationValue; // filtration value f + filtrationValues_.emplace_back(numArrow_, previousFiltrationValue_); } + + _process_backward_arrow(it->second, dimension); + handleToKey_.erase(it); } /** - * @brief Returns the ``index persistence diagram'' of the current filtration, that is, the pairs of atomic arrow - * numbers corresponding to a birth-death pair. Does not contain points at infinity, only the cycle classes which + * @brief Returns the ``index persistence diagram'' of the current filtration, that is, the pairs of atomic arrow + * numbers corresponding to a birth-death pair. Does not contain points at infinity, only the cycle classes which * already died are represented. * * @return Reference to the list of intervals. */ - const std::list& get_index_persistence_diagram() const { return persistence_diagram_; } + const std::list& get_index_persistence_diagram() const { return persistenceDiagram_; } /** * @brief Returns the filtration values \f$[f(b),f(d)]\f$ associated to the indices \f$[b,d]\f$ which are retrieved * by @ref get_index_persistence_diagram. * - * @param b_key Birth index - * @param d_key Death index + * @param birthKey Birth index + * @param deathKey Death index * @return A pair of filtration values associated to the given indices. */ - std::pair map_index_to_filtration_value( - Simplex_key b_key, Simplex_key d_key) const - { + std::pair map_index_to_filtration_value(internal_key birthKey, + internal_key deathKey) const { // filtration_values_ must be sorted by increasing keys. - auto it_b = // lower_bound(x) returns leftmost y s.t. x <= y + auto itBirth = // lower_bound(x) returns leftmost y s.t. x <= y std::lower_bound( - filtration_values_.begin(), filtration_values_.end(), - std::pair(b_key, std::numeric_limits::infinity()), - [](std::pair p1, std::pair p2) { + filtrationValues_.begin(), filtrationValues_.end(), + std::pair(birthKey, std::numeric_limits::infinity()), + [](std::pair p1, std::pair p2) { return p1.first < p2.first; }); - if (it_b == filtration_values_.end() || it_b->first > b_key) { - --it_b; + if (itBirth == filtrationValues_.end() || itBirth->first > birthKey) { + --itBirth; } // it points to the rightmost z such that z <= x - auto it_d = // + auto itDeath = // std::lower_bound( - filtration_values_.begin(), filtration_values_.end(), - std::pair(d_key, std::numeric_limits::infinity()), - [](std::pair p1, std::pair p2) { + filtrationValues_.begin(), filtrationValues_.end(), + std::pair(deathKey, std::numeric_limits::infinity()), + [](std::pair p1, std::pair p2) { return p1.first < p2.first; }); - if (it_d == filtration_values_.end() || it_d->first > d_key) { - --it_d; + if (itDeath == filtrationValues_.end() || itDeath->first > deathKey) { + --itDeath; } - return std::make_pair(it_b->second, it_d->second); + return std::make_pair(itBirth->second, itDeath->second); } /** - * @brief Returns the current persistence diagram ordered first by length, than by dimension, + * @brief Returns the current persistence diagram ordered first by length, than by dimension, * than by birth value and finally by death value. - * - * @param shortest_interval Threshold. Every bar shorter than the given value will be ignored. Default value: 0. - * @param include_infinit_bars If set to true, infinit bars are included in the diagram. Default value: false. + * + * @param shortestInterval Threshold. Every bar shorter than the given value will be ignored. Default value: 0. + * @param includeInfinitBars If set to true, infinit bars are included in the diagram. Default value: false. * @return A vector of pairs of filtration values representing the persistence diagram. */ - std::vector get_persistence_diagram(Filtration_value shortest_interval = 0., - bool include_infinit_bars = false) { - auto comp = [](filtration_value_interval p, filtration_value_interval q) { + std::vector get_persistence_diagram(filtration_value shortestInterval = 0., + bool includeInfinitBars = false) { + auto comp = [](Filtration_value_interval p, Filtration_value_interval q) { if (p.length() != q.length()) { return p.length() > q.length(); } // longest 1st @@ -468,9 +403,9 @@ class Zigzag_persistence return p.death() < q.death(); }; - std::vector diag = _get_persistence_diagram(shortest_interval); + std::vector diag = _get_persistence_diagram(shortestInterval); - if (include_infinit_bars) { + if (includeInfinitBars) { _retrieve_infinit_bars(diag); } @@ -479,113 +414,74 @@ class Zigzag_persistence return diag; } - /** - * @brief Returns a reference to the complex storing the simplices. - * A simplex is added in a call of @ref insert_simplex and is removed in a call of @ref remove_simplex. - * - * @return Const reference to the complex. - */ -// const ZigzagComplex& get_complex() const{ -// return cpx_; -// } - - /** - * @brief Returns a reference to the complex storing the simplices. - * A simplex is added in a call of @ref insert_simplex and is removed in a call of @ref remove_simplex. - * @warning The complex is not const for now for technical reasons, but DO NOT modify it. - * - * @return Reference to the complex. - */ - ZigzagComplex& get_complex() const{ - return cpx_; - } - - /** - * @brief For debug purposes, to remove. - */ - void print_current_complex() const { - for (auto& sh : cpx_.complex_simplex_range()) { - for (auto v : cpx_.simplex_vertex_range(sh)) { - std::cout << v << " "; - } - std::cout << " - " << cpx_.filtration(sh) << "\n"; - } - } - private: + /** - * @brief Computes the boundary cycle of the new simplex zzsh, and express it as a - * sum of cycles in a matrix. If some cycles are not boundary cycles, i.e., columns with F-index + * @brief Express the boundary cycle of the new face as a sum of cycles in a matrix. + * If some cycles are not boundary cycles, i.e., columns with F-index * in the matrix, it applies a surjective diamond to the zigzag module. - * - * @param zzsh Simplex handle of the inserted simplex. + * + * @param boundary Boundary of the inserted face. + * @param dim Dimension of the inserted face. */ - void _process_forward_arrow(Simplex_handle zzsh) { // maintain the <=b order - // Reduce the boundary of zzsh in the basis of cycles. - // Compute the simplex keys of the simplices of the boundary of zzsh. - std::set col_bsh; // set maintains the natural order on indices - for (auto b_sh : cpx_.boundary_simplex_range(zzsh)) { - col_bsh.insert(cpx_.key(b_sh)); - } - - std::vector chains_in_F = matrix_.insert_boundary(num_arrow_, col_bsh); + void _process_forward_arrow(const std::set& boundary, dimension_type dim) { + std::vector chainsInF = matrix_.insert_boundary(numArrow_, boundary); - if (!chains_in_F.empty()) { - _apply_surjective_reflection_diamond(zzsh, chains_in_F); + if (!chainsInF.empty()) { + _apply_surjective_reflection_diamond(dim, chainsInF); } else { - birth_ordering_.add_birth_forward(num_arrow_); - births_.emplace_hint(births_.end(), matrix_.get_column_with_pivot(num_arrow_), num_arrow_); + birthOrdering_.add_birth_forward(numArrow_); + births_.emplace_hint(births_.end(), matrix_.get_column_with_pivot(numArrow_), numArrow_); } } /** * @brief Applies the surjective reflection diamond principle to the current filtration. - * - * @details The vector chains_in_F is sorted by decreasing lowest index values in the + * + * @details The vector chainsInF is sorted by decreasing lowest index values in the * columns corresponding to the chains, due to its computation in the reduction of - * \partial zzsh in _process_forward_arrow(...). It is equivalent to decreasing death index + * the boundary in _process_forward_arrow(...). It is equivalent to decreasing death index * order w.r.t. the & chains_in_F) { + void _apply_surjective_reflection_diamond(dimension_type dim, const std::vector& chainsInF) { // fp is the largest death index for <=d // Set col_fp: col_fp <- col_f1+...+col_fp (now in G); preserves lowest idx - auto chain_fp = chains_in_F[0]; // col_fp, with largest death bool { - return birth_ordering_.reverse_birth_order(k1, k2); + auto cmp_birth = [this](internal_key k1, internal_key k2) -> bool { + return birthOrdering_.reverse_birth_order(k1, k2); }; // true iff b(k1) >b b(k2) // available_birth: for all i by >d value of the d_i, // contains at step i all b_j, j > i, and maybe b_i if not stolen - std::set available_birth(cmp_birth); + std::set availableBirth(cmp_birth); // for f1 to f_{p} (i by <=d), insertion in available_birth_to_fidx sorts by >=b - for (auto& chain_f : chains_in_F) { - available_birth.insert(births_.at(chain_f)); + for (auto& chainF : chainsInF) { + availableBirth.insert(births_.at(chainF)); } - auto maxb_it = available_birth.begin(); // max birth cycle - auto maxb = *maxb_it; // max birth value, for persistence diagram - available_birth.erase(maxb_it); // remove max birth cycle (stolen) + auto maxbIt = availableBirth.begin(); // max birth cycle + auto maxb = *maxbIt; // max birth value, for persistence diagram + availableBirth.erase(maxbIt); // remove max birth cycle (stolen) - auto last_modified_chain_it = chains_in_F.rbegin(); + auto lastModifiedChainIt = chainsInF.rbegin(); // consider all death indices by increasing the maximal availabe death. // Let c_1 ... c_f be the chains s.t. <[c_1+...+c_f]> is the kernel and @@ -600,99 +496,97 @@ class Zigzag_persistence // last_modified is equal to c_k+...+c_1, all c_j, i>j>k, are indeed c_j // set c_i <- c_i + (c_i-1) + ... + (c_k+1) + (c_k + ... + c_1) - for (auto chain_passed_it = last_modified_chain_it; // all with smaller modified_columns; - const auto& row = matrix_.get_row(simplexIndex); - modified_columns.reserve(row.size()); - std::transform(row.begin(), row.end(), std::back_inserter(modified_columns), + std::vector modifiedColumns; + const auto& row = matrix_.get_row(faceID); + modifiedColumns.reserve(row.size()); + std::transform(row.begin(), row.end(), std::back_inserter(modifiedColumns), [](const auto& cell) { return cell.get_column_index(); }); // Sort by left-to-right order in the matrix_ (no order maintained in rows) - std::stable_sort(modified_columns.begin(), modified_columns.end(), + std::stable_sort(modifiedColumns.begin(), modifiedColumns.end(), [this](index i1, index i2) { return matrix_.get_pivot(i1) < matrix_.get_pivot(i2); }); // Modifies curr_col, not the other one. - for (auto other_col_it = std::next(modified_columns.begin()); other_col_it != modified_columns.end(); - ++other_col_it) { - curr_col = matrix_.vine_swap_with_z_eq_1_case(curr_col, *other_col_it); + for (auto otherColIt = std::next(modifiedColumns.begin()); otherColIt != modifiedColumns.end(); ++otherColIt) { + currCol = matrix_.vine_swap_with_z_eq_1_case(currCol, *otherColIt); } // curr_col points to the column to remove by restriction of K to K-{\sigma} - if (!matrix_.get_column(curr_col).is_paired()) { // in F - int dim_zzsh = cpx_.dimension(zzsh); - auto it = births_.find(curr_col); - if (dim_max_ == -1 || (dim_max_ != -1 && dim_zzsh < dim_max_)) { // don't record intervals of max dim - persistence_diagram_.emplace_back(dim_zzsh, it->second, num_arrow_); // -1); + if (!matrix_.get_column(currCol).is_paired()) { // in F + auto it = births_.find(currCol); + if (dimMax_ == -1 || (dimMax_ != -1 && dim < dimMax_)) { // don't record intervals over max dim + persistenceDiagram_.emplace_back(dim, it->second, numArrow_); } - //Following value can be erased, but it slowes the process down a bit, so I keep it as a remainder for now: - // birth_ordering_.remove_birth(it->second); + // Following value can be erased, but it slowes the process down a bit, so I keep it as a remainder for now: + // birthOrdering_.remove_birth(it->second); births_.erase(it); } else { // in H -> paired with c_g, that now belongs to F now // maintain the <=b order - birth_ordering_.add_birth_backward(num_arrow_); - births_.try_emplace(matrix_.get_column(curr_col).get_paired_chain_index(), num_arrow_); + birthOrdering_.add_birth_backward(numArrow_); + births_.try_emplace(matrix_.get_column(currCol).get_paired_chain_index(), numArrow_); } - // cannot be in G as the removed simplex is maximal - matrix_.remove_maximal_face(simplexIndex, {}); + // cannot be in G as the removed face is maximal + matrix_.remove_maximal_face(faceID, {}); } /** * @brief Returns the current persistence diagram ordered by length without infinit bars. * - * @param shortest_interval Intervals shorter than the given value are ignored. + * @param shortestInterval Intervals shorter than the given value are ignored. * @return Vector of intervals. */ - std::vector _get_persistence_diagram(Filtration_value shortest_interval) { - std::vector diag; - diag.reserve(persistence_diagram_.size()); + std::vector _get_persistence_diagram(filtration_value shortestInterval) { + std::vector diag; + diag.reserve(persistenceDiagram_.size()); - std::stable_sort(filtration_values_.begin(), filtration_values_.end(), - [](std::pair p1, std::pair p2) { + std::stable_sort(filtrationValues_.begin(), filtrationValues_.end(), + [](std::pair p1, std::pair p2) { return p1.first < p2.first; }); - for (auto bar : persistence_diagram_) { - Filtration_value birth, death; + for (auto bar : persistenceDiagram_) { + filtration_value birth, death; std::tie(birth, death) = map_index_to_filtration_value(bar.birth(), bar.death()); if (birth > death) { std::swap(birth, death); } - if (death - birth > shortest_interval) { + if (death - birth > shortestInterval) { diag.emplace_back(bar.dim(), birth, death); } } @@ -705,43 +599,43 @@ class Zigzag_persistence * * @param diag Reference to vector where to store the infinit bars. */ - void _retrieve_infinit_bars(std::vector& diag) const { - auto birth = [this](Simplex_key b_key) { - auto it_b = // lower_bound(x) returns leftmost y s.t. x <= y + void _retrieve_infinit_bars(std::vector& diag) const { + auto birth = [this](internal_key birthKey) { + auto itBirth = // lower_bound(x) returns leftmost y s.t. x <= y std::lower_bound( - filtration_values_.begin(), filtration_values_.end(), - std::pair(b_key, std::numeric_limits::infinity()), - [](std::pair p1, std::pair p2) { + filtrationValues_.begin(), filtrationValues_.end(), + std::pair(birthKey, std::numeric_limits::infinity()), + [](std::pair p1, std::pair p2) { return p1.first < p2.first; }); - if (it_b == filtration_values_.end() || it_b->first > b_key) { - --it_b; + if (itBirth == filtrationValues_.end() || itBirth->first > birthKey) { + --itBirth; } - return it_b->second; + return itBirth->second; }; for (auto& p : births_) { auto dim = matrix_.get_column(matrix_.get_column_with_pivot(p.first)).get_dimension(); - if (dim_max_ == -1 || (dim_max_ != -1 && dim < dim_max_)) - diag.emplace_back(dim, birth(p.second), std::numeric_limits::infinity()); + if (dimMax_ == -1 || (dimMax_ != -1 && dim < dimMax_)) + diag.emplace_back(dim, birth(p.second), std::numeric_limits::infinity()); } } private: - Complex cpx_; /**< Complex in which the current simplices are stored. */ - int dim_max_; /**< Maximal dimension of a bar to record. */ - matrix_type matrix_; /**< Matrix storing a base of the current chain complex. */ - std::unordered_map births_; /**< Map simplex index in F to corresponding birth. */ - birth_ordering birth_ordering_; /**< Maintains persistence_diagram_; /**< Stores current closed persistence intervals. */ - Simplex_key num_arrow_; /**< Current arrow number. */ - Filtration_value previous_filtration_value_; /**< Filtration value of the previous arrow. */ + std::unordered_map handleToKey_; /**< Map from input keys to internal keys. */ + dimension_type dimMax_; /**< Maximal dimension of a bar to record. */ + Matrix_type matrix_; /**< Matrix storing a base of the current chain complex. */ + std::unordered_map births_; /**< Map face index in F to corresponding birth. */ + Birth_ordering birthOrdering_; /**< Maintains persistenceDiagram_; /**< Stores current closed persistence intervals. */ + internal_key numArrow_; /**< Current arrow number. */ + filtration_value previousFiltrationValue_; /**< Filtration value of the previous arrow. */ /** - * @brief filtration_values_ stores consecutive pairs (i,f) , (j,f') with f != f', - * meaning that all inserted simplices with key in [i;j-1] have filtration value f - * i is the smallest simplex index whose simplex has filtration value f. + * @brief filtrationValues_ stores consecutive pairs (i,f) , (j,f') with f != f', + * meaning that all inserted faces with key in [i;j-1] have filtration value f, + * i is the smallest face index whose face has filtration value f. */ - std::vector> filtration_values_; + std::vector > filtrationValues_; }; // end class Zigzag_persistence } // namespace zigzag_persistence diff --git a/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp b/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp index d09699d750..65e80323dc 100644 --- a/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp +++ b/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp @@ -8,9 +8,6 @@ * - YYYY/MM Author: Description of the modification */ -#include -#include -#include #include #include @@ -19,26 +16,16 @@ #include #include -#include - -using namespace Gudhi; -using namespace boost::unit_test; - -struct Simplex_tree_options_zigzag_persistence : Gudhi::Simplex_tree_options_minimal { - static const bool store_key = true; -}; - -using ST = Gudhi::Simplex_tree; -using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; -using Vertex_handle = ST::Vertex_handle; -using Filtration_value = ST::Filtration_value; -using interval_index = ZP::index_interval; -using interval_filtration = ZP::filtration_value_interval; +using ZP = Gudhi::zigzag_persistence::Zigzag_persistence<>; +using face_handle = ZP::face_key; +using filtration_value = ZP::filtration_value; +using Interval_index = ZP::Index_interval; +using Interval_filtration = ZP::Filtration_value_interval; struct cmp_intervals_by_length { cmp_intervals_by_length() {} - bool operator()(interval_filtration p, interval_filtration q) { + bool operator()(Interval_filtration p, Interval_filtration q) { if (p.length() != q.length()) { return p.length() > q.length(); } @@ -60,7 +47,7 @@ BOOST_AUTO_TEST_CASE(constructor) { BOOST_CHECK(zp.get_persistence_diagram(0).empty()); } -void test_barcode(ZP& zp, std::vector& barcode) { +void test_barcode(ZP& zp, std::vector& barcode) { std::stable_sort(barcode.begin(), barcode.end(), cmp_intervals_by_length()); auto it = barcode.begin(); for (const auto& interval : zp.get_persistence_diagram(0, true)) { @@ -72,7 +59,7 @@ void test_barcode(ZP& zp, std::vector& barcode) { BOOST_CHECK(it == barcode.end()); } -void test_indices(ZP& zp, std::vector& indices, std::vector& indexToFil) { +void test_indices(ZP& zp, std::vector& indices, std::vector& indexToFil) { auto it = indices.begin(); for (const auto& interval : zp.get_index_persistence_diagram()) { BOOST_CHECK_EQUAL(interval.dim(), it->dim()); @@ -86,7 +73,7 @@ void test_indices(ZP& zp, std::vector& indices, std::vector > get_simplices() { +std::vector > get_simplices() { return {{0}, {1}, {2}, @@ -118,7 +105,39 @@ std::vector > get_simplices() { {0, 1, 2, 4}}; // remove } -std::vector get_filtration_values() { +std::vector > get_boundaries() { + return {{}, + {}, + {}, + {0, 1}, + {0, 2}, + {}, + {1, 2}, + {}, + {5, 7}, + {}, + {3, 4, 6}, + {7, 9}, + {5, 9}, + {8, 11, 12}, + {10}, // remove + {13}, // remove + {1, 7}, + {3, 4, 6}, + {2, 7}, + {8, 11, 12}, + {0, 7}, + {4, 18, 20}, + {6, 16, 18}, + {3, 16, 20}, + {19}, // remove + {8}, // remove + {12}, // remove + {17, 21, 22, 23}, + {27}}; // remove +} + +std::vector get_filtration_values() { return {0, 0, 0, 1, 1, 1, 2, 2, 2, @@ -134,16 +153,16 @@ std::vector get_filtration_values() { BOOST_AUTO_TEST_CASE(zigzag_persistence_single) { ZP zp(28); - std::vector realIndices; - std::vector realBarcode; + std::vector realIndices; + std::vector realBarcode; realIndices.reserve(13); realBarcode.reserve(9); - std::vector > simplices = get_simplices(); - std::vector filValues = get_filtration_values(); + std::vector > simplices = get_boundaries(); + std::vector filValues = get_filtration_values(); for (unsigned int i = 0; i < 14; ++i) { - zp.insert_simplex(simplices[i], filValues[i]); + zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); } realIndices.emplace_back(0, 1, 3); @@ -159,11 +178,12 @@ BOOST_AUTO_TEST_CASE(zigzag_persistence_single) { realBarcode.emplace_back(1, 3, 4); for (unsigned int i = 14; i < 16; ++i) { - zp.remove_simplex(simplices[i], filValues[i]); + auto id = simplices[i][0]; + zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[i]); } for (unsigned int i = 16; i < 24; ++i) { - zp.insert_simplex(simplices[i], filValues[i]); + zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); } realIndices.emplace_back(0, 5, 16); @@ -177,22 +197,24 @@ BOOST_AUTO_TEST_CASE(zigzag_persistence_single) { realBarcode.emplace_back(1, 6, 7); for (unsigned int i = 24; i < 27; ++i) { - zp.remove_simplex(simplices[i], filValues[i]); + auto id = simplices[i][0]; + zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[i]); } realIndices.emplace_back(1, 24, 25); realBarcode.emplace_back(1, 8, 9); - zp.insert_simplex(simplices[27], filValues[27]); + zp.insert_face(27, simplices[27], simplices[27].size() == 0 ? 0 : simplices[27].size() - 1, filValues[27]); realIndices.emplace_back(2, 23, 27); realBarcode.emplace_back(2, 7, 9); - zp.remove_simplex(simplices[28], filValues[28]); + auto id = simplices[28][0]; + zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[28]); - realBarcode.emplace_back(0, 0, std::numeric_limits::infinity()); - realBarcode.emplace_back(0, 9, std::numeric_limits::infinity()); - realBarcode.emplace_back(2, 10, std::numeric_limits::infinity()); + realBarcode.emplace_back(0, 0, std::numeric_limits::infinity()); + realBarcode.emplace_back(0, 9, std::numeric_limits::infinity()); + realBarcode.emplace_back(2, 10, std::numeric_limits::infinity()); test_indices(zp, realIndices, filValues); test_barcode(zp, realBarcode); @@ -200,18 +222,18 @@ BOOST_AUTO_TEST_CASE(zigzag_persistence_single) { BOOST_AUTO_TEST_CASE(zigzag_persistence_single_max1) { ZP zp(28, 1); - std::vector realIndices; - std::vector indexToFil(28); - std::vector realBarcode; + std::vector realIndices; + std::vector indexToFil(28); + std::vector realBarcode; realIndices.reserve(5); realBarcode.reserve(3); - std::vector > simplices = get_simplices(); - std::vector filValues = get_filtration_values(); + std::vector > simplices = get_boundaries(); + std::vector filValues = get_filtration_values(); unsigned int currIndex = 0; for (unsigned int i = 0; i < 14; ++i) { - zp.insert_simplex(simplices[i], filValues[i]); + zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); if (simplices[i].size() < 3) { indexToFil[currIndex++] = filValues[i]; } @@ -226,14 +248,15 @@ BOOST_AUTO_TEST_CASE(zigzag_persistence_single_max1) { realBarcode.emplace_back(0, 0, 1); for (unsigned int i = 14; i < 16; ++i) { - zp.remove_simplex(simplices[i], filValues[i]); - if (simplices[i].size() < 3) { + auto id = simplices[i][0]; + zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[i]); + if (simplices[id].size() < 3) { indexToFil[currIndex++] = filValues[i]; } } for (unsigned int i = 16; i < 24; ++i) { - zp.insert_simplex(simplices[i], filValues[i]); + zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); if (simplices[i].size() < 3) { indexToFil[currIndex++] = filValues[i]; } @@ -243,249 +266,19 @@ BOOST_AUTO_TEST_CASE(zigzag_persistence_single_max1) { realBarcode.emplace_back(0, 1, 6); for (unsigned int i = 24; i < 27; ++i) { - zp.remove_simplex(simplices[i], filValues[i]); - if (simplices[i].size() < 3) { + auto id = simplices[i][0]; + zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[i]); + if (simplices[id].size() < 3) { indexToFil[currIndex++] = filValues[i]; } } - zp.insert_simplex(simplices[27], filValues[27]); - zp.remove_simplex(simplices[28], filValues[28]); - - realBarcode.emplace_back(0, 0, std::numeric_limits::infinity()); - realBarcode.emplace_back(0, 9, std::numeric_limits::infinity()); - - test_indices(zp, realIndices, indexToFil); - test_barcode(zp, realBarcode); -} - -BOOST_AUTO_TEST_CASE(zigzag_persistence_batch_with_iterators) { - ZP zp; - std::vector realIndices; - std::vector realBarcode; - realIndices.reserve(13); - realBarcode.reserve(9); - - std::vector > simplices = get_simplices(); - std::vector filValues = get_filtration_values(); - - zp.insert_simplices_contiguously(simplices.begin(), simplices.begin() + 14, filValues.begin()); - - realIndices.emplace_back(0, 1, 3); - realIndices.emplace_back(0, 2, 4); - realIndices.emplace_back(0, 7, 8); - realIndices.emplace_back(1, 6, 10); - realIndices.emplace_back(0, 9, 11); - realIndices.emplace_back(1, 12, 13); - - realBarcode.emplace_back(0, 0, 1); - realBarcode.emplace_back(0, 0, 1); - realBarcode.emplace_back(1, 2, 3); - realBarcode.emplace_back(1, 3, 4); - - zp.remove_simplices_contiguously(simplices.begin() + 14, simplices.begin() + 16, filValues.begin() + 14); - zp.insert_simplices_contiguously(simplices.begin() + 16, simplices.begin() + 24, filValues.begin() + 16); - - realIndices.emplace_back(0, 5, 16); - realIndices.emplace_back(1, 14, 17); - realIndices.emplace_back(1, 15, 19); - realIndices.emplace_back(1, 20, 21); - realIndices.emplace_back(1, 18, 22); - - realBarcode.emplace_back(0, 1, 6); - realBarcode.emplace_back(1, 5, 6); - realBarcode.emplace_back(1, 6, 7); - - zp.remove_simplices_contiguously(simplices.begin() + 24, simplices.begin() + 27, filValues.begin() + 24); - - realIndices.emplace_back(1, 24, 25); - realBarcode.emplace_back(1, 8, 9); - - zp.insert_simplices_contiguously(simplices.begin() + 27, simplices.begin() + 28, filValues.begin() + 27); - - realIndices.emplace_back(2, 23, 27); - realBarcode.emplace_back(2, 7, 9); - - zp.remove_simplices_contiguously(simplices.begin() + 28, simplices.begin() + 29, filValues.begin() + 28); - - realBarcode.emplace_back(0, 0, std::numeric_limits::infinity()); - realBarcode.emplace_back(0, 9, std::numeric_limits::infinity()); - realBarcode.emplace_back(2, 10, std::numeric_limits::infinity()); - - test_indices(zp, realIndices, filValues); - test_barcode(zp, realBarcode); -} - -BOOST_AUTO_TEST_CASE(zigzag_persistence_batch_with_iterators_max1) { - ZP zp(28, 1); - std::vector realIndices; - std::vector indexToFil(28); - std::vector realBarcode; - realIndices.reserve(5); - realBarcode.reserve(3); - - std::vector > simplices = get_simplices(); - std::vector filValues = get_filtration_values(); - unsigned int currIndex = 0; - - for (unsigned int i = 0; i < 28; ++i) { - if (simplices[i].size() < 3) { - indexToFil[currIndex++] = filValues[i]; - } - } - - zp.insert_simplices_contiguously(simplices.begin(), simplices.begin() + 14, filValues.begin()); - - realIndices.emplace_back(0, 1, 3); - realIndices.emplace_back(0, 2, 4); - realIndices.emplace_back(0, 7, 8); - realIndices.emplace_back(0, 9, 10); - - realBarcode.emplace_back(0, 0, 1); - realBarcode.emplace_back(0, 0, 1); - - zp.remove_simplices_contiguously(simplices.begin() + 14, simplices.begin() + 16, filValues.begin() + 14); - zp.insert_simplices_contiguously(simplices.begin() + 16, simplices.begin() + 24, filValues.begin() + 16); - - realIndices.emplace_back(0, 5, 12); - realBarcode.emplace_back(0, 1, 6); - - zp.remove_simplices_contiguously(simplices.begin() + 24, simplices.begin() + 27, filValues.begin() + 24); - zp.insert_simplices_contiguously(simplices.begin() + 27, simplices.begin() + 28, filValues.begin() + 27); - zp.remove_simplices_contiguously(simplices.begin() + 28, simplices.begin() + 29, filValues.begin() + 28); - - realBarcode.emplace_back(0, 0, std::numeric_limits::infinity()); - realBarcode.emplace_back(0, 9, std::numeric_limits::infinity()); - - test_indices(zp, realIndices, indexToFil); - test_barcode(zp, realBarcode); -} - -BOOST_AUTO_TEST_CASE(zigzag_persistence_batch) { - ZP zp; - std::vector realIndices; - std::vector realBarcode; - realIndices.reserve(13); - realBarcode.reserve(9); - - std::vector > simplices = get_simplices(); - std::vector filValues = get_filtration_values(); - - std::vector > subSimplices(simplices.begin(), simplices.begin() + 14); - std::vector subFilValues(filValues.begin(), filValues.begin() + 14); - zp.insert_simplices_contiguously(subSimplices, subFilValues); - - realIndices.emplace_back(0, 1, 3); - realIndices.emplace_back(0, 2, 4); - realIndices.emplace_back(0, 7, 8); - realIndices.emplace_back(1, 6, 10); - realIndices.emplace_back(0, 9, 11); - realIndices.emplace_back(1, 12, 13); - - realBarcode.emplace_back(0, 0, 1); - realBarcode.emplace_back(0, 0, 1); - realBarcode.emplace_back(1, 2, 3); - realBarcode.emplace_back(1, 3, 4); - - subSimplices = std::vector >(simplices.begin() + 14, simplices.begin() + 16); - subFilValues = std::vector(filValues.begin() + 14, filValues.begin() + 16); - zp.remove_simplices_contiguously(subSimplices, subFilValues); - - subSimplices = std::vector >(simplices.begin() + 16, simplices.begin() + 24); - subFilValues = std::vector(filValues.begin() + 16, filValues.begin() + 24); - zp.insert_simplices_contiguously(subSimplices, subFilValues); - - realIndices.emplace_back(0, 5, 16); - realIndices.emplace_back(1, 14, 17); - realIndices.emplace_back(1, 15, 19); - realIndices.emplace_back(1, 20, 21); - realIndices.emplace_back(1, 18, 22); - - realBarcode.emplace_back(0, 1, 6); - realBarcode.emplace_back(1, 5, 6); - realBarcode.emplace_back(1, 6, 7); - - subSimplices = std::vector >(simplices.begin() + 24, simplices.begin() + 27); - subFilValues = std::vector(filValues.begin() + 24, filValues.begin() + 27); - zp.remove_simplices_contiguously(subSimplices, subFilValues); - - realIndices.emplace_back(1, 24, 25); - realBarcode.emplace_back(1, 8, 9); - - subSimplices = std::vector >(simplices.begin() + 27, simplices.begin() + 28); - subFilValues = std::vector(filValues.begin() + 27, filValues.begin() + 28); - zp.insert_simplices_contiguously(subSimplices, subFilValues); - - realIndices.emplace_back(2, 23, 27); - realBarcode.emplace_back(2, 7, 9); - - subSimplices = std::vector >(simplices.begin() + 28, simplices.begin() + 29); - subFilValues = std::vector(filValues.begin() + 28, filValues.begin() + 29); - zp.remove_simplices_contiguously(subSimplices, subFilValues); - - realBarcode.emplace_back(0, 0, std::numeric_limits::infinity()); - realBarcode.emplace_back(0, 9, std::numeric_limits::infinity()); - realBarcode.emplace_back(2, 10, std::numeric_limits::infinity()); - - test_indices(zp, realIndices, filValues); - test_barcode(zp, realBarcode); -} - -BOOST_AUTO_TEST_CASE(zigzag_persistence_batch_max1) { - ZP zp(28, 1); - std::vector realIndices; - std::vector indexToFil(28); - std::vector realBarcode; - realIndices.reserve(5); - realBarcode.reserve(3); - - std::vector > simplices = get_simplices(); - std::vector filValues = get_filtration_values(); - unsigned int currIndex = 0; - - for (unsigned int i = 0; i < 28; ++i) { - if (simplices[i].size() < 3) { - indexToFil[currIndex++] = filValues[i]; - } - } - - std::vector > subSimplices(simplices.begin(), simplices.begin() + 14); - std::vector subFilValues(filValues.begin(), filValues.begin() + 14); - zp.insert_simplices_contiguously(subSimplices, subFilValues); - - realIndices.emplace_back(0, 1, 3); - realIndices.emplace_back(0, 2, 4); - realIndices.emplace_back(0, 7, 8); - realIndices.emplace_back(0, 9, 10); - - realBarcode.emplace_back(0, 0, 1); - realBarcode.emplace_back(0, 0, 1); - - subSimplices = std::vector >(simplices.begin() + 14, simplices.begin() + 16); - subFilValues = std::vector(filValues.begin() + 14, filValues.begin() + 16); - zp.remove_simplices_contiguously(subSimplices, subFilValues); - - subSimplices = std::vector >(simplices.begin() + 16, simplices.begin() + 24); - subFilValues = std::vector(filValues.begin() + 16, filValues.begin() + 24); - zp.insert_simplices_contiguously(subSimplices, subFilValues); - - realIndices.emplace_back(0, 5, 12); - realBarcode.emplace_back(0, 1, 6); - - subSimplices = std::vector >(simplices.begin() + 24, simplices.begin() + 27); - subFilValues = std::vector(filValues.begin() + 24, filValues.begin() + 27); - zp.remove_simplices_contiguously(subSimplices, subFilValues); - - subSimplices = std::vector >(simplices.begin() + 27, simplices.begin() + 28); - subFilValues = std::vector(filValues.begin() + 27, filValues.begin() + 28); - zp.insert_simplices_contiguously(subSimplices, subFilValues); - - subSimplices = std::vector >(simplices.begin() + 27, simplices.begin() + 28); - subFilValues = std::vector(filValues.begin() + 27, filValues.begin() + 28); - zp.remove_simplices_contiguously(subSimplices, subFilValues); + zp.insert_face(27, simplices[27], simplices[27].size() == 0 ? 0 : simplices[27].size() - 1, filValues[27]); + auto id = simplices[28][0]; + zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[28]); - realBarcode.emplace_back(0, 0, std::numeric_limits::infinity()); - realBarcode.emplace_back(0, 9, std::numeric_limits::infinity()); + realBarcode.emplace_back(0, 0, std::numeric_limits::infinity()); + realBarcode.emplace_back(0, 9, std::numeric_limits::infinity()); test_indices(zp, realIndices, indexToFil); test_barcode(zp, realBarcode); From ece12948d6b1aabf9a34a217c396a82d489f63b7 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Fri, 31 May 2024 15:30:24 +0200 Subject: [PATCH 20/51] updates concepts --- .../concept/ZigzagComplex.h | 128 ------------------ ...agPersistenceOptions.h => ZigzagOptions.h} | 0 2 files changed, 128 deletions(-) delete mode 100644 src/Zigzag_persistence/concept/ZigzagComplex.h rename src/Zigzag_persistence/concept/{ZigzagPersistenceOptions.h => ZigzagOptions.h} (100%) diff --git a/src/Zigzag_persistence/concept/ZigzagComplex.h b/src/Zigzag_persistence/concept/ZigzagComplex.h deleted file mode 100644 index a8b03a209a..0000000000 --- a/src/Zigzag_persistence/concept/ZigzagComplex.h +++ /dev/null @@ -1,128 +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): Hannah Schreiber - * - * Copyright (C) 2023 Inria - * - * Modification(s): - * - YYYY/MM Author: Description of the modification - */ - -#ifndef CONCEPT_ZZ_COMPLEX_TYPE_H_ -#define CONCEPT_ZZ_COMPLEX_TYPE_H_ - -/** @file ZigzagComplex.h - * @brief Contains @ref Gudhi::zigzag_persistence::ZigzagComplex concept. - */ - -namespace Gudhi { -namespace zigzag_persistence { - -/** - * @brief Data structure storing the simplices in the current complex. - */ -class ZigzagComplex { - public: - /** - * @brief Signed integer type that needs to be long enough to store the numbers of arrows in the zigzag filtration. - */ - typename Simplex_key; - - /** - * @brief Handle to specify a simplex. - */ - typename Simplex_handle; - - /** - * @brief Handle to specify a vertex. Should be an integer type. - */ - typename Vertex_handle; - - /** - * @brief Type for filtration values. Usually 'double'. - */ - typename Filtration_value; - - /** - * @brief Range of simplex handles over the boundary of a simplex - */ - typename Boundary_simplex_range; - - /** - * @brief Constructor - */ - ZigzagComplex(); - - /** - * @brief Inserts the given simplex in the complex. - * - * @tparam VertexRange Range over the vertices of a simplex. - * @param simplex Simplex to insert represented by its vertices. - * @return A pair of a simplex handle and a boolean. - * The simplex handle represents the inserted simplex and - * the boolean if simplex was already contained in the complex or not. - */ - template - std::pair insert_simplex(const VertexRange& simplex); - - /** - * @brief Removes the given simplex. Assumes that the simplex is maximal and can be safely removed. - * - * @param sh Simplex handle representing the simplex to remove. - */ - void remove_maximal_simplex(Simplex_handle sh); - - /** - * @brief Returns the dimension of the given simplex. - * - * @param sh Simplex handle representing the simplex. - * @return Dimension of @a sh. - */ - int dimension(Simplex_handle sh); - - /** - * @brief Returns the key associated to the given simplex. - * - * @param sh Simplex handle representing the simplex. - * @return The key. - */ - Simplex_key key(Simplex_handle sh); - - /** - * @brief Assignes the given value to the given simplex as a key. - * - * @param sh Simplex handle representing the simplex. - * @param key Values to associate as key. - */ - void assign_key(Simplex_handle sh, Simplex_key key); - - /** - * @brief Finds the given simplex in the complex and returns the associated simplex handle. - * - * @tparam VertexRange Range over the vertices of a simplex. - * @param simplex Simplex to find represented by its vertices. - * @return The simplex handle associated to @a simplex if the simplex is found, @ref null_simplex() otherwise. - */ - template - Simplex_handle find(const VertexRange& simplex); - - /** - * @brief Returns a range of simplex handles representing the boundary of the given simplex. - * - * @param sh Simplex handle representing the simplex. - * @return Range of simplex handles. - */ - Boundary_simplex_range boundary_simplex_range(Simplex_handle sh); - - /** - * @brief Returns a simplex handle representing a non existing simplex. - * - * @return A simplex handle. - */ - Simplex_handle null_simplex(); -}; - -} // namespace zigzag_persistence -} // namespace Gudhi - -#endif // CONCEPT_ZZ_COMPLEX_TYPE_H_ diff --git a/src/Zigzag_persistence/concept/ZigzagPersistenceOptions.h b/src/Zigzag_persistence/concept/ZigzagOptions.h similarity index 100% rename from src/Zigzag_persistence/concept/ZigzagPersistenceOptions.h rename to src/Zigzag_persistence/concept/ZigzagOptions.h From 20ae2490f27a1d3399cb24b431ee1c2fdbc8a025 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Fri, 31 May 2024 15:31:59 +0200 Subject: [PATCH 21/51] updates concepts --- .../concept/ZigzagOptions.h | 70 +++++-------------- 1 file changed, 17 insertions(+), 53 deletions(-) diff --git a/src/Zigzag_persistence/concept/ZigzagOptions.h b/src/Zigzag_persistence/concept/ZigzagOptions.h index 7ee9d9be7d..181116adc4 100644 --- a/src/Zigzag_persistence/concept/ZigzagOptions.h +++ b/src/Zigzag_persistence/concept/ZigzagOptions.h @@ -2,7 +2,7 @@ * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. * Author(s): Hannah Schreiber * - * Copyright (C) 2023 Inria + * Copyright (C) 2024 Inria * * Modification(s): * - YYYY/MM Author: Description of the modification @@ -11,77 +11,41 @@ #ifndef CONCEPT_ZZ_OPTIONS_TYPE_H_ #define CONCEPT_ZZ_OPTIONS_TYPE_H_ -/** @file ZigzagPersistenceOptions.h - * @brief Contains @ref Gudhi::zigzag_persistence::ZigzagPersistenceOptions concept. +/** @file ZigzagOptions.h + * @brief Contains @ref Gudhi::zigzag_persistence::ZigzagOptions concept. */ namespace Gudhi { namespace zigzag_persistence { /** - * @brief List of options used for the matrix maintained for the zigzag persistence computation. + * @brief List of options used for the zigzag persistence computation. */ -struct ZigzagPersistenceOptions { +struct ZigzagOptions { /** - * @brief Type for the coefficient field type. Has to support \f$Z_2\f$. + * @brief Type for the face IDs used internally and other indexations. It must be signed. */ - typename field_coeff_type; + using internal_key = unspecified; /** - * @brief Has to be set to true. Indicates that the computation will be made with \f$Z_2\f$ coefficients. + * @brief Type for the face IDs used at insertion and in the boundaries given as argument. */ - static const bool is_z2 = true; - /** - * @brief Type of the columns in the matrix. - * The available column types are given by @ref Gudhi::persistence_matrix::Column_types. - * The column type has to support row access. - */ - static const Column_types column_type; + using face_key = unspecified; /** - * @brief Has to be set to true. Indicates that the rows should be directly accessible in the matrix. - */ - static const bool has_row_access = true; - /** - * @brief Set to true, if the rows should be intrusive lists or to false if they should be sets. True is recommended. - * Note that intrusive rows are not compatible with certain column types. - */ - static const bool has_intrusive_rows; - /** - * @brief Has to set to true. Indicates that the rows of the matrix can be removed. - */ - static const bool has_removable_rows = true; - /** - * @brief Has to be set to false. Indicates that the matrix should not store birth/death pairs of its columns. - */ - static const bool has_column_pairings = false; - /** - * @brief Has to be set to true. Enables maintaining the matrix while switching columns. - */ - static const bool has_vine_update = true; - /** - * @brief If set to true, the matrix can retrieve the representative cycles for the cycle classes. - * This option is useless for zigzag computation and therefore it is recommended to set it to false. - */ - static const bool can_retrieve_representative_cycles; - /** - * @brief This value has to be defined but will be ignored. + * @brief Type for filtration values. */ - static const bool has_column_compression; - /** - * @brief Has to be set to false. - * Indicates that the matrix should represent the base of the chain complex and not of the boundary group. - */ - static const bool is_of_boundary_type = false; + using filtration_value = unspecified; + /** - * @brief Has to be set to true. Indicates that the columns of the matrix can be removed. + * @brief Type for the dimension values. */ - static const bool has_removable_columns = true; + using dimension_type = unspecified; + /** - * @brief Has to be set to false. - * Indicates that the access to the columns will be done through simplex IDs instead of column positions. + * @brief Column type used by the internal matrix. */ - static const bool is_indexed_by_position = false; + static const Gudhi::persistence_matrix::Column_types column_type; }; } // namespace zigzag_persistence From df39f9c0318ac3eab84838becedd1e7aa042b1ab Mon Sep 17 00:00:00 2001 From: hschreiber Date: Fri, 31 May 2024 15:48:19 +0200 Subject: [PATCH 22/51] zigzag doc update --- .../doc/Intro_zigzag_persistence.h | 2 +- .../include/gudhi/Zigzag_persistence.h | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h index 5fe392a659..afa843fcbd 100644 --- a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h +++ b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h @@ -33,7 +33,7 @@ namespace zigzag_persistence { * filtration anymore. This makes it possible to build very long fine tuned filtrations with relatively small complexes * which can be processed without overreaching memory space. For this purpose, it is possible to feed the module with * information about the filtration "on the fly" to avoid loading the whole filtration at once. Information about the - * current complex and current barcode can be retrieved between any steps. + * current barcode can be retrieved between any steps. * * \subsection zigzagexamples Examples * diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h index b9a686e0a8..527ec44903 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h @@ -68,21 +68,21 @@ struct Default_zigzag_options { //also needs to be implemented. The theory needs to be done first. /** \class Zigzag_persistence Zigzag_persistence.h gudhi/Zigzag_persistence.h * \brief Class computating the zigzag persistent homology of a zigzag - * filtration. Algorithm based on \cite zigzag_reflection. + * filtration. Algorithm based on \cite zigzag. * * \ingroup zigzag_persistence * - * \tparam ZigzagOptions TODO: concept + * \tparam ZigzagOptions Structure following the @ref ZigzagOptions concept. Default value: @ref Default_zigzag_options. */ template class Zigzag_persistence { public: - using Options = ZigzagOptions; - using Matrix_options = Zigzag_matrix_options; /**< Matrix options */ - using internal_key = typename Options::internal_key; - using face_key = typename Options::face_key; - using filtration_value = typename Options::filtration_value; - using dimension_type = typename Options::dimension_type; + using Options = ZigzagOptions; /**< Zigzag options. */ + using Matrix_options = Zigzag_matrix_options; /**< Matrix options. */ + using internal_key = typename Options::internal_key; /**< Key and index type, has to be signed. */ + using face_key = typename Options::face_key; /**< Face ID type from external inputs. */ + using filtration_value = typename Options::filtration_value; /**< Type for filtration values. */ + using dimension_type = typename Options::dimension_type; /**< Type for dimension values. */ /** \brief Structure to store persistence intervals by their index values. * @@ -245,10 +245,10 @@ class Zigzag_persistence { * To retrieve the current persistence diagram at any moment of the filtration, * use @ref get_persistence_diagram or @ref get_index_persistence_diagram. * - * @param min_number_of_faces Minimum number of faces that will be inserted at some point in the filtration. + * @param minNumberOfFaces Minimum number of faces that will be inserted at some point in the filtration. * If the total number of faces is known in advance, the memory allocation can be better optimized. * Default value: 0. - * @param ignore_cycles_above_dim Ignores cycles in dimension larger or equal in the final diagram. + * @param ignoreCyclesAboveDim Ignores cycles in dimension larger or equal in the final diagram. * If -1, no cycles are ignored. Default value: -1. */ Zigzag_persistence(unsigned int minNumberOfFaces = 0, int ignoreCyclesAboveDim = -1) @@ -335,7 +335,7 @@ class Zigzag_persistence { } /** - * @brief Returns the ``index persistence diagram'' of the current filtration, that is, the pairs of atomic arrow + * @brief Returns the "index persistence diagram" of the current filtration, that is, the pairs of atomic arrow * numbers corresponding to a birth-death pair. Does not contain points at infinity, only the cycle classes which * already died are represented. * From 03e7b7de371a15f06d64097a5f7b0338bf702d67 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Fri, 31 May 2024 16:56:45 +0200 Subject: [PATCH 23/51] small fixes --- .../example_zzfiltration_from_file.cpp | 23 +++----- .../example/zigzag_filtration_example.txt | 58 +++++++++---------- .../include/gudhi/Zigzag_persistence.h | 43 +++++++------- .../test/zigzag_persistence_unit_test.cpp | 4 +- 4 files changed, 62 insertions(+), 66 deletions(-) diff --git a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp index 37ebf7185f..21f3e56f4e 100644 --- a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp +++ b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp @@ -25,20 +25,20 @@ enum lineType : int { INCLUSION, REMOVAL, COMMENT }; void print_barcode(ZP& zp) { std::clog << std::endl << "Current barcode:" << std::endl; for (auto& bar : zp.get_persistence_diagram(0, true)) { - std::clog << std::floor(bar.birth()) << " - "; + std::clog << bar.birth() << " - "; if (bar.death() == std::numeric_limits::infinity()) { std::clog << "inf"; } else { - std::clog << std::floor(bar.death()); + std::clog << bar.death(); } std::clog << " (" << bar.dim() << ")" << std::endl; } std::clog << std::endl; } -lineType read_operation(std::string& line, std::vector& vertices, double& timestamp) { +lineType read_operation(std::string& line, std::vector& faces, double& timestamp) { lineType type; - vertices.clear(); + faces.clear(); id_handle num; size_t current = line.find_first_not_of(' ', 0); @@ -51,30 +51,25 @@ lineType read_operation(std::string& line, std::vector& vertices, dou else if (line[current] == '#') return COMMENT; else { - std::clog << "Syntaxe error in file." << std::endl; + std::clog << "(1) Syntaxe error in file." << std::endl; exit(0); } current = line.find_first_not_of(' ', current + 1); if (current == std::string::npos) { - std::clog << "Syntaxe error in file." << std::endl; + std::clog << "(2) Syntaxe error in file." << std::endl; exit(0); } size_t next = line.find_first_of(' ', current); timestamp = std::stod(line.substr(current, next - current)); current = line.find_first_not_of(' ', next); - if (current == std::string::npos) { - std::clog << "Syntaxe error in file." << std::endl; - exit(0); - } - - do { + while (current != std::string::npos) { next = line.find_first_of(' ', current); num = std::stoi(line.substr(current, next - current)); - vertices.push_back(num); + faces.push_back(num); current = line.find_first_not_of(' ', next); - } while (current != std::string::npos); + } return type; } diff --git a/src/Zigzag_persistence/example/zigzag_filtration_example.txt b/src/Zigzag_persistence/example/zigzag_filtration_example.txt index e0fd923d9a..e34c23d027 100644 --- a/src/Zigzag_persistence/example/zigzag_filtration_example.txt +++ b/src/Zigzag_persistence/example/zigzag_filtration_example.txt @@ -8,42 +8,42 @@ # If removal: id of the simplex to remove + dimension # #: comment line -i 0 -i 0 -i 0 +i 0. +i 0. +i 0. -i 1 0 1 -i 1 0 2 -i 1 +i 1. 0 1 +i 1. 0 2 +i 1. -i 2 1 2 -i 2 -i 2 5 7 +i 2. 1 2 +i 2. +i 2. 5 7 -i 3 -i 3 3 4 6 -i 3 7 9 -i 3 5 9 +i 3. +i 3. 3 4 6 +i 3. 7 9 +i 3. 5 9 -i 4 8 11 12 +i 4. 8 11 12 -r 5 10 2 +r 5. 10 2 -r 6 13 2 -i 6 1 7 -i 6 3 4 6 +r 6. 13 2 +i 6. 1 7 +i 6. 3 4 6 -i 7 2 7 -i 7 8 11 12 -i 7 0 7 -i 7 4 18 20 -i 7 6 16 18 -i 7 3 16 20 +i 7. 2 7 +i 7. 8 11 12 +i 7. 0 7 +i 7. 4 18 20 +i 7. 6 16 18 +i 7. 3 16 20 -r 8 19 2 +r 8. 19 2 -r 9 8 1 -r 9 12 1 -i 9 17 21 22 23 +r 9. 8 1 +r 9. 12 1 +i 9. 17 21 22 23 -r 10 27 3 \ No newline at end of file +r 10. 27 3 \ No newline at end of file diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h index 527ec44903..c2331e9dca 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h @@ -96,15 +96,15 @@ class Zigzag_persistence { Interval(int dim, value_type b, value_type d) : dim_(dim), b_(b), d_(d) {} /** Returns the dimension of the homological feature corresponding to the interval. */ int dim() const { return dim_; } - /** Returns the birth index of the interval.*/ + /** Returns the birth value of the interval.*/ value_type birth() const { return b_; } - /** Returns the death index of the interval.*/ + /** Returns the death value of the interval.*/ value_type death() const { return d_; } protected: - int dim_; // homological dimension - value_type b_; // filtration value associated to birth index - value_type d_; // filtration value associated to death index + int dim_; /**< Homological dimension. */ + value_type b_; /**< Value associated to the interval birth. */ + value_type d_; /**< Value associated to the interval death. */ }; using Index_interval = Interval; @@ -155,7 +155,7 @@ class Zigzag_persistence { private: /** \brief Maintains the birth ordering \f$\leq_b\f$. * - * \details Contains an std::map of size the number of + * \details Contains a map of size the number of * non-zero rows of the homology matrix, at any time during the computation of * zigzag persistence. * @@ -381,8 +381,7 @@ class Zigzag_persistence { } /** - * @brief Returns the current persistence diagram ordered first by length, than by dimension, - * than by birth value and finally by death value. + * @brief Returns the current persistence diagram. * * @param shortestInterval Threshold. Every bar shorter than the given value will be ignored. Default value: 0. * @param includeInfinitBars If set to true, infinit bars are included in the diagram. Default value: false. @@ -390,18 +389,18 @@ class Zigzag_persistence { */ std::vector get_persistence_diagram(filtration_value shortestInterval = 0., bool includeInfinitBars = false) { - auto comp = [](Filtration_value_interval p, Filtration_value_interval q) { - if (p.length() != q.length()) { - return p.length() > q.length(); - } // longest 1st - if (p.dim() != q.dim()) { - return p.dim() < q.dim(); - } // lower dimension first - if (p.birth() != q.birth()) { - return p.birth() < q.birth(); - } // lex order - return p.death() < q.death(); - }; + // auto comp = [](Filtration_value_interval p, Filtration_value_interval q) { + // if (p.length() != q.length()) { + // return p.length() > q.length(); + // } // longest 1st + // if (p.dim() != q.dim()) { + // return p.dim() < q.dim(); + // } // lower dimension first + // if (p.birth() != q.birth()) { + // return p.birth() < q.birth(); + // } // lex order + // return p.death() < q.death(); + // }; std::vector diag = _get_persistence_diagram(shortestInterval); @@ -409,7 +408,7 @@ class Zigzag_persistence { _retrieve_infinit_bars(diag); } - std::stable_sort(diag.begin(), diag.end(), comp); + // std::stable_sort(diag.begin(), diag.end(), comp); return diag; } @@ -625,7 +624,7 @@ class Zigzag_persistence { std::unordered_map handleToKey_; /**< Map from input keys to internal keys. */ dimension_type dimMax_; /**< Maximal dimension of a bar to record. */ Matrix_type matrix_; /**< Matrix storing a base of the current chain complex. */ - std::unordered_map births_; /**< Map face index in F to corresponding birth. */ + std::unordered_map births_; /**< Map face index in F to corresponding birth. */ Birth_ordering birthOrdering_; /**< Maintains persistenceDiagram_; /**< Stores current closed persistence intervals. */ internal_key numArrow_; /**< Current arrow number. */ diff --git a/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp b/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp index 65e80323dc..f1596e714d 100644 --- a/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp +++ b/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp @@ -48,9 +48,11 @@ BOOST_AUTO_TEST_CASE(constructor) { } void test_barcode(ZP& zp, std::vector& barcode) { + auto bars = zp.get_persistence_diagram(0, true); + std::stable_sort(bars.begin(), bars.end(), cmp_intervals_by_length()); std::stable_sort(barcode.begin(), barcode.end(), cmp_intervals_by_length()); auto it = barcode.begin(); - for (const auto& interval : zp.get_persistence_diagram(0, true)) { + for (const auto& interval : bars) { BOOST_CHECK_EQUAL(interval.dim(), it->dim()); BOOST_CHECK_EQUAL(interval.birth(), it->birth()); BOOST_CHECK_EQUAL(interval.death(), it->death()); From 26161a7be7f5401150d997db767114d16e53579a Mon Sep 17 00:00:00 2001 From: hschreiber Date: Fri, 31 May 2024 17:21:22 +0200 Subject: [PATCH 24/51] persistence diagram: list -> vector --- .../include/gudhi/Zigzag_persistence.h | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h index c2331e9dca..413ba61297 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h @@ -341,7 +341,7 @@ class Zigzag_persistence { * * @return Reference to the list of intervals. */ - const std::list& get_index_persistence_diagram() const { return persistenceDiagram_; } + const std::vector& get_index_persistence_diagram() const { return persistenceDiagram_; } /** * @brief Returns the filtration values \f$[f(b),f(d)]\f$ associated to the indices \f$[b,d]\f$ which are retrieved @@ -389,18 +389,18 @@ class Zigzag_persistence { */ std::vector get_persistence_diagram(filtration_value shortestInterval = 0., bool includeInfinitBars = false) { - // auto comp = [](Filtration_value_interval p, Filtration_value_interval q) { - // if (p.length() != q.length()) { - // return p.length() > q.length(); - // } // longest 1st - // if (p.dim() != q.dim()) { - // return p.dim() < q.dim(); - // } // lower dimension first - // if (p.birth() != q.birth()) { - // return p.birth() < q.birth(); - // } // lex order - // return p.death() < q.death(); - // }; + auto comp = [](Filtration_value_interval p, Filtration_value_interval q) { + if (p.length() != q.length()) { + return p.length() > q.length(); + } // longest 1st + if (p.dim() != q.dim()) { + return p.dim() < q.dim(); + } // lower dimension first + if (p.birth() != q.birth()) { + return p.birth() < q.birth(); + } // lex order + return p.death() < q.death(); + }; std::vector diag = _get_persistence_diagram(shortestInterval); @@ -408,7 +408,7 @@ class Zigzag_persistence { _retrieve_infinit_bars(diag); } - // std::stable_sort(diag.begin(), diag.end(), comp); + std::stable_sort(diag.begin(), diag.end(), comp); return diag; } @@ -516,7 +516,7 @@ class Zigzag_persistence { births_.erase(chainFp); // Update persistence diagram with left interval [fil(b_max) ; fil(m)) - persistenceDiagram_.emplace_back(dim - 1, maxb, numArrow_); //-1);// + persistenceDiagram_.emplace_back(dim - 1, maxb, numArrow_); } /** @@ -626,7 +626,7 @@ class Zigzag_persistence { Matrix_type matrix_; /**< Matrix storing a base of the current chain complex. */ std::unordered_map births_; /**< Map face index in F to corresponding birth. */ Birth_ordering birthOrdering_; /**< Maintains persistenceDiagram_; /**< Stores current closed persistence intervals. */ + std::vector persistenceDiagram_; /**< Stores current closed persistence intervals. */ internal_key numArrow_; /**< Current arrow number. */ filtration_value previousFiltrationValue_; /**< Filtration value of the previous arrow. */ /** From 9c444097c7ebc2ca658cd6e4c1cc57ee2032753d Mon Sep 17 00:00:00 2001 From: hschreiber Date: Fri, 31 May 2024 17:51:38 +0200 Subject: [PATCH 25/51] removal of useless abs --- .../include/gudhi/Zigzag_persistence.h | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h index 413ba61297..c3b02d50d2 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h @@ -139,7 +139,7 @@ class Zigzag_persistence { if (Base::b_ == Base::d_) { return 0; } // otherwise inf - inf would return nan. - return std::abs(Base::b_ - Base::d_); + return Base::d_ - Base::b_; } /** * @brief Returns the absolute length of the log values of birth and death, i.e. \f$|\log d - \log b|\f$. @@ -148,7 +148,7 @@ class Zigzag_persistence { if (Base::b_ == Base::d_) { return 0; } // otherwise inf - inf would return nan. - return std::abs(std::log2(static_cast(Base::b_)) - std::log2(static_cast(Base::d_))); + return std::log2(static_cast(Base::d_)) - std::log2(static_cast(Base::b_)); } }; @@ -389,18 +389,18 @@ class Zigzag_persistence { */ std::vector get_persistence_diagram(filtration_value shortestInterval = 0., bool includeInfinitBars = false) { - auto comp = [](Filtration_value_interval p, Filtration_value_interval q) { - if (p.length() != q.length()) { - return p.length() > q.length(); - } // longest 1st - if (p.dim() != q.dim()) { - return p.dim() < q.dim(); - } // lower dimension first - if (p.birth() != q.birth()) { - return p.birth() < q.birth(); - } // lex order - return p.death() < q.death(); - }; + // auto comp = [](Filtration_value_interval p, Filtration_value_interval q) { + // if (p.length() != q.length()) { + // return p.length() > q.length(); + // } // longest 1st + // if (p.dim() != q.dim()) { + // return p.dim() < q.dim(); + // } // lower dimension first + // if (p.birth() != q.birth()) { + // return p.birth() < q.birth(); + // } // lex order + // return p.death() < q.death(); + // }; std::vector diag = _get_persistence_diagram(shortestInterval); @@ -408,7 +408,7 @@ class Zigzag_persistence { _retrieve_infinit_bars(diag); } - std::stable_sort(diag.begin(), diag.end(), comp); + // std::stable_sort(diag.begin(), diag.end(), comp); return diag; } From 2b46f1396c108609f7ccb8252110eec9bb3bd5bc Mon Sep 17 00:00:00 2001 From: hschreiber Date: Thu, 20 Jun 2024 12:17:37 +0200 Subject: [PATCH 26/51] split of zigzag computation and filtration values management --- .../concept/ZigzagOptions.h | 31 +- .../example_simple_zigzag_filtration.cpp | 4 +- .../example_zzfiltration_from_file.cpp | 40 +- .../gudhi/Filtered_zigzag_persistence.h | 553 ++++++++++++++++++ .../include/gudhi/Zigzag_persistence.h | 501 +++++----------- src/Zigzag_persistence/test/CMakeLists.txt | 11 +- .../filtered_zigzag_persistence_unit_test.cpp | 477 +++++++++++++++ .../test/zigzag_persistence_unit_test.cpp | 232 +++----- 8 files changed, 1308 insertions(+), 541 deletions(-) create mode 100644 src/Zigzag_persistence/include/gudhi/Filtered_zigzag_persistence.h create mode 100644 src/Zigzag_persistence/test/filtered_zigzag_persistence_unit_test.cpp diff --git a/src/Zigzag_persistence/concept/ZigzagOptions.h b/src/Zigzag_persistence/concept/ZigzagOptions.h index 181116adc4..712d5df2a1 100644 --- a/src/Zigzag_persistence/concept/ZigzagOptions.h +++ b/src/Zigzag_persistence/concept/ZigzagOptions.h @@ -12,16 +12,19 @@ #define CONCEPT_ZZ_OPTIONS_TYPE_H_ /** @file ZigzagOptions.h - * @brief Contains @ref Gudhi::zigzag_persistence::ZigzagOptions concept. + * @brief Contains @ref Gudhi::zigzag_persistence::ZigzagOptions and + * @ref Gudhi::zigzag_persistence::FilteredZigzagOptions concept. */ namespace Gudhi { namespace zigzag_persistence { /** - * @brief List of options used for the zigzag persistence computation. + * @ingroup zigzag_persistence + * + * @brief List of options used for the filtered zigzag persistence computation. */ -struct ZigzagOptions { +struct FilteredZigzagOptions { /** * @brief Type for the face IDs used internally and other indexations. It must be signed. */ @@ -48,6 +51,28 @@ struct ZigzagOptions { static const Gudhi::persistence_matrix::Column_types column_type; }; +/** + * @ingroup zigzag_persistence + * + * @brief List of options used for the zigzag persistence computation. + */ +struct ZigzagOptions { + /** + * @brief Type for the face IDs used internally and other indexations. It must be signed. + */ + using internal_key = unspecified; + + /** + * @brief Type for the dimension values. + */ + using dimension_type = unspecified; + + /** + * @brief Column type used by the internal matrix. + */ + static const Gudhi::persistence_matrix::Column_types column_type; +}; + } // namespace zigzag_persistence } // namespace Gudhi diff --git a/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp b/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp index a13b998906..f99fd9bc59 100644 --- a/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp +++ b/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp @@ -11,9 +11,9 @@ #include #include -#include +#include -using ZP = Gudhi::zigzag_persistence::Zigzag_persistence<>; +using ZP = Gudhi::zigzag_persistence::Filtered_zigzag_persistence_with_storage<>; using face_handle = ZP::face_key; using filtration_value = ZP::filtration_value; using Interval_filtration = ZP::Filtration_value_interval; diff --git a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp index 21f3e56f4e..bd2cb61bdf 100644 --- a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp +++ b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp @@ -13,29 +13,15 @@ #include #include -#include +#include -using ZP = Gudhi::zigzag_persistence::Zigzag_persistence<>; +using ZP = Gudhi::zigzag_persistence::Filtered_zigzag_persistence<>; using id_handle = ZP::face_key; using filtration_value = ZP::filtration_value; -using Interval_filtration = ZP::Filtration_value_interval; +using dimension_type = ZP::dimension_type; enum lineType : int { INCLUSION, REMOVAL, COMMENT }; -void print_barcode(ZP& zp) { - std::clog << std::endl << "Current barcode:" << std::endl; - for (auto& bar : zp.get_persistence_diagram(0, true)) { - std::clog << bar.birth() << " - "; - if (bar.death() == std::numeric_limits::infinity()) { - std::clog << "inf"; - } else { - std::clog << bar.death(); - } - std::clog << " (" << bar.dim() << ")" << std::endl; - } - std::clog << std::endl; -} - lineType read_operation(std::string& line, std::vector& faces, double& timestamp) { lineType type; faces.clear(); @@ -85,7 +71,13 @@ int main(int argc, char* const argv[]) { std::string line; std::ifstream file(argv[1]); - ZP zp; + + //std::clog could be replaced by any other output stream + ZP zp([](dimension_type dim, filtration_value birth, filtration_value death) { + std::clog << "[" << dim << "] "; + std::clog << birth << " - " << death; + std::clog << std::endl; + }); if (file.is_open()) { std::vector data; @@ -97,15 +89,12 @@ int main(int argc, char* const argv[]) { double lastTimestamp = timestamp; // first operation has to be an insertion. zp.insert_face(id, data, 0, timestamp); - std::cout << line << std::endl; while (getline(file, line, '\n')) { type = read_operation(line, data, timestamp); if (type != COMMENT && lastTimestamp != timestamp) { - print_barcode(zp); lastTimestamp = timestamp; } - if (type != COMMENT) std::cout << line << std::endl; if (type == INCLUSION) { ++id; @@ -116,7 +105,6 @@ int main(int argc, char* const argv[]) { zp.remove_face(data[0], data[1], timestamp); } } - print_barcode(zp); file.close(); } else { @@ -124,5 +112,13 @@ int main(int argc, char* const argv[]) { file.setstate(std::ios::failbit); } + //retrieve infinit bars remaining at the end + //again std::clog could be replaced by any other output stream + zp.get_current_infinit_intervals([](dimension_type dim, filtration_value birth) { + std::clog << "[" << dim << "] "; + std::clog << birth << " - inf"; + std::clog << std::endl; + }); + return 0; } \ No newline at end of file diff --git a/src/Zigzag_persistence/include/gudhi/Filtered_zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/Filtered_zigzag_persistence.h new file mode 100644 index 0000000000..e7735ac1d0 --- /dev/null +++ b/src/Zigzag_persistence/include/gudhi/Filtered_zigzag_persistence.h @@ -0,0 +1,553 @@ +/* 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): Clément Maria + * + * Copyright (C) 2021 Inria + * + * Modification(s): + * - 2023/05 Hannah Schreiber: Rework of the interface, reorganization and debug + * - 2023/05 Hannah Schreiber: Addition of infinit bars + * - 2024/06 Hannah Schreiber: Separation of the zigzag algorithm from the filtration value management + * - YYYY/MM Author: Description of the modification + */ + +/** + * @file Filtered_zigzag_persistence.h + * @author Clément Maria, Hannah Schreiber + * @brief Contains the implementation of the @ref Interval structure and the + * @ref Gudhi::zigzag_persistence::Filtered_zigzag_persistence_with_storage and + * @ref Gudhi::zigzag_persistence::Filtered_zigzag_persistence classes. + */ + +#ifndef FILTERED_ZIGZAG_PERSISTENCE_H_ +#define FILTERED_ZIGZAG_PERSISTENCE_H_ + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace Gudhi { +namespace zigzag_persistence { + +/** + * @ingroup zigzag_persistence + * + * @brief Structure to store persistence intervals by their birth and death values. + * + * @tparam value_type Type for the birth and death indices. + */ +template +struct Interval { + /** + * @brief Default constructor. All values are initialized with default values. + */ + Interval() {} + /** + * @brief Constructor. + * + * @param dim Dimension of the cycle. + * @param b Birth index or value of the cycle. + * @param d Death index or value of the cycle. + */ + Interval(int dim, value_type b, value_type d) : dim_(dim), b_(b), d_(d) {} + /** + * @brief Returns the dimension of the homological feature corresponding to the interval. + * + * @return Stored dimension. + */ + int dim() const { return dim_; } + /** + * @brief Returns the birth value of the interval. + * + * @return The stored birth. + */ + value_type birth() const { return b_; } + /** + * @brief Returns the death value of the interval. + * + * @return The stored death. + */ + value_type death() const { return d_; } + + protected: + int dim_; /**< Homological dimension. */ + value_type b_; /**< Value associated to the interval birth. */ + value_type d_; /**< Value associated to the interval death. */ +}; + +/** + * @ingroup zigzag_persistence + * + * @brief Default options for @ref Filtered_zigzag_persistence_with_storage and @ref Filtered_zigzag_persistence. + */ +struct Default_filtered_zigzag_options { + using internal_key = int; /**< Face ID used internaly, must be signed. */ + using face_key = int; /**< Face ID used in the given boundaries. */ + using filtration_value = double; /**< Filtration value type. */ + using dimension_type = int; /**< Dimension value type. */ + /** + * @brief Column type use by the internal matrix. + */ + static const Gudhi::persistence_matrix::Column_types column_type = + Gudhi::persistence_matrix::Column_types::INTRUSIVE_LIST; +}; + +/** + * @ingroup zigzag_persistence + * + * @brief Class computating the zigzag persistent homology of a zigzag filtration. Algorithm based on \cite zigzag. + * Eventhough the insertions and removals are given in a "stream-like" way, the barcode and other values are + * stored during the whole process and not removed. It is therefore suited for smaller filtrations where the clean + * ups produce a higher overhead than the memory consumption. + * + * @tparam FilteredZigzagOptions Structure following the @ref FilteredZigzagOptions concept. + * Default value: @ref Default_filtered_zigzag_options. + */ +template +class Filtered_zigzag_persistence_with_storage +{ + public: + using Options = FilteredZigzagOptions; /**< Zigzag options. */ + using internal_key = typename Options::internal_key; /**< Key and index type, has to be signed. */ + using face_key = typename Options::face_key; /**< Face ID type from external inputs. */ + using filtration_value = typename Options::filtration_value; /**< Type for filtration values. */ + using dimension_type = typename Options::dimension_type; /**< Type for dimension values. */ + using Index_interval = Interval; /**< Persistence interval type. */ + + /** \brief Structure to store persistence intervals by their filtration values. + * + * \details By convention, interval \f$[b;d]\f$ are + * closed for finite indices b and d, and open for left-infinite and/or + * right-infinite endpoints. + */ + struct Filtration_value_interval : Interval { + private: + using Base = Interval; + + public: + /** + * @brief Default constructor + */ + Filtration_value_interval() : Base() {} + /** + * @brief Construct a new interval with given parameters + * + * @param dim Dimension of the interval. + * @param b Start value of the interval. + * @param d End value of the interval. + */ + Filtration_value_interval(int dim, filtration_value b, filtration_value d) : Base(dim, b, d) {} + + /** + * @brief Returns the absolute length of the interval \f$|d-b|\f$. + */ + filtration_value length() const { + if (Base::b_ == Base::d_) { + return 0; + } // otherwise inf - inf would return nan. + return Base::d_ - Base::b_; + } + /** + * @brief Returns the absolute length of the log values of birth and death, i.e. \f$|\log d - \log b|\f$. + */ + filtration_value log_length() const { + if (Base::b_ == Base::d_) { + return 0; + } // otherwise inf - inf would return nan. + return std::log2(static_cast(Base::d_)) - std::log2(static_cast(Base::b_)); + } + }; + + /** + * @brief Constructor. + * @details After construction of the class, the zigzag filtration should be given in a streaming like way, i.e., + * call @ref insert_face, @ref remove_face or @ref apply_identity for each step of the filtration in order of + * the filtration. To retrieve the current persistence diagram at any moment of the filtration, + * use @ref get_persistence_diagram or @ref get_index_persistence_diagram. + * + * @param minNumberOfFaces Minimum number of faces that will be in a complex at some point in the filtration. + * If the maximal number of faces is known in advance, the memory allocation can be better optimized. + * Default value: 0. + * @param ignoreCyclesAboveDim Ignores cycles in dimension larger or equal in the final diagram. + * If -1, no cycles are ignored. Default value: -1. + */ + Filtered_zigzag_persistence_with_storage(unsigned int minNumberOfFaces = 0, int ignoreCyclesAboveDim = -1) + : dimMax_(ignoreCyclesAboveDim), + persistenceDiagram_(), + numArrow_(-1), + previousFiltrationValue_(std::numeric_limits::infinity()), + pers_( + [&](dimension_type dim, internal_key birth, internal_key death) { + if (dimMax_ == -1 || (dimMax_ != -1 && dim < dimMax_)) { // don't record intervals over max dim + persistenceDiagram_.emplace_back(dim, birth, death); + } + }, + minNumberOfFaces) {} + + /** + * @brief Updates the zigzag persistence diagram after the insertion of the given face. + * + * @tparam BoundaryRange Range type needing begin and end members. + * @param faceID ID representing the inserted face. + * @param boundary Boundary of the inserted face. The range should be composed of the IDs of all faces contained in + * the boundary (i.e. with non-zero coefficients), using the ID specified as `faceID` when the corresponding face + * was previously inserted (recall that the faces should be inserted in order of filtration). + * @param dimension Dimension of the inserted face. + * @param filtrationValue Filtration value associated to the face. + * Assumed to be larger or equal to previously used filtration values. + */ + template > + void insert_face(face_key faceID, + const BoundaryRange& boundary, + dimension_type dimension, + filtration_value filtrationValue) { + ++numArrow_; + + if (dimMax_ != -1 && dimension > dimMax_) { + pers_.apply_identity(); + return; + } + + if (filtrationValue != previousFiltrationValue_) // check whether the filt value has changed + { // consecutive pairs (i,f), (j,f') mean faces of index k in [i,j-1] have + previousFiltrationValue_ = filtrationValue; // filtration value f + filtrationValues_.emplace_back(numArrow_, previousFiltrationValue_); + } + + [[maybe_unused]] auto res = handleToKey_.try_emplace(faceID, numArrow_); + + GUDHI_CHECK(res.second, "Zigzag_persistence::insert_face - face already in the complex"); + + // Reduce the boundary of zzsh in the basis of cycles. + // Compute the keys of the faces of the boundary of zzsh. + std::set translatedBoundary; // set maintains the natural order on indices + for (auto b : boundary) { + translatedBoundary.insert(handleToKey_.at(b)); // TODO: add possibilities of coefficients + } + + pers_.insert_face(translatedBoundary, dimension); + } + + /** + * @brief Updates the zigzag persistence diagram after the removal of the given face. + * + * @param faceID ID representing the face to remove. Should be the same than the one used to insert it. + * @param dimension Dimension of the face. + * @param filtrationValue Filtration value associated to the removal. + * Assumed to be larger or equal to previously used filtration values. + */ + void remove_face(face_key faceID, dimension_type dimension, filtration_value filtrationValue) { + ++numArrow_; + + if (dimMax_ != -1 && dimension > dimMax_) { + pers_.apply_identity(); + return; + } + + auto it = handleToKey_.find(faceID); + GUDHI_CHECK(it != handleToKey_.end(), "Zigzag_persistence::remove_face - face not in the complex"); + + if (filtrationValue != previousFiltrationValue_) // check whether the filt value has changed + { // consecutive pairs (i,f), (j,f') mean faces of index k in [i,j-1] have + previousFiltrationValue_ = filtrationValue; // filtration value f + filtrationValues_.emplace_back(numArrow_, previousFiltrationValue_); + } + + pers_.remove_face(it->second, dimension); + handleToKey_.erase(it); + } + + /** + * @brief To use when a face is neither inserted nor removed, but the filtration moves along the identity operator + * on homology level. Useful to keep the birth/death indices aligned when insertions/removals are purposly skipped + * to avoid useless computation. + */ + void apply_identity() { + ++numArrow_; + pers_.apply_identity(); + } + + /** + * @brief Returns the "index persistence diagram" of the current filtration, that is, the pairs of atomic arrow + * numbers corresponding to a birth-death pair. Does not contain points at infinity, only the cycle classes which + * already died are represented. + * + * @return Reference to the list of intervals. + */ + const std::vector& get_index_persistence_diagram() const { return persistenceDiagram_; } + + /** + * @brief Returns the filtration values \f$[f(b),f(d)]\f$ associated to the indices \f$[b,d]\f$ which are retrieved + * by @ref get_index_persistence_diagram. + * + * @param birthKey Birth index + * @param deathKey Death index + * @return A pair of filtration values associated to the given indices. + */ + std::pair map_index_to_filtration_value(internal_key birthKey, + internal_key deathKey) const { + // filtration_values_ must be sorted by increasing keys. + auto itBirth = // lower_bound(x) returns leftmost y s.t. x <= y + std::lower_bound( + filtrationValues_.begin(), filtrationValues_.end(), + std::pair(birthKey, std::numeric_limits::infinity()), + [](std::pair p1, std::pair p2) { + return p1.first < p2.first; + }); + if (itBirth == filtrationValues_.end() || itBirth->first > birthKey) { + --itBirth; + } + // it points to the rightmost z such that z <= x + + auto itDeath = // + std::lower_bound( + filtrationValues_.begin(), filtrationValues_.end(), + std::pair(deathKey, std::numeric_limits::infinity()), + [](std::pair p1, std::pair p2) { + return p1.first < p2.first; + }); + if (itDeath == filtrationValues_.end() || itDeath->first > deathKey) { + --itDeath; + } + + return std::make_pair(itBirth->second, itDeath->second); + } + + /** + * @brief Returns the current persistence diagram. + * + * @param shortestInterval Threshold. Every bar shorter than the given value will be ignored. Default value: 0. + * @param includeInfinitBars If set to true, infinit bars are included in the diagram. Default value: false. + * @return A vector of pairs of filtration values representing the persistence diagram. + */ + std::vector get_persistence_diagram(filtration_value shortestInterval = 0., + bool includeInfinitBars = false) { + std::vector diag = _get_persistence_diagram(shortestInterval); + + if (includeInfinitBars) { + _retrieve_infinit_bars(diag); + } + + return diag; + } + + private: + std::unordered_map handleToKey_; /**< Map from input keys to internal keys. */ + dimension_type dimMax_; /**< Maximal dimension of a bar to record. */ + std::vector persistenceDiagram_; /**< Stores current closed persistence intervals. */ + internal_key numArrow_; /**< Current arrow number. */ + filtration_value previousFiltrationValue_; /**< Filtration value of the previous arrow. */ + /** + * @brief filtrationValues_ stores consecutive pairs (i,f) , (j,f') with f != f', + * meaning that all inserted faces with key in [i;j-1] have filtration value f, + * i is the smallest face index whose face has filtration value f. + */ + std::vector > filtrationValues_; + Zigzag_persistence pers_; /**< Class computing the pairs. */ + + /** + * @brief Returns the current persistence diagram without infinit bars. + * + * @param shortestInterval Intervals shorter than the given value are ignored. + * @return Vector of intervals. + */ + std::vector _get_persistence_diagram(filtration_value shortestInterval) { + std::vector diag; + diag.reserve(persistenceDiagram_.size()); + + std::stable_sort(filtrationValues_.begin(), filtrationValues_.end(), + [](std::pair p1, std::pair p2) { + return p1.first < p2.first; + }); + + for (auto bar : persistenceDiagram_) { + filtration_value birth, death; + std::tie(birth, death) = map_index_to_filtration_value(bar.birth(), bar.death()); + if (birth > death) { + std::swap(birth, death); + } + + if (death - birth > shortestInterval) { + diag.emplace_back(bar.dim(), birth, death); + } + } + + return diag; + } + + /** + * @brief Computes the births of the current essential cycles. + * + * @param diag Reference to vector where to store the infinit bars. + */ + void _retrieve_infinit_bars(std::vector& diag) { + auto birth = [this](internal_key birthKey) { + auto itBirth = // lower_bound(x) returns leftmost y s.t. x <= y + std::lower_bound( + filtrationValues_.begin(), filtrationValues_.end(), + std::pair(birthKey, std::numeric_limits::infinity()), + [](std::pair p1, std::pair p2) { + return p1.first < p2.first; + }); + if (itBirth == filtrationValues_.end() || itBirth->first > birthKey) { + --itBirth; + } + return itBirth->second; + }; + + auto stream_infinit_interval = [&](dimension_type dim, internal_key birthIndex) { + if (dimMax_ == -1 || (dimMax_ != -1 && dim < dimMax_)) + diag.emplace_back(dim, birth(birthIndex), std::numeric_limits::infinity()); + }; + + pers_.get_current_infinit_intervals(stream_infinit_interval); + } +}; // end class Filtered_zigzag_persistence_with_storage + +/** + * @ingroup zigzag_persistence + * + * @brief Class computating the zigzag persistent homology of a zigzag filtration. Algorithm based on \cite zigzag. + * + * @tparam FilteredZigzagOptions Structure following the @ref FilteredZigzagOptions concept. + * Default value: @ref Default_filtered_zigzag_options. + */ +template +class Filtered_zigzag_persistence { + public: + using Options = FilteredZigzagOptions; /**< Zigzag options. */ + using internal_key = typename Options::internal_key; /**< Key and index type, has to be signed. */ + using face_key = typename Options::face_key; /**< Face ID type from external inputs. */ + using filtration_value = typename Options::filtration_value; /**< Type for filtration values. */ + using dimension_type = typename Options::dimension_type; /**< Type for dimension values. */ + + /** + * @brief Constructor. + * @details After construction of the class, the zigzag filtration should be given in a streaming like way, i.e., + * call @ref insert_face, @ref remove_face or @ref apply_identity for each step of the filtration in order of + * the filtration. The bars of the diagram are retrieved via the given callback method every time + * a pair with non-zero length is closed. To retrieve the open/infinit bars, use @ref get_current_infinit_intervals. + * + * @param stream_interval Callback method to process the birth and death values of a persistence bar. + * Has to take three arguments as input: first the dimension of the cycle, then the birth value of the cycle + * and third the death value of the cycle. The values corresponds to the filtration values which were given at + * insertions or removals. + * @param minNumberOfFaces Minimum number of faces that will be in a complex at some point in the filtration. + * If the maximal number of faces is known in advance, the memory allocation can be better optimized. + * Default value: 0. + */ + Filtered_zigzag_persistence(std::function stream_interval, + unsigned int minNumberOfFaces = 0) + : handleToKey_(minNumberOfFaces), + numArrow_(-1), + keyToFiltrationValue_(minNumberOfFaces), + stream_interval_(std::move(stream_interval)), + pers_( + [&](dimension_type dim, internal_key birth, internal_key death) { + auto itB = keyToFiltrationValue_.find(birth); + auto itD = keyToFiltrationValue_.find(death); + if (itB->second != itD->second) stream_interval_(dim, itB->second, itD->second); + keyToFiltrationValue_.erase(itB); + keyToFiltrationValue_.erase(itD); + }, + minNumberOfFaces) {} + + /** + * @brief Updates the zigzag persistence diagram after the insertion of the given face. + * + * @tparam BoundaryRange Range type needing begin and end members. + * @param faceID ID representing the inserted face. + * @param boundary Boundary of the inserted face. The range should be composed of the IDs of all faces contained in + * the boundary (i.e. with non-zero coefficients), using the ID specified as `faceID` when the corresponding face + * was previously inserted (recall that the faces should be inserted in order of filtration). + * @param dimension Dimension of the inserted face. + * @param filtrationValue Filtration value associated to the face. + * Assumed to be larger or equal to previously used filtration values. + */ + template > + void insert_face(face_key faceID, + const BoundaryRange& boundary, + dimension_type dimension, + filtration_value filtrationValue) { + ++numArrow_; + + [[maybe_unused]] auto res = handleToKey_.try_emplace(faceID, numArrow_); + + GUDHI_CHECK(res.second, "Zigzag_persistence::insert_face - face already in the complex"); + + keyToFiltrationValue_.try_emplace(numArrow_, filtrationValue); + + // Reduce the boundary of zzsh in the basis of cycles. + // Compute the keys of the faces of the boundary of zzsh. + std::set translatedBoundary; // set maintains the natural order on indices + for (auto b : boundary) { + translatedBoundary.insert(handleToKey_.at(b)); // TODO: add possibilities of coefficients + } + + pers_.insert_face(translatedBoundary, dimension); + } + + /** + * @brief Updates the zigzag persistence diagram after the removal of the given face. + * + * @param faceID ID representing the face to remove. Should be the same than the one used to insert it. + * @param dimension Dimension of the face. + * @param filtrationValue Filtration value associated to the removal. + * Assumed to be larger or equal to previously used filtration values. + */ + void remove_face(face_key faceID, dimension_type dimension, filtration_value filtrationValue) { + ++numArrow_; + + auto it = handleToKey_.find(faceID); + GUDHI_CHECK(it != handleToKey_.end(), "Zigzag_persistence::remove_face - face not in the complex"); + + keyToFiltrationValue_.try_emplace(numArrow_, filtrationValue); + + pers_.remove_face(it->second, dimension); + handleToKey_.erase(it); + } + + /** + * @brief To use when a face is neither inserted nor removed, but the filtration moves along the identity operator + * on homology level. Useful to keep the birth/death indices aligned when insertions/removals are purposly skipped + * to avoid useless computation. + */ + void apply_identity() { + ++numArrow_; + pers_.apply_identity(); + } + + /** + * @brief Outputs through the given callback method all current infinit bars. + * + * @tparam F Type of the callback method. Takes two arguments: the dimension of the cycle and the birth value + * of the cycle. + * @param stream_infinit_interval Method processing the unpaired birth values. + */ + template + void get_current_infinit_intervals(F&& stream_infinit_interval) { + pers_.get_current_infinit_intervals( + [&](dimension_type dim, internal_key birth) { stream_infinit_interval(dim, keyToFiltrationValue_.at(birth)); }); + } + + private: + template + using dictionnary = std::unordered_map; // TODO: benchmark with other map types + + dictionnary handleToKey_; /**< Map from input keys to internal keys. */ + internal_key numArrow_; /**< Current arrow number. */ + dictionnary keyToFiltrationValue_; /**< Face Key to filtration value map. */ + std::function stream_interval_; /**< Callback method for finite bars. */ + Zigzag_persistence pers_; /**< Class computing the pairs. */ +}; // end class Filtered_zigzag_persistence + +} // namespace zigzag_persistence +} // namespace Gudhi + +#endif // FILTERED_ZIGZAG_PERSISTENCE_H_ diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h index c3b02d50d2..651f9bbf2d 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h @@ -7,6 +7,7 @@ * Modification(s): * - 2023/05 Hannah Schreiber: Rework of the interface, reorganization and debug * - 2023/05 Hannah Schreiber: Addition of infinit bars + * - 2024/06 Hannah Schreiber: Separation of the zigzag algorithm from the filtration value management * - YYYY/MM Author: Description of the modification */ @@ -20,22 +21,21 @@ #define ZIGZAG_PERSISTENCE_H_ #include -#include -#include #include #include #include #include -#include #include namespace Gudhi { namespace zigzag_persistence { /** + * @ingroup zigzag_persistence + * * @brief Options for the internal matrix of @ref Zigzag_persistence. - * + * * @tparam column_type Column type of the matrix. */ template @@ -50,109 +50,46 @@ struct Zigzag_matrix_options : Gudhi::persistence_matrix::Default_options -class Zigzag_persistence { +template +class Zigzag_persistence +{ public: - using Options = ZigzagOptions; /**< Zigzag options. */ - using Matrix_options = Zigzag_matrix_options; /**< Matrix options. */ - using internal_key = typename Options::internal_key; /**< Key and index type, has to be signed. */ - using face_key = typename Options::face_key; /**< Face ID type from external inputs. */ - using filtration_value = typename Options::filtration_value; /**< Type for filtration values. */ - using dimension_type = typename Options::dimension_type; /**< Type for dimension values. */ - - /** \brief Structure to store persistence intervals by their index values. - * - * \details By convention, interval [b;d] are - * closed for finite indices b and d, and open for left-infinite and/or - * right-infinite endpoints. - */ - template - struct Interval { - Interval() {} - Interval(int dim, value_type b, value_type d) : dim_(dim), b_(b), d_(d) {} - /** Returns the dimension of the homological feature corresponding to the interval. */ - int dim() const { return dim_; } - /** Returns the birth value of the interval.*/ - value_type birth() const { return b_; } - /** Returns the death value of the interval.*/ - value_type death() const { return d_; } - - protected: - int dim_; /**< Homological dimension. */ - value_type b_; /**< Value associated to the interval birth. */ - value_type d_; /**< Value associated to the interval death. */ - }; - using Index_interval = Interval; - - /** \brief Structure to store persistence intervals by their filtration values. - * - * \details By convention, interval \f$[b;d]\f$ are - * closed for finite indices b and d, and open for left-infinite and/or - * right-infinite endpoints. - */ - struct Filtration_value_interval : Interval { - private: - using Base = Interval; - - public: - /** - * @brief Default constructor - */ - Filtration_value_interval() : Base() {} - /** - * @brief Construct a new interval with given parameters - * - * @param dim Dimension of the interval. - * @param b Start value of the interval. - * @param d End value of the interval. - */ - Filtration_value_interval(int dim, filtration_value b, filtration_value d) : Base(dim, b, d) {} - - /** - * @brief Returns the absolute length of the interval \f$|d-b|\f$. - */ - filtration_value length() const { - if (Base::b_ == Base::d_) { - return 0; - } // otherwise inf - inf would return nan. - return Base::d_ - Base::b_; - } - /** - * @brief Returns the absolute length of the log values of birth and death, i.e. \f$|\log d - \log b|\f$. - */ - filtration_value log_length() const { - if (Base::b_ == Base::d_) { - return 0; - } // otherwise inf - inf would return nan. - return std::log2(static_cast(Base::d_)) - std::log2(static_cast(Base::b_)); - } - }; + using Options = ZigzagOptions; /**< Zigzag options. */ + using index = typename Options::internal_key; /**< Key and index type, has to be signed. */ + using dimension_type = typename Options::dimension_type; /**< Type for dimension values. */ private: + using birth_dictionnary = std::unordered_map; /**< Dictionnary type. */ + using Matrix_options = Zigzag_matrix_options; /**< Matrix options. */ + using Matrix_type = Gudhi::persistence_matrix::Matrix; /**< Matrix. */ + using matrix_index = typename Matrix_type::index; /**< Matrix indexation type. */ + /** \brief Maintains the birth ordering \f$\leq_b\f$. * * \details Contains a map of size the number of @@ -183,10 +120,10 @@ class Zigzag_persistence { * When the arrow key-1 -> key is forward, key is larger than any other index * i < key in the birth ordering b k2. * @@ -224,213 +161,130 @@ class Zigzag_persistence { * @param k2 * @return true if k1 >b k2, false otherwise. */ - bool reverse_birth_order(internal_key k1, internal_key k2) const { - return birthToPos_.at(k1) > birthToPos_.at(k2); - } + bool reverse_birth_order(index k1, index k2) const { return birthToPos_.at(k1) > birthToPos_.at(k2); } private: - std::unordered_map birthToPos_; /**< birth_to_pos_[i] < birth_to_pos_[j] iff i ; - using index = typename Matrix_type::index; - public: /** * @brief Constructor of the Zigzag_persistence class. * @details After construction of the class, the zigzag filtration should be given in a streaming like way, i.e., - * call @ref insert_face or @ref remove_face for each step of the filtration in order of the filtration. - * To retrieve the current persistence diagram at any moment of the filtration, - * use @ref get_persistence_diagram or @ref get_index_persistence_diagram. + * call @ref insert_face, @ref remove_face or @ref apply_identity for each step of the filtration in order of + * the filtration. The pairs of birth and death indices are retrieved via the given callback method every time + * a pair is closed. To retrieve the open pairs (corresponding to infinit bars), + * use @ref get_current_infinit_intervals. * - * @param minNumberOfFaces Minimum number of faces that will be inserted at some point in the filtration. - * If the total number of faces is known in advance, the memory allocation can be better optimized. + * @param stream_interval Callback method to process the birth and death index pairs. Has to take three arguments + * as input: first the dimension of the cycle, then the birth index of the cycle and third the death index of the + * cycle. An index always corresponds to the arrow number the event occured (one call to @ref insert_face, + * @ref remove_face or @ref apply_identity is equal to one arrow and increases the arrow count by one). + * @param minNumberOfFaces Minimum number of faces that will be in a complex at some point in the filtration. + * If the maximal number of faces is known in advance, the memory allocation can be better optimized. * Default value: 0. - * @param ignoreCyclesAboveDim Ignores cycles in dimension larger or equal in the final diagram. - * If -1, no cycles are ignored. Default value: -1. */ - Zigzag_persistence(unsigned int minNumberOfFaces = 0, int ignoreCyclesAboveDim = -1) - : dimMax_(ignoreCyclesAboveDim), - matrix_( + Zigzag_persistence(std::function stream_interval, + unsigned int minNumberOfFaces = 0) + : matrix_( minNumberOfFaces, - [this](index columnIndex1, index columnIndex2) -> bool { + [this](matrix_index columnIndex1, matrix_index columnIndex2) -> bool { if (matrix_.get_column(columnIndex1).is_paired()) { return matrix_.get_pivot(columnIndex1) < matrix_.get_pivot(columnIndex2); } return birthOrdering_.birth_order(births_.at(columnIndex1), births_.at(columnIndex2)); }, - [this](index columnIndex1, index columnIndex2) -> bool { return false; }), + [this](matrix_index columnIndex1, matrix_index columnIndex2) -> bool { return false; }), numArrow_(-1), - previousFiltrationValue_(std::numeric_limits::infinity()) {} - + stream_interval_(std::move(stream_interval)) {} /** * @brief Updates the zigzag persistence diagram after the insertion of the given face. - * + * * @tparam BoundaryRange Range type needing begin and end members. - * @param faceID ID representing the inserted face. - * @param boundary Boundary of the inserted face. The range should be composed of the IDs of all faces contained in - * the boundary (i.e. with non-zero coefficients), using the ID specified as `faceID` when the corresponding face - * was previously inserted (recall that the faces should be inserted in order of filtration). + * @param boundary Boundary of the inserted face. The boundary should be represented by all the faces with + * non-zero coefficients generating it. A face should be represented by the arrow number when the face appeared for + * the first time in the filtration (if a face was inserted and then removed and reinserted etc., only the last + * insertion counts). The face range should be ordered by increasing arrow numbers. * @param dimension Dimension of the inserted face. - * @param filtrationValue Filtration value associated to the face. - * Assumed to be larger or equal to previously used filtration values. */ - template > - void insert_face(face_key faceID, - const BoundaryRange& boundary, - dimension_type dimension, - filtration_value filtrationValue) { - if (dimMax_ != -1 && dimension > dimMax_) return; - + template > + void insert_face(const BoundaryRange& boundary, dimension_type dimension) { ++numArrow_; - - //TODO: to make it really stream like, we should stream out finished bars and remove unnecessary filtration values - //from memory. - if (filtrationValue != previousFiltrationValue_) // check whether the filt value has changed - { // consecutive pairs (i,f), (j,f') mean faces of index k in [i,j-1] have - previousFiltrationValue_ = filtrationValue; // filtration value f - filtrationValues_.emplace_back(numArrow_, previousFiltrationValue_); - } - - [[maybe_unused]] auto res = handleToKey_.try_emplace(faceID, numArrow_); - - GUDHI_CHECK(res.second, "Zigzag_persistence::insert_face - face already in the complex"); - - // Reduce the boundary of zzsh in the basis of cycles. - // Compute the keys of the faces of the boundary of zzsh. - std::set col_bsh; // set maintains the natural order on indices - for (auto b_sh : boundary) { - col_bsh.insert(handleToKey_.at(b_sh)); // TODO: add possibilities of coefficients - } - - _process_forward_arrow(col_bsh, dimension); + _process_forward_arrow(boundary, dimension); } /** * @brief Updates the zigzag persistence diagram after the removal of the given face. - * - * @param faceID ID representing the face to remove. Should be the same than the one used to insert it. - * @param dimension Dimension of the face. - * @param filtrationValue Filtration value associated to the removal. - * Assumed to be larger or equal to previously used filtration values. + * + * @param arrowNumber Arrow number of when the face to remove was inserted for the last time. + * @param dimension Dimension of the face to remove. */ - void remove_face(face_key faceID, dimension_type dimension, filtration_value filtrationValue) { - if (dimMax_ != -1 && dimension > dimMax_) return; - + void remove_face(index arrowNumber, dimension_type dimension) { ++numArrow_; - - auto it = handleToKey_.find(faceID); - GUDHI_CHECK(it != handleToKey_.end(), "Zigzag_persistence::remove_face - face not in the complex"); - - if (filtrationValue != previousFiltrationValue_) // check whether the filt value has changed - { // consecutive pairs (i,f), (j,f') mean faces of index k in [i,j-1] have - previousFiltrationValue_ = filtrationValue; // filtration value f - filtrationValues_.emplace_back(numArrow_, previousFiltrationValue_); - } - - _process_backward_arrow(it->second, dimension); - handleToKey_.erase(it); + _process_backward_arrow(arrowNumber, dimension); } /** - * @brief Returns the "index persistence diagram" of the current filtration, that is, the pairs of atomic arrow - * numbers corresponding to a birth-death pair. Does not contain points at infinity, only the cycle classes which - * already died are represented. - * - * @return Reference to the list of intervals. - */ - const std::vector& get_index_persistence_diagram() const { return persistenceDiagram_; } - - /** - * @brief Returns the filtration values \f$[f(b),f(d)]\f$ associated to the indices \f$[b,d]\f$ which are retrieved - * by @ref get_index_persistence_diagram. - * - * @param birthKey Birth index - * @param deathKey Death index - * @return A pair of filtration values associated to the given indices. + * @brief To use when a face is neither inserted nor removed, but the filtration moves along the identity operator + * on homology level. Useful to keep the birth/death indices aligned when insertions/removals are purposly skipped + * to avoid useless computation. Increases the arrow number by one. */ - std::pair map_index_to_filtration_value(internal_key birthKey, - internal_key deathKey) const { - // filtration_values_ must be sorted by increasing keys. - auto itBirth = // lower_bound(x) returns leftmost y s.t. x <= y - std::lower_bound( - filtrationValues_.begin(), filtrationValues_.end(), - std::pair(birthKey, std::numeric_limits::infinity()), - [](std::pair p1, std::pair p2) { - return p1.first < p2.first; - }); - if (itBirth == filtrationValues_.end() || itBirth->first > birthKey) { - --itBirth; - } - // it points to the rightmost z such that z <= x - - auto itDeath = // - std::lower_bound( - filtrationValues_.begin(), filtrationValues_.end(), - std::pair(deathKey, std::numeric_limits::infinity()), - [](std::pair p1, std::pair p2) { - return p1.first < p2.first; - }); - if (itDeath == filtrationValues_.end() || itDeath->first > deathKey) { - --itDeath; - } - - return std::make_pair(itBirth->second, itDeath->second); - } + void apply_identity() { ++numArrow_; } /** - * @brief Returns the current persistence diagram. - * - * @param shortestInterval Threshold. Every bar shorter than the given value will be ignored. Default value: 0. - * @param includeInfinitBars If set to true, infinit bars are included in the diagram. Default value: false. - * @return A vector of pairs of filtration values representing the persistence diagram. + * @brief Outputs through the given callback method all birth indices which are currently not paired with + * a death index. + * + * @tparam F Type of the callback method. Takes two arguments: the dimension of the cycle and the birth index + * of the cycle. + * @param stream_infinit_interval Method processing the unpaired birth indices. */ - std::vector get_persistence_diagram(filtration_value shortestInterval = 0., - bool includeInfinitBars = false) { - // auto comp = [](Filtration_value_interval p, Filtration_value_interval q) { - // if (p.length() != q.length()) { - // return p.length() > q.length(); - // } // longest 1st - // if (p.dim() != q.dim()) { - // return p.dim() < q.dim(); - // } // lower dimension first - // if (p.birth() != q.birth()) { - // return p.birth() < q.birth(); - // } // lex order - // return p.death() < q.death(); - // }; - - std::vector diag = _get_persistence_diagram(shortestInterval); - - if (includeInfinitBars) { - _retrieve_infinit_bars(diag); + template + void get_current_infinit_intervals(F&& stream_infinit_interval) { + for (auto& p : births_) { + if constexpr (erase_birth_history) { + auto& col = matrix_.get_column(p.first); + stream_infinit_interval(col.get_dimension(), p.second); + } else { + try { + auto& col = matrix_.get_column(p.first); + if (!col.is_paired()) { + stream_infinit_interval(col.get_dimension(), p.second); + } + } catch (const std::out_of_range& e) { + continue; + } + } } - - // std::stable_sort(diag.begin(), diag.end(), comp); - - return diag; } private: - /** * @brief Express the boundary cycle of the new face as a sum of cycles in a matrix. * If some cycles are not boundary cycles, i.e., columns with F-index * in the matrix, it applies a surjective diamond to the zigzag module. - * + * * @param boundary Boundary of the inserted face. * @param dim Dimension of the inserted face. */ - void _process_forward_arrow(const std::set& boundary, dimension_type dim) { - std::vector chainsInF = matrix_.insert_boundary(numArrow_, boundary); + template + void _process_forward_arrow(const BoundaryRange& boundary, dimension_type dim) { + std::vector chainsInF = matrix_.insert_boundary(numArrow_, boundary); if (!chainsInF.empty()) { _apply_surjective_reflection_diamond(dim, chainsInF); } else { birthOrdering_.add_birth_forward(numArrow_); - births_.emplace_hint(births_.end(), matrix_.get_column_with_pivot(numArrow_), numArrow_); + if constexpr (erase_birth_history) { + births_.emplace_hint(births_.end(), matrix_.get_column_with_pivot(numArrow_), numArrow_); + } else { + auto res = births_.try_emplace(matrix_.get_column_with_pivot(numArrow_), numArrow_); + if (!res.second) { + res.first->second = numArrow_; + } + } } } @@ -441,11 +295,11 @@ class Zigzag_persistence { * columns corresponding to the chains, due to its computation in the reduction of * the boundary in _process_forward_arrow(...). It is equivalent to decreasing death index * order w.r.t. the & chainsInF) { + void _apply_surjective_reflection_diamond(dimension_type dim, const std::vector& chainsInF) { // fp is the largest death index for <=d // Set col_fp: col_fp <- col_f1+...+col_fp (now in G); preserves lowest idx auto chainFp = chainsInF[0]; // col_fp, with largest death bool { + auto cmp_birth = [this](index k1, index k2) -> bool { return birthOrdering_.reverse_birth_order(k1, k2); }; // true iff b(k1) >b b(k2) // available_birth: for all i by >d value of the d_i, // contains at step i all b_j, j > i, and maybe b_i if not stolen - std::set availableBirth(cmp_birth); + std::set availableBirth(cmp_birth); // for f1 to f_{p} (i by <=d), insertion in available_birth_to_fidx sorts by >=b for (auto& chainF : chainsInF) { availableBirth.insert(births_.at(chainF)); @@ -475,9 +329,9 @@ class Zigzag_persistence { auto lastModifiedChainIt = chainsInF.rbegin(); // consider all death indices by increasing modifiedColumns; + std::vector modifiedColumns; const auto& row = matrix_.get_row(faceID); modifiedColumns.reserve(row.size()); std::transform(row.begin(), row.end(), std::back_inserter(modifiedColumns), [](const auto& cell) { return cell.get_column_index(); }); // Sort by left-to-right order in the matrix_ (no order maintained in rows) - std::stable_sort(modifiedColumns.begin(), modifiedColumns.end(), - [this](index i1, index i2) { return matrix_.get_pivot(i1) < matrix_.get_pivot(i2); }); + std::stable_sort(modifiedColumns.begin(), modifiedColumns.end(), [this](matrix_index i1, matrix_index i2) { + return matrix_.get_pivot(i1) < matrix_.get_pivot(i2); + }); // Modifies curr_col, not the other one. for (auto otherColIt = std::next(modifiedColumns.begin()); otherColIt != modifiedColumns.end(); ++otherColIt) { @@ -547,98 +403,37 @@ class Zigzag_persistence { // curr_col points to the column to remove by restriction of K to K-{\sigma} if (!matrix_.get_column(currCol).is_paired()) { // in F auto it = births_.find(currCol); - if (dimMax_ == -1 || (dimMax_ != -1 && dim < dimMax_)) { // don't record intervals over max dim - persistenceDiagram_.emplace_back(dim, it->second, numArrow_); + stream_interval_(dim, it->second, numArrow_); + if constexpr (erase_birth_history) { + birthOrdering_.remove_birth(it->second); + births_.erase(it); } - // Following value can be erased, but it slowes the process down a bit, so I keep it as a remainder for now: - // birthOrdering_.remove_birth(it->second); - births_.erase(it); } else { // in H -> paired with c_g, that now belongs to F now // maintain the <=b order birthOrdering_.add_birth_backward(numArrow_); - births_.try_emplace(matrix_.get_column(currCol).get_paired_chain_index(), numArrow_); - } - - // cannot be in G as the removed face is maximal - matrix_.remove_maximal_face(faceID, {}); - } - - /** - * @brief Returns the current persistence diagram ordered by length without infinit bars. - * - * @param shortestInterval Intervals shorter than the given value are ignored. - * @return Vector of intervals. - */ - std::vector _get_persistence_diagram(filtration_value shortestInterval) { - std::vector diag; - diag.reserve(persistenceDiagram_.size()); - - std::stable_sort(filtrationValues_.begin(), filtrationValues_.end(), - [](std::pair p1, std::pair p2) { - return p1.first < p2.first; - }); - - for (auto bar : persistenceDiagram_) { - filtration_value birth, death; - std::tie(birth, death) = map_index_to_filtration_value(bar.birth(), bar.death()); - if (birth > death) { - std::swap(birth, death); - } - - if (death - birth > shortestInterval) { - diag.emplace_back(bar.dim(), birth, death); + if constexpr (erase_birth_history) { + births_.try_emplace(matrix_.get_column(currCol).get_paired_chain_index(), numArrow_); + } else { + auto res = births_.try_emplace(matrix_.get_column(currCol).get_paired_chain_index(), numArrow_); + if (!res.second) { + res.first->second = numArrow_; + } } } - return diag; - } - - /** - * @brief Computes the births of the current essential cycles. - * - * @param diag Reference to vector where to store the infinit bars. - */ - void _retrieve_infinit_bars(std::vector& diag) const { - auto birth = [this](internal_key birthKey) { - auto itBirth = // lower_bound(x) returns leftmost y s.t. x <= y - std::lower_bound( - filtrationValues_.begin(), filtrationValues_.end(), - std::pair(birthKey, std::numeric_limits::infinity()), - [](std::pair p1, std::pair p2) { - return p1.first < p2.first; - }); - if (itBirth == filtrationValues_.end() || itBirth->first > birthKey) { - --itBirth; - } - return itBirth->second; - }; - - for (auto& p : births_) { - auto dim = matrix_.get_column(matrix_.get_column_with_pivot(p.first)).get_dimension(); - if (dimMax_ == -1 || (dimMax_ != -1 && dim < dimMax_)) - diag.emplace_back(dim, birth(p.second), std::numeric_limits::infinity()); - } + // cannot be in G as the removed face is maximal + matrix_.remove_maximal_face(faceID, {}); // also unpaires c_g if in H } private: - std::unordered_map handleToKey_; /**< Map from input keys to internal keys. */ - dimension_type dimMax_; /**< Maximal dimension of a bar to record. */ - Matrix_type matrix_; /**< Matrix storing a base of the current chain complex. */ - std::unordered_map births_; /**< Map face index in F to corresponding birth. */ - Birth_ordering birthOrdering_; /**< Maintains persistenceDiagram_; /**< Stores current closed persistence intervals. */ - internal_key numArrow_; /**< Current arrow number. */ - filtration_value previousFiltrationValue_; /**< Filtration value of the previous arrow. */ - /** - * @brief filtrationValues_ stores consecutive pairs (i,f) , (j,f') with f != f', - * meaning that all inserted faces with key in [i;j-1] have filtration value f, - * i is the smallest face index whose face has filtration value f. - */ - std::vector > filtrationValues_; + Matrix_type matrix_; /**< Matrix storing a base of the current chain complex. */ + birth_dictionnary births_; /**< Map face index in F to corresponding birth. */ + Birth_ordering birthOrdering_; /**< Maintains stream_interval_; /**< Callback method for closed pairs. */ }; // end class Zigzag_persistence } // namespace zigzag_persistence - } // namespace Gudhi #endif // ZIGZAG_PERSISTENCE_H_ diff --git a/src/Zigzag_persistence/test/CMakeLists.txt b/src/Zigzag_persistence/test/CMakeLists.txt index c5be1da0a9..b14024ad1d 100644 --- a/src/Zigzag_persistence/test/CMakeLists.txt +++ b/src/Zigzag_persistence/test/CMakeLists.txt @@ -2,10 +2,11 @@ project(Zigzag_persistence_tests) include(GUDHI_boost_test) -add_executable ( Zigzag_persistence_unit_test zigzag_persistence_unit_test.cpp ) -if(TARGET TBB::tbb) - target_link_libraries(Zigzag_persistence_unit_test TBB::tbb) -endif() - +add_executable_with_targets(Zigzag_persistence_unit_test zigzag_persistence_unit_test.cpp TBB::tbb) gudhi_add_boost_test(Zigzag_persistence_unit_test) +add_executable_with_targets(Filtered_zigzag_persistence_unit_test filtered_zigzag_persistence_unit_test.cpp TBB::tbb) +gudhi_add_boost_test(Filtered_zigzag_persistence_unit_test) + + + diff --git a/src/Zigzag_persistence/test/filtered_zigzag_persistence_unit_test.cpp b/src/Zigzag_persistence/test/filtered_zigzag_persistence_unit_test.cpp new file mode 100644 index 0000000000..20d28c72b8 --- /dev/null +++ b/src/Zigzag_persistence/test/filtered_zigzag_persistence_unit_test.cpp @@ -0,0 +1,477 @@ +/* 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): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#include +#include +#include + +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MODULE "zigzag_persistence" +#include + +#include + +struct cmp_intervals_by_length { + cmp_intervals_by_length() {} + template + bool operator()(const Interval_filtration& p, const Interval_filtration& q) { + if (p.length() != q.length()) { + return p.length() > q.length(); + } + if (p.dim() != q.dim()) { + return p.dim() < q.dim(); + } + if (p.birth() != q.birth()) { + return p.birth() < q.birth(); + } + return p.death() < q.death(); + } +}; + +BOOST_AUTO_TEST_CASE(constructor) { + using ZP1 = Gudhi::zigzag_persistence::Filtered_zigzag_persistence_with_storage<>; + BOOST_CHECK_NO_THROW(ZP1 zp); + BOOST_CHECK_NO_THROW(ZP1 zp(28)); + BOOST_CHECK_NO_THROW(ZP1 zp(28, 2)); + ZP1 zp1; + BOOST_CHECK(zp1.get_persistence_diagram(0).empty()); + + using ZP2 = Gudhi::zigzag_persistence::Filtered_zigzag_persistence<>; + std::vector > pairs; + auto stream = [&](int dim, double birth, double death){ pairs.emplace_back(dim, birth, death); }; + BOOST_CHECK_NO_THROW(ZP2 zp(stream)); + BOOST_CHECK_NO_THROW(ZP2 zp(stream, 28)); + + ZP2 zp2(stream); + BOOST_CHECK(pairs.empty()); +} + +template +void test_barcode(ZP& zp, std::vector& barcode) { + auto bars = zp.get_persistence_diagram(0, true); + std::stable_sort(bars.begin(), bars.end(), cmp_intervals_by_length()); + std::stable_sort(barcode.begin(), barcode.end(), cmp_intervals_by_length()); + auto it = barcode.begin(); + for (const auto& interval : bars) { + BOOST_CHECK_EQUAL(interval.dim(), it->dim()); + BOOST_CHECK_EQUAL(interval.birth(), it->birth()); + BOOST_CHECK_EQUAL(interval.death(), it->death()); + ++it; + } + BOOST_CHECK(it == barcode.end()); +} + +template +void test_indices(ZP& zp, std::vector& indices, + std::vector& indexToFil) { + auto it = indices.begin(); + for (const auto& interval : zp.get_index_persistence_diagram()) { + BOOST_CHECK_EQUAL(interval.dim(), it->dim()); + BOOST_CHECK_EQUAL(interval.birth(), it->birth()); + BOOST_CHECK_EQUAL(interval.death(), it->death()); + auto p = zp.map_index_to_filtration_value(interval.birth(), interval.death()); + BOOST_CHECK_EQUAL(p.first, indexToFil[interval.birth()]); + BOOST_CHECK_EQUAL(p.second, indexToFil[interval.death()]); + ++it; + } + BOOST_CHECK(it == indices.end()); +} + +std::vector > get_boundaries() { + return {{}, + {}, + {}, + {0, 1}, + {0, 2}, + {}, + {1, 2}, + {}, + {5, 7}, + {}, + {3, 4, 6}, + {7, 9}, + {5, 9}, + {8, 11, 12}, + {10}, // remove + {13}, // remove + {1, 7}, + {3, 4, 6}, + {2, 7}, + {8, 11, 12}, + {0, 7}, + {4, 18, 20}, + {6, 16, 18}, + {3, 16, 20}, + {19}, // remove + {8}, // remove + {12}, // remove + {17, 21, 22, 23}, + {27}}; // remove +} + +std::vector get_filtration_values() { + return {0, 0, 0, + 1, 1, 1, + 2, 2, 2, + 3, 3, 3, 3, + 4, + 5, + 6, 6, 6, + 7, 7, 7, 7, 7, 7, + 8, + 9, 9, 9, + 10}; +} + +template +void test_filtered_zigzag_with_storage() { + using face_handle = typename ZP::face_key; + using filtration_value = typename ZP::filtration_value; + using Interval_index = typename ZP::Index_interval; + using Interval_filtration = typename ZP::Filtration_value_interval; + + ZP zp(28); + std::vector realIndices; + std::vector realBarcode; + realIndices.reserve(13); + realBarcode.reserve(9); + + std::vector > simplices = get_boundaries(); + std::vector filValues = get_filtration_values(); + + for (unsigned int i = 0; i < 14; ++i) { + zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); + } + + realIndices.emplace_back(0, 1, 3); + realIndices.emplace_back(0, 2, 4); + realIndices.emplace_back(0, 7, 8); + realIndices.emplace_back(1, 6, 10); + realIndices.emplace_back(0, 9, 11); + realIndices.emplace_back(1, 12, 13); + + realBarcode.emplace_back(0, 0, 1); + realBarcode.emplace_back(0, 0, 1); + realBarcode.emplace_back(1, 2, 3); + realBarcode.emplace_back(1, 3, 4); + + for (unsigned int i = 14; i < 16; ++i) { + auto id = simplices[i][0]; + zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[i]); + } + + for (unsigned int i = 16; i < 24; ++i) { + zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); + } + + realIndices.emplace_back(0, 5, 16); + realIndices.emplace_back(1, 14, 17); + realIndices.emplace_back(1, 15, 19); + realIndices.emplace_back(1, 20, 21); + realIndices.emplace_back(1, 18, 22); + + realBarcode.emplace_back(0, 1, 6); + realBarcode.emplace_back(1, 5, 6); + realBarcode.emplace_back(1, 6, 7); + + for (unsigned int i = 24; i < 27; ++i) { + auto id = simplices[i][0]; + zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[i]); + } + + realIndices.emplace_back(1, 24, 25); + realBarcode.emplace_back(1, 8, 9); + + zp.insert_face(27, simplices[27], simplices[27].size() == 0 ? 0 : simplices[27].size() - 1, filValues[27]); + + realIndices.emplace_back(2, 23, 27); + realBarcode.emplace_back(2, 7, 9); + + auto id = simplices[28][0]; + zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[28]); + + realBarcode.emplace_back(0, 0, std::numeric_limits::infinity()); + realBarcode.emplace_back(0, 9, std::numeric_limits::infinity()); + realBarcode.emplace_back(2, 10, std::numeric_limits::infinity()); + + test_indices(zp, realIndices, filValues); + test_barcode(zp, realBarcode); +} + +template +void test_filtered_zigzag_with_storage_max1() { + using face_handle = typename ZP::face_key; + using filtration_value = typename ZP::filtration_value; + using Interval_index = typename ZP::Index_interval; + using Interval_filtration = typename ZP::Filtration_value_interval; + + ZP zp(28, 1); + std::vector realIndices; + std::vector realBarcode; + realIndices.reserve(5); + realBarcode.reserve(3); + + std::vector > simplices = get_boundaries(); + std::vector filValues = get_filtration_values(); + + for (unsigned int i = 0; i < 14; ++i) { + zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); + } + + realIndices.emplace_back(0, 1, 3); + realIndices.emplace_back(0, 2, 4); + realIndices.emplace_back(0, 7, 8); + realIndices.emplace_back(0, 9, 11); + + realBarcode.emplace_back(0, 0, 1); + realBarcode.emplace_back(0, 0, 1); + + for (unsigned int i = 14; i < 16; ++i) { + auto id = simplices[i][0]; + zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[i]); + } + + for (unsigned int i = 16; i < 24; ++i) { + zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); + } + + realIndices.emplace_back(0, 5, 16); + realBarcode.emplace_back(0, 1, 6); + + for (unsigned int i = 24; i < 27; ++i) { + auto id = simplices[i][0]; + zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[i]); + } + + zp.insert_face(27, simplices[27], simplices[27].size() == 0 ? 0 : simplices[27].size() - 1, filValues[27]); + auto id = simplices[28][0]; + zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[28]); + + realBarcode.emplace_back(0, 0, std::numeric_limits::infinity()); + realBarcode.emplace_back(0, 9, std::numeric_limits::infinity()); + + test_indices(zp, realIndices, filValues); + test_barcode(zp, realBarcode); +} + +BOOST_AUTO_TEST_CASE(filtered_zigzag_persistence_with_storage) { + test_filtered_zigzag_with_storage >(); + test_filtered_zigzag_with_storage_max1 >(); +} + +template +void test_filtered_zigzag() { + using face_handle = typename ZP::face_key; + using filtration_value = typename ZP::filtration_value; + using dimension_type = typename ZP::dimension_type; + using Interval = std::tuple; + + Interval interval; + ZP zp([&](dimension_type dim, filtration_value birth, filtration_value death){ + BOOST_CHECK_EQUAL(std::get<0>(interval), dim); + BOOST_CHECK_EQUAL(std::get<1>(interval), birth); + BOOST_CHECK_EQUAL(std::get<2>(interval), death); + },28); + + std::vector realBarcode; + realBarcode.reserve(28); + realBarcode.emplace_back(3, 0, 0); //dummy + realBarcode.emplace_back(3, 0, 1); //dummy + realBarcode.emplace_back(3, 0, 2); //dummy + realBarcode.emplace_back(0, 0, 1); //1-3 + realBarcode.emplace_back(0, 0, 1); //2-4 + realBarcode.emplace_back(3, 0, 5); //dummy + realBarcode.emplace_back(3, 0, 6); //dummy + realBarcode.emplace_back(3, 0, 7); //dummy + realBarcode.emplace_back(0, 7, 8); //dummy + realBarcode.emplace_back(3, 0, 9); //dummy + realBarcode.emplace_back(1, 2, 3); //6-10 + realBarcode.emplace_back(0, 9, 11); //dummy + realBarcode.emplace_back(3, 0, 12); //dummy + realBarcode.emplace_back(1, 3, 4); //12-13 + realBarcode.emplace_back(3, 0, 14); //dummy + realBarcode.emplace_back(3, 0, 15); //dummy + realBarcode.emplace_back(0, 1, 6); //5-16 + realBarcode.emplace_back(1, 5, 6); //14-17 + realBarcode.emplace_back(3, 0, 18); //dummy + realBarcode.emplace_back(1, 6, 7); //15-19 + realBarcode.emplace_back(3, 0, 20); //dummy + realBarcode.emplace_back(1, 20, 21); //dummy + realBarcode.emplace_back(1, 18, 22); //dummy + realBarcode.emplace_back(3, 0, 23); //dummy + realBarcode.emplace_back(3, 0, 24); //dummy + realBarcode.emplace_back(1, 8, 9); //24-25 + realBarcode.emplace_back(3, 0, 26); //dummy + realBarcode.emplace_back(2, 7, 9); //23-27 + realBarcode.emplace_back(3, 0, 28); //dummy + + std::vector > simplices = get_boundaries(); + std::vector filValues = get_filtration_values(); + + for (unsigned int i = 0; i < 14; ++i) { + interval = realBarcode[i]; + zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); + } + + for (unsigned int i = 14; i < 16; ++i) { + interval = realBarcode[i]; + auto id = simplices[i][0]; + zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[i]); + } + + for (unsigned int i = 16; i < 24; ++i) { + interval = realBarcode[i]; + zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); + } + + for (unsigned int i = 24; i < 27; ++i) { + interval = realBarcode[i]; + auto id = simplices[i][0]; + zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[i]); + } + + interval = realBarcode[27]; + zp.insert_face(27, simplices[27], simplices[27].size() == 0 ? 0 : simplices[27].size() - 1, filValues[27]); + + interval = realBarcode[28]; + auto id = simplices[28][0]; + zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[28]); + + //there is no real garantee on the order of the infinite bars + std::vector infiniteBars; + zp.get_current_infinit_intervals([&](dimension_type dim, filtration_value birth) { + infiniteBars.emplace_back(dim, birth, std::numeric_limits::infinity()); + }); + + realBarcode.clear(); + realBarcode.emplace_back(0, 0, std::numeric_limits::infinity()); + realBarcode.emplace_back(0, 9, std::numeric_limits::infinity()); + realBarcode.emplace_back(2, 10, std::numeric_limits::infinity()); + + std::sort(infiniteBars.begin(), infiniteBars.end()); + std::sort(realBarcode.begin(), realBarcode.end()); + auto it = realBarcode.begin(); + for (const auto& interval : infiniteBars) { + BOOST_CHECK_EQUAL(std::get<0>(interval), std::get<0>(*it)); + BOOST_CHECK_EQUAL(std::get<1>(interval), std::get<1>(*it)); + ++it; + } + BOOST_CHECK(it == realBarcode.end()); +} + +template +void test_filtered_zigzag_max1() { + using face_handle = typename ZP::face_key; + using filtration_value = typename ZP::filtration_value; + using dimension_type = typename ZP::dimension_type; + using Interval = std::tuple; + + Interval interval; + ZP zp([&](dimension_type dim, filtration_value birth, filtration_value death){ + if (dim < 1){ + BOOST_CHECK_EQUAL(std::get<0>(interval), dim); + BOOST_CHECK_EQUAL(std::get<1>(interval), birth); + BOOST_CHECK_EQUAL(std::get<2>(interval), death); + } else { + BOOST_CHECK_NE(std::get<0>(interval), 0); + } + },28); + + std::vector realBarcode; + realBarcode.reserve(28); + realBarcode.emplace_back(1, 0, 0); //dummy + realBarcode.emplace_back(1, 0, 1); //dummy + realBarcode.emplace_back(1, 0, 2); //dummy + realBarcode.emplace_back(0, 0, 1); //1-3 + realBarcode.emplace_back(0, 0, 1); //2-4 + realBarcode.emplace_back(1, 0, 5); //dummy + realBarcode.emplace_back(1, 0, 6); //dummy + realBarcode.emplace_back(1, 0, 7); //dummy + realBarcode.emplace_back(1, 7, 8); //dummy + realBarcode.emplace_back(1, 0, 9); //dummy + realBarcode.emplace_back(1, 2, 3); //6-10 + realBarcode.emplace_back(1, 9, 11); //dummy + realBarcode.emplace_back(1, 0, 12); //dummy + realBarcode.emplace_back(1, 3, 4); //12-13 + realBarcode.emplace_back(1, 0, 14); //dummy + realBarcode.emplace_back(1, 0, 15); //dummy + realBarcode.emplace_back(0, 1, 6); //5-16 + realBarcode.emplace_back(1, 5, 6); //14-17 + realBarcode.emplace_back(1, 0, 18); //dummy + realBarcode.emplace_back(1, 6, 7); //15-19 + realBarcode.emplace_back(1, 0, 20); //dummy + realBarcode.emplace_back(1, 20, 21); //dummy + realBarcode.emplace_back(1, 18, 22); //dummy + realBarcode.emplace_back(1, 0, 23); //dummy + realBarcode.emplace_back(1, 0, 24); //dummy + realBarcode.emplace_back(1, 8, 9); //24-25 + realBarcode.emplace_back(1, 0, 26); //dummy + realBarcode.emplace_back(2, 7, 9); //23-27 + realBarcode.emplace_back(1, 0, 28); //dummy + + std::vector > simplices = get_boundaries(); + std::vector filValues = get_filtration_values(); + + for (unsigned int i = 0; i < 14; ++i) { + interval = realBarcode[i]; + zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); + } + + for (unsigned int i = 14; i < 16; ++i) { + interval = realBarcode[i]; + auto id = simplices[i][0]; + zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[i]); + } + + for (unsigned int i = 16; i < 24; ++i) { + interval = realBarcode[i]; + zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); + } + + for (unsigned int i = 24; i < 27; ++i) { + interval = realBarcode[i]; + auto id = simplices[i][0]; + zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[i]); + } + + interval = realBarcode[27]; + zp.insert_face(27, simplices[27], simplices[27].size() == 0 ? 0 : simplices[27].size() - 1, filValues[27]); + + interval = realBarcode[28]; + auto id = simplices[28][0]; + zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[28]); + + //there is no real garantee on the order of the infinite bars + std::vector infiniteBars; + zp.get_current_infinit_intervals([&](dimension_type dim, filtration_value birth) { + if (dim < 1){ + infiniteBars.emplace_back(dim, birth, std::numeric_limits::infinity()); + } + }); + + realBarcode.clear(); + realBarcode.emplace_back(0, 0, std::numeric_limits::infinity()); + realBarcode.emplace_back(0, 9, std::numeric_limits::infinity()); + + std::sort(infiniteBars.begin(), infiniteBars.end()); + std::sort(realBarcode.begin(), realBarcode.end()); + auto it = realBarcode.begin(); + for (const auto& interval : infiniteBars) { + BOOST_CHECK_EQUAL(std::get<0>(interval), std::get<0>(*it)); + BOOST_CHECK_EQUAL(std::get<1>(interval), std::get<1>(*it)); + ++it; + } + BOOST_CHECK(it == realBarcode.end()); +} + +BOOST_AUTO_TEST_CASE(filtered_zigzag_persistence) { + test_filtered_zigzag >(); + test_filtered_zigzag_max1 >(); +} diff --git a/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp b/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp index f1596e714d..e255179150 100644 --- a/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp +++ b/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp @@ -9,7 +9,6 @@ */ #include -#include #define BOOST_TEST_DYN_LINK #define BOOST_TEST_MODULE "zigzag_persistence" @@ -18,96 +17,44 @@ #include using ZP = Gudhi::zigzag_persistence::Zigzag_persistence<>; -using face_handle = ZP::face_key; -using filtration_value = ZP::filtration_value; -using Interval_index = ZP::Index_interval; -using Interval_filtration = ZP::Filtration_value_interval; - -struct cmp_intervals_by_length { - cmp_intervals_by_length() {} - bool operator()(Interval_filtration p, Interval_filtration q) { - if (p.length() != q.length()) { - return p.length() > q.length(); - } - if (p.dim() != q.dim()) { - return p.dim() < q.dim(); - } - if (p.birth() != q.birth()) { - return p.birth() < q.birth(); - } - return p.death() < q.death(); - } +// using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; + +struct Interval { + Interval() {} + Interval(int dim, ZP::index b, ZP::index d) : dim_(dim), b_(b), d_(d) {} + + int dim() const { return dim_; } + int birth() const { return b_; } + int death() const { return d_; } + +private: + int dim_; + ZP::index b_; + ZP::index d_; }; BOOST_AUTO_TEST_CASE(constructor) { - BOOST_CHECK_NO_THROW(ZP zp); - BOOST_CHECK_NO_THROW(ZP zp(28)); - BOOST_CHECK_NO_THROW(ZP zp(28, 2)); - ZP zp; - BOOST_CHECK(zp.get_persistence_diagram(0).empty()); -} + std::vector pairs; + auto stream = [&](int dim, ZP::index birth, ZP::index death){ pairs.emplace_back(dim, birth, death); }; + BOOST_CHECK_NO_THROW(ZP zp(stream)); + BOOST_CHECK_NO_THROW(ZP zp(stream, 28)); -void test_barcode(ZP& zp, std::vector& barcode) { - auto bars = zp.get_persistence_diagram(0, true); - std::stable_sort(bars.begin(), bars.end(), cmp_intervals_by_length()); - std::stable_sort(barcode.begin(), barcode.end(), cmp_intervals_by_length()); - auto it = barcode.begin(); - for (const auto& interval : bars) { - BOOST_CHECK_EQUAL(interval.dim(), it->dim()); - BOOST_CHECK_EQUAL(interval.birth(), it->birth()); - BOOST_CHECK_EQUAL(interval.death(), it->death()); - ++it; - } - BOOST_CHECK(it == barcode.end()); + ZP zp(stream); + BOOST_CHECK(pairs.empty()); } -void test_indices(ZP& zp, std::vector& indices, std::vector& indexToFil) { - auto it = indices.begin(); - for (const auto& interval : zp.get_index_persistence_diagram()) { +void test_indices(std::vector& zp_indices, std::vector& witness_indices) { + auto it = witness_indices.begin(); + for (const Interval& interval : zp_indices) { BOOST_CHECK_EQUAL(interval.dim(), it->dim()); BOOST_CHECK_EQUAL(interval.birth(), it->birth()); BOOST_CHECK_EQUAL(interval.death(), it->death()); - auto p = zp.map_index_to_filtration_value(interval.birth(), interval.death()); - BOOST_CHECK_EQUAL(p.first, indexToFil[interval.birth()]); - BOOST_CHECK_EQUAL(p.second, indexToFil[interval.death()]); ++it; } - BOOST_CHECK(it == indices.end()); -} - -std::vector > get_simplices() { - return {{0}, - {1}, - {2}, - {0, 1}, - {0, 2}, - {3}, - {1, 2}, - {4}, - {3, 4}, - {5}, - {0, 1, 2}, - {4, 5}, - {3, 5}, - {3, 4, 5}, - {0, 1, 2}, // remove - {3, 4, 5}, // remove - {1, 4}, - {0, 1, 2}, - {2, 4}, - {3, 4, 5}, - {0, 4}, - {0, 2, 4}, - {1, 2, 4}, - {0, 1, 4}, - {3, 4, 5}, // remove - {3, 4}, // remove - {3, 5}, // remove - {0, 1, 2, 4}, - {0, 1, 2, 4}}; // remove + BOOST_CHECK(it == witness_indices.end()); } -std::vector > get_boundaries() { +std::vector > get_boundaries() { return {{}, {}, {}, @@ -139,32 +86,18 @@ std::vector > get_boundaries() { {27}}; // remove } -std::vector get_filtration_values() { - return {0, 0, 0, - 1, 1, 1, - 2, 2, 2, - 3, 3, 3, 3, - 4, - 5, - 6, 6, 6, - 7, 7, 7, 7, 7, 7, - 8, - 9, 9, 9, - 10}; -} - BOOST_AUTO_TEST_CASE(zigzag_persistence_single) { - ZP zp(28); - std::vector realIndices; - std::vector realBarcode; + std::vector pairs; + auto stream = [&](int dim, ZP::index birth, ZP::index death) { pairs.emplace_back(dim, birth, death); }; + auto stream_inf = [&](int dim, ZP::index birth) { pairs.emplace_back(dim, birth, -1); }; + ZP zp(stream, 28); + std::vector realIndices; realIndices.reserve(13); - realBarcode.reserve(9); - std::vector > simplices = get_boundaries(); - std::vector filValues = get_filtration_values(); + std::vector > simplices = get_boundaries(); for (unsigned int i = 0; i < 14; ++i) { - zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); + zp.insert_face(simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1); } realIndices.emplace_back(0, 1, 3); @@ -174,18 +107,13 @@ BOOST_AUTO_TEST_CASE(zigzag_persistence_single) { realIndices.emplace_back(0, 9, 11); realIndices.emplace_back(1, 12, 13); - realBarcode.emplace_back(0, 0, 1); - realBarcode.emplace_back(0, 0, 1); - realBarcode.emplace_back(1, 2, 3); - realBarcode.emplace_back(1, 3, 4); - for (unsigned int i = 14; i < 16; ++i) { auto id = simplices[i][0]; - zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[i]); + zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1); } for (unsigned int i = 16; i < 24; ++i) { - zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); + zp.insert_face(simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1); } realIndices.emplace_back(0, 5, 16); @@ -194,94 +122,86 @@ BOOST_AUTO_TEST_CASE(zigzag_persistence_single) { realIndices.emplace_back(1, 20, 21); realIndices.emplace_back(1, 18, 22); - realBarcode.emplace_back(0, 1, 6); - realBarcode.emplace_back(1, 5, 6); - realBarcode.emplace_back(1, 6, 7); - for (unsigned int i = 24; i < 27; ++i) { auto id = simplices[i][0]; - zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[i]); + zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1); } realIndices.emplace_back(1, 24, 25); - realBarcode.emplace_back(1, 8, 9); - zp.insert_face(27, simplices[27], simplices[27].size() == 0 ? 0 : simplices[27].size() - 1, filValues[27]); + zp.insert_face(simplices[27], simplices[27].size() == 0 ? 0 : simplices[27].size() - 1); realIndices.emplace_back(2, 23, 27); - realBarcode.emplace_back(2, 7, 9); auto id = simplices[28][0]; - zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[28]); + zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1); + + realIndices.emplace_back(0, 0, -1); + realIndices.emplace_back(0, 26, -1); + realIndices.emplace_back(2, 28, -1); - realBarcode.emplace_back(0, 0, std::numeric_limits::infinity()); - realBarcode.emplace_back(0, 9, std::numeric_limits::infinity()); - realBarcode.emplace_back(2, 10, std::numeric_limits::infinity()); + auto start = pairs.size(); + zp.get_current_infinit_intervals(stream_inf); + std::sort(pairs.begin() + start, pairs.end(), [](const Interval& i1, const Interval& i2){ + if (i1.dim() != i2.dim()) return i1.dim() < i2.dim(); + return i1.birth() < i2.birth(); + }); - test_indices(zp, realIndices, filValues); - test_barcode(zp, realBarcode); + test_indices(pairs, realIndices); } BOOST_AUTO_TEST_CASE(zigzag_persistence_single_max1) { - ZP zp(28, 1); - std::vector realIndices; - std::vector indexToFil(28); - std::vector realBarcode; + std::vector pairs; + auto stream = [&](int dim, ZP::index birth, ZP::index death) { + if (dim < 1) pairs.emplace_back(dim, birth, death); + }; + auto stream_inf = [&](int dim, ZP::index birth) { + if (dim < 1) pairs.emplace_back(dim, birth, -1); + }; + ZP zp(stream, 28); + std::vector realIndices; realIndices.reserve(5); - realBarcode.reserve(3); - std::vector > simplices = get_boundaries(); - std::vector filValues = get_filtration_values(); - unsigned int currIndex = 0; + std::vector > simplices = get_boundaries(); for (unsigned int i = 0; i < 14; ++i) { - zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); - if (simplices[i].size() < 3) { - indexToFil[currIndex++] = filValues[i]; - } + zp.insert_face(simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1); } realIndices.emplace_back(0, 1, 3); realIndices.emplace_back(0, 2, 4); realIndices.emplace_back(0, 7, 8); - realIndices.emplace_back(0, 9, 10); - - realBarcode.emplace_back(0, 0, 1); - realBarcode.emplace_back(0, 0, 1); + realIndices.emplace_back(0, 9, 11); for (unsigned int i = 14; i < 16; ++i) { auto id = simplices[i][0]; - zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[i]); - if (simplices[id].size() < 3) { - indexToFil[currIndex++] = filValues[i]; - } + zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1); } for (unsigned int i = 16; i < 24; ++i) { - zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); - if (simplices[i].size() < 3) { - indexToFil[currIndex++] = filValues[i]; - } + zp.insert_face(simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1); } - realIndices.emplace_back(0, 5, 12); - realBarcode.emplace_back(0, 1, 6); + realIndices.emplace_back(0, 5, 16); for (unsigned int i = 24; i < 27; ++i) { auto id = simplices[i][0]; - zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[i]); - if (simplices[id].size() < 3) { - indexToFil[currIndex++] = filValues[i]; - } + zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1); } - zp.insert_face(27, simplices[27], simplices[27].size() == 0 ? 0 : simplices[27].size() - 1, filValues[27]); + zp.insert_face(simplices[27], simplices[27].size() == 0 ? 0 : simplices[27].size() - 1); auto id = simplices[28][0]; - zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[28]); + zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1); + + realIndices.emplace_back(0, 0, -1); + realIndices.emplace_back(0, 26, -1); - realBarcode.emplace_back(0, 0, std::numeric_limits::infinity()); - realBarcode.emplace_back(0, 9, std::numeric_limits::infinity()); + auto start = pairs.size(); + zp.get_current_infinit_intervals(stream_inf); + std::sort(pairs.begin() + start, pairs.end(), [](const Interval& i1, const Interval& i2){ + if (i1.dim() != i2.dim()) return i1.dim() < i2.dim(); + return i1.birth() < i2.birth(); + }); - test_indices(zp, realIndices, indexToFil); - test_barcode(zp, realBarcode); + test_indices(pairs, realIndices); } From dfdaaacf98486bca6a11ab8dccbcf70db716977c Mon Sep 17 00:00:00 2001 From: hschreiber Date: Fri, 21 Jun 2024 15:59:57 +0200 Subject: [PATCH 27/51] change of default values --- .../include/gudhi/Filtered_zigzag_persistence.h | 2 +- .../include/gudhi/Zigzag_persistence.h | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Zigzag_persistence/include/gudhi/Filtered_zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/Filtered_zigzag_persistence.h index e7735ac1d0..c3159d7264 100644 --- a/src/Zigzag_persistence/include/gudhi/Filtered_zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Filtered_zigzag_persistence.h @@ -95,7 +95,7 @@ struct Default_filtered_zigzag_options { * @brief Column type use by the internal matrix. */ static const Gudhi::persistence_matrix::Column_types column_type = - Gudhi::persistence_matrix::Column_types::INTRUSIVE_LIST; + Gudhi::persistence_matrix::Column_types::NAIVE_VECTOR; }; /** diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h index 651f9bbf2d..a306ff58e9 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h @@ -22,10 +22,13 @@ #include #include -#include +// #include #include #include +#include +// #include + #include namespace Gudhi { @@ -61,7 +64,7 @@ struct Default_zigzag_options { * @brief Column type use by the internal matrix. */ static const Gudhi::persistence_matrix::Column_types column_type = - Gudhi::persistence_matrix::Column_types::INTRUSIVE_LIST; // TODO: benchmark different column types + Gudhi::persistence_matrix::Column_types::NAIVE_VECTOR; //TODO: redo benchmark with oscillating rips }; // TODO: add the possibility of something else than Z2. Which means that the possibility of vineyards without Z2 @@ -85,7 +88,9 @@ class Zigzag_persistence using dimension_type = typename Options::dimension_type; /**< Type for dimension values. */ private: - using birth_dictionnary = std::unordered_map; /**< Dictionnary type. */ + // using birth_dictionnary = std::unordered_map; /**< Dictionnary type. */ + using birth_dictionnary = boost::unordered_flat_map; /**< Dictionnary type. */ + // using birth_dictionnary = boost::unordered_map; /**< Dictionnary type. */ using Matrix_options = Zigzag_matrix_options; /**< Matrix options. */ using Matrix_type = Gudhi::persistence_matrix::Matrix; /**< Matrix. */ using matrix_index = typename Matrix_type::index; /**< Matrix indexation type. */ From e51a1f3f82c3a8d8c16a24c804f97c02f50280d7 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Fri, 28 Jun 2024 12:32:14 +0200 Subject: [PATCH 28/51] change of barcode + minor corrections --- .../doc/Intro_zigzag_persistence.h | 23 ++- .../example_simple_zigzag_filtration.cpp | 36 ++-- .../example_zzfiltration_from_file.cpp | 19 +- .../gudhi/Filtered_zigzag_persistence.h | 163 +++++++----------- .../include/gudhi/Zigzag_persistence.h | 4 +- .../filtered_zigzag_persistence_unit_test.cpp | 53 +++--- .../test/zigzag_persistence_unit_test.cpp | 4 +- 7 files changed, 149 insertions(+), 153 deletions(-) diff --git a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h index afa843fcbd..bf7f52fe01 100644 --- a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h +++ b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h @@ -25,6 +25,23 @@ namespace zigzag_persistence { * complex by adding simplices, zigzag persistence also allows removals. Hence the name "zigzag", as the module * diagram will have arrows alterning between forward and backward. * + * The module is partitioned in two types of classes: filtered and non-filtered. + * - There is one non-filtered class: + * @ref Zigzag_persistence. It computes the persistence by considering only the atomic operations in the filtration. + * If the order in which the operations are made still matters, the filtration values associated to an operation + * is not token into account. For example, if a cycle is born at operation number 6 and dies at operation number 7, it + * will output a bar starting at 6 and ending at 7, even if both operations have the same filtration value and therefore + * the "real" bar has length 0. + * - There are two filtered classes: @ref Filtered_zigzag_persistence and @ref Filtered_zigzag_persistence_with_storage. + * They are both based on @ref Zigzag_persistence and manage additionnaly the filtration values which are ignored by + * @ref Zigzag_persistence. They automatically translate the operation numbers into their corresponding filtration + * values and remove bars below a given length threshold. They also have more flexible inputs (the boundaries do not + * have to be ordered, nor identified continously from 0). The two classes diverge on the way they manage the memory: + * @ref Filtered_zigzag_persistence removes systematically all unnecessary information and outputs a pair as soon + * it is closed, while @ref Filtered_zigzag_persistence_with_storage will store all informations about filtration values + * and bars until the end and output the pairs only when asked. Depending on the use and the length of the filtration, + * one will be more efficiant than the other and vice versa. + * * The implementation is based on the algorithm introduced in \cite zigzag. * * \subsection zigzaginterface Stream-like interface @@ -33,16 +50,16 @@ namespace zigzag_persistence { * filtration anymore. This makes it possible to build very long fine tuned filtrations with relatively small complexes * which can be processed without overreaching memory space. For this purpose, it is possible to feed the module with * information about the filtration "on the fly" to avoid loading the whole filtration at once. Information about the - * current barcode can be retrieved between any steps. + * current barcode can be retrieved between any steps via callback methods. * * \subsection zigzagexamples Examples * * Here is a list of zigzag persistence examples : * \li \gudhi_example_link{Zigzag_persistence,example_simple_zigzag_filtration.cpp} - A simple example to showcase how - * to use the \ref Zigzag_persistence class. + * to use the @ref Filtered_zigzag_persistence_with_storage class. * * \li \gudhi_example_link{Zigzag_persistence,example_zzfiltration_from_file.cpp} - An example of a "stream-like" usage - * by reading of the filtration from a file. + * with @ref Filtered_zigzag_persistence by reading off the filtration from a file. * * @} */ diff --git a/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp b/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp index f99fd9bc59..6a35fed140 100644 --- a/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp +++ b/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp @@ -19,24 +19,34 @@ using filtration_value = ZP::filtration_value; using Interval_filtration = ZP::Filtration_value_interval; void print_barcode(ZP& zp) { - std::clog << std::endl << "Current barcode:" << std::endl; - for (auto& bar : zp.get_persistence_diagram(0, true)) { - std::clog << std::floor(bar.birth()) << " - "; - if (bar.death() == std::numeric_limits::infinity()) { - std::clog << "inf"; - } else { - std::clog << std::floor(bar.death()); - } - std::clog << " (" << bar.dim() << ")" << std::endl; + std::cout << std::endl << "Current barcode:" << std::endl; + for (Interval_filtration& bar : zp.get_persistence_diagram(0, true)) { + //stream out content of bar + std::cout << bar << std::endl; + //to access the content of the bar, it can either be used as a struct: + // bar.birth + // bar.death + // bar.dim + //or as a tuple + // std::get<0>(bar) <- birth + // std::get<1>(bar) <- death + // std::get<2>(bar) <- dim } } void print_indices(ZP& zp) { - std::clog << std::endl << "Current pairs:" << std::endl; + std::cout << std::endl << "Current pairs:" << std::endl; for (auto& bar : zp.get_index_persistence_diagram()) { - std::clog << bar.birth() << " - "; - std::clog << bar.death(); - std::clog << " (" << bar.dim() << ")" << std::endl; + //stream out content of bar + std::cout << bar << std::endl; + //to access the content of the bar, it can either be used as a struct: + // bar.birth + // bar.death + // bar.dim + //or as a tuple: + // std::get<0>(bar) <- birth + // std::get<1>(bar) <- death + // std::get<2>(bar) <- dim } } diff --git a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp index bd2cb61bdf..d0d41171b6 100644 --- a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp +++ b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp @@ -60,6 +60,7 @@ lineType read_operation(std::string& line, std::vector& faces, double return type; } +//example of input file: example/zigzag_filtration_example.txt int main(int argc, char* const argv[]) { if (argc != 2) { if (argc < 2) @@ -72,11 +73,11 @@ int main(int argc, char* const argv[]) { std::string line; std::ifstream file(argv[1]); - //std::clog could be replaced by any other output stream + //std::cout could be replaced by any other output stream ZP zp([](dimension_type dim, filtration_value birth, filtration_value death) { - std::clog << "[" << dim << "] "; - std::clog << birth << " - " << death; - std::clog << std::endl; + std::cout << "[" << dim << "] "; + std::cout << birth << " - " << death; + std::cout << std::endl; }); if (file.is_open()) { @@ -113,11 +114,11 @@ int main(int argc, char* const argv[]) { } //retrieve infinit bars remaining at the end - //again std::clog could be replaced by any other output stream - zp.get_current_infinit_intervals([](dimension_type dim, filtration_value birth) { - std::clog << "[" << dim << "] "; - std::clog << birth << " - inf"; - std::clog << std::endl; + //again std::cout could be replaced by any other output stream + zp.get_current_infinite_intervals([](dimension_type dim, filtration_value birth) { + std::cout << "[" << dim << "] "; + std::cout << birth << " - inf"; + std::cout << std::endl; }); return 0; diff --git a/src/Zigzag_persistence/include/gudhi/Filtered_zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/Filtered_zigzag_persistence.h index c3159d7264..93e9251ea8 100644 --- a/src/Zigzag_persistence/include/gudhi/Filtered_zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Filtered_zigzag_persistence.h @@ -31,56 +31,11 @@ #include #include +#include namespace Gudhi { namespace zigzag_persistence { -/** - * @ingroup zigzag_persistence - * - * @brief Structure to store persistence intervals by their birth and death values. - * - * @tparam value_type Type for the birth and death indices. - */ -template -struct Interval { - /** - * @brief Default constructor. All values are initialized with default values. - */ - Interval() {} - /** - * @brief Constructor. - * - * @param dim Dimension of the cycle. - * @param b Birth index or value of the cycle. - * @param d Death index or value of the cycle. - */ - Interval(int dim, value_type b, value_type d) : dim_(dim), b_(b), d_(d) {} - /** - * @brief Returns the dimension of the homological feature corresponding to the interval. - * - * @return Stored dimension. - */ - int dim() const { return dim_; } - /** - * @brief Returns the birth value of the interval. - * - * @return The stored birth. - */ - value_type birth() const { return b_; } - /** - * @brief Returns the death value of the interval. - * - * @return The stored death. - */ - value_type death() const { return d_; } - - protected: - int dim_; /**< Homological dimension. */ - value_type b_; /**< Value associated to the interval birth. */ - value_type d_; /**< Value associated to the interval death. */ -}; - /** * @ingroup zigzag_persistence * @@ -118,51 +73,67 @@ class Filtered_zigzag_persistence_with_storage using face_key = typename Options::face_key; /**< Face ID type from external inputs. */ using filtration_value = typename Options::filtration_value; /**< Type for filtration values. */ using dimension_type = typename Options::dimension_type; /**< Type for dimension values. */ - using Index_interval = Interval; /**< Persistence interval type. */ - /** \brief Structure to store persistence intervals by their filtration values. - * - * \details By convention, interval \f$[b;d]\f$ are - * closed for finite indices b and d, and open for left-infinite and/or - * right-infinite endpoints. + /** + * @brief Persistence index interval type. */ - struct Filtration_value_interval : Interval { - private: - using Base = Interval; - - public: - /** - * @brief Default constructor - */ - Filtration_value_interval() : Base() {} - /** - * @brief Construct a new interval with given parameters - * - * @param dim Dimension of the interval. - * @param b Start value of the interval. - * @param d End value of the interval. - */ - Filtration_value_interval(int dim, filtration_value b, filtration_value d) : Base(dim, b, d) {} - - /** - * @brief Returns the absolute length of the interval \f$|d-b|\f$. - */ - filtration_value length() const { - if (Base::b_ == Base::d_) { - return 0; - } // otherwise inf - inf would return nan. - return Base::d_ - Base::b_; - } - /** - * @brief Returns the absolute length of the log values of birth and death, i.e. \f$|\log d - \log b|\f$. - */ - filtration_value log_length() const { - if (Base::b_ == Base::d_) { - return 0; - } // otherwise inf - inf would return nan. - return std::log2(static_cast(Base::d_)) - std::log2(static_cast(Base::b_)); - } - }; + using Index_interval = Gudhi::persistence_matrix::Persistence_interval; + /** + * @brief Persistence filtration interval type. + */ + using Filtration_value_interval = Gudhi::persistence_matrix::Persistence_interval; + + // /** \brief Structure to store persistence intervals by their filtration values. + // * + // * \details By convention, interval \f$[b;d]\f$ are + // * closed for finite indices b and d, and open for left-infinite and/or + // * right-infinite endpoints. + // */ + // struct Filtration_value_interval + // : public Gudhi::persistence_matrix::Persistence_interval { + // private: + // using Base = Gudhi::persistence_matrix::Persistence_interval; + + // public: + // /** + // * @brief Default constructor + // */ + // Filtration_value_interval() : Base() {} + // /** + // * @brief Construct a new infinit interval with given parameters. + // * + // * @param dim Dimension of the interval. + // * @param b Start value of the interval. + // */ + // Filtration_value_interval(int dim, filtration_value b) : Base(dim, b) {} + // /** + // * @brief Construct a new interval with given parameters + // * + // * @param dim Dimension of the interval. + // * @param b Start value of the interval. + // * @param d End value of the interval. + // */ + // Filtration_value_interval(int dim, filtration_value b, filtration_value d) : Base(dim, b, d) {} + + // /** + // * @brief Returns the absolute length of the interval \f$|d-b|\f$. + // */ + // filtration_value length() const { + // if (Base::birth == Base::death) { + // return 0; + // } // otherwise inf - inf would return nan. + // return Base::death - Base::birth; + // } + // /** + // * @brief Returns the absolute length of the log values of birth and death, i.e. \f$|\log d - \log b|\f$. + // */ + // filtration_value log_length() const { + // if (Base::birth == Base::death) { + // return 0; + // } // otherwise inf - inf would return nan. + // return std::log2(static_cast(Base::death)) - std::log2(static_cast(Base::birth)); + // } + // }; /** * @brief Constructor. @@ -368,13 +339,13 @@ class Filtered_zigzag_persistence_with_storage for (auto bar : persistenceDiagram_) { filtration_value birth, death; - std::tie(birth, death) = map_index_to_filtration_value(bar.birth(), bar.death()); + std::tie(birth, death) = map_index_to_filtration_value(bar.birth, bar.death); if (birth > death) { std::swap(birth, death); } if (death - birth > shortestInterval) { - diag.emplace_back(bar.dim(), birth, death); + diag.emplace_back(bar.dim, birth, death); } } @@ -403,10 +374,10 @@ class Filtered_zigzag_persistence_with_storage auto stream_infinit_interval = [&](dimension_type dim, internal_key birthIndex) { if (dimMax_ == -1 || (dimMax_ != -1 && dim < dimMax_)) - diag.emplace_back(dim, birth(birthIndex), std::numeric_limits::infinity()); + diag.emplace_back(dim, birth(birthIndex)); }; - pers_.get_current_infinit_intervals(stream_infinit_interval); + pers_.get_current_infinite_intervals(stream_infinit_interval); } }; // end class Filtered_zigzag_persistence_with_storage @@ -432,7 +403,7 @@ class Filtered_zigzag_persistence { * @details After construction of the class, the zigzag filtration should be given in a streaming like way, i.e., * call @ref insert_face, @ref remove_face or @ref apply_identity for each step of the filtration in order of * the filtration. The bars of the diagram are retrieved via the given callback method every time - * a pair with non-zero length is closed. To retrieve the open/infinit bars, use @ref get_current_infinit_intervals. + * a pair with non-zero length is closed. To retrieve the open/infinit bars, use @ref get_current_infinite_intervals. * * @param stream_interval Callback method to process the birth and death values of a persistence bar. * Has to take three arguments as input: first the dimension of the cycle, then the birth value of the cycle @@ -531,8 +502,8 @@ class Filtered_zigzag_persistence { * @param stream_infinit_interval Method processing the unpaired birth values. */ template - void get_current_infinit_intervals(F&& stream_infinit_interval) { - pers_.get_current_infinit_intervals( + void get_current_infinite_intervals(F&& stream_infinit_interval) { + pers_.get_current_infinite_intervals( [&](dimension_type dim, internal_key birth) { stream_infinit_interval(dim, keyToFiltrationValue_.at(birth)); }); } diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h index a306ff58e9..f27d51d87c 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h @@ -181,7 +181,7 @@ class Zigzag_persistence * call @ref insert_face, @ref remove_face or @ref apply_identity for each step of the filtration in order of * the filtration. The pairs of birth and death indices are retrieved via the given callback method every time * a pair is closed. To retrieve the open pairs (corresponding to infinit bars), - * use @ref get_current_infinit_intervals. + * use @ref get_current_infinite_intervals. * * @param stream_interval Callback method to process the birth and death index pairs. Has to take three arguments * as input: first the dimension of the cycle, then the birth index of the cycle and third the death index of the @@ -247,7 +247,7 @@ class Zigzag_persistence * @param stream_infinit_interval Method processing the unpaired birth indices. */ template - void get_current_infinit_intervals(F&& stream_infinit_interval) { + void get_current_infinite_intervals(F&& stream_infinit_interval) { for (auto& p : births_) { if constexpr (erase_birth_history) { auto& col = matrix_.get_column(p.first); diff --git a/src/Zigzag_persistence/test/filtered_zigzag_persistence_unit_test.cpp b/src/Zigzag_persistence/test/filtered_zigzag_persistence_unit_test.cpp index 20d28c72b8..8cecbddf07 100644 --- a/src/Zigzag_persistence/test/filtered_zigzag_persistence_unit_test.cpp +++ b/src/Zigzag_persistence/test/filtered_zigzag_persistence_unit_test.cpp @@ -18,20 +18,17 @@ #include -struct cmp_intervals_by_length { - cmp_intervals_by_length() {} +struct Interval_comparator { + Interval_comparator() {} template bool operator()(const Interval_filtration& p, const Interval_filtration& q) { - if (p.length() != q.length()) { - return p.length() > q.length(); + if (p.dim != q.dim) { + return p.dim < q.dim; } - if (p.dim() != q.dim()) { - return p.dim() < q.dim(); + if (p.birth != q.birth) { + return p.birth < q.birth; } - if (p.birth() != q.birth()) { - return p.birth() < q.birth(); - } - return p.death() < q.death(); + return p.death < q.death; } }; @@ -56,13 +53,13 @@ BOOST_AUTO_TEST_CASE(constructor) { template void test_barcode(ZP& zp, std::vector& barcode) { auto bars = zp.get_persistence_diagram(0, true); - std::stable_sort(bars.begin(), bars.end(), cmp_intervals_by_length()); - std::stable_sort(barcode.begin(), barcode.end(), cmp_intervals_by_length()); + std::stable_sort(bars.begin(), bars.end(), Interval_comparator()); + std::stable_sort(barcode.begin(), barcode.end(), Interval_comparator()); auto it = barcode.begin(); for (const auto& interval : bars) { - BOOST_CHECK_EQUAL(interval.dim(), it->dim()); - BOOST_CHECK_EQUAL(interval.birth(), it->birth()); - BOOST_CHECK_EQUAL(interval.death(), it->death()); + BOOST_CHECK_EQUAL(interval.dim, it->dim); + BOOST_CHECK_EQUAL(interval.birth, it->birth); + BOOST_CHECK_EQUAL(interval.death, it->death); ++it; } BOOST_CHECK(it == barcode.end()); @@ -73,12 +70,12 @@ void test_indices(ZP& zp, std::vector& indices, std::vector& indexToFil) { auto it = indices.begin(); for (const auto& interval : zp.get_index_persistence_diagram()) { - BOOST_CHECK_EQUAL(interval.dim(), it->dim()); - BOOST_CHECK_EQUAL(interval.birth(), it->birth()); - BOOST_CHECK_EQUAL(interval.death(), it->death()); - auto p = zp.map_index_to_filtration_value(interval.birth(), interval.death()); - BOOST_CHECK_EQUAL(p.first, indexToFil[interval.birth()]); - BOOST_CHECK_EQUAL(p.second, indexToFil[interval.death()]); + BOOST_CHECK_EQUAL(interval.dim, it->dim); + BOOST_CHECK_EQUAL(interval.birth, it->birth); + BOOST_CHECK_EQUAL(interval.death, it->death); + auto p = zp.map_index_to_filtration_value(interval.birth, interval.death); + BOOST_CHECK_EQUAL(p.first, indexToFil[interval.birth]); + BOOST_CHECK_EQUAL(p.second, indexToFil[interval.death]); ++it; } BOOST_CHECK(it == indices.end()); @@ -197,9 +194,9 @@ void test_filtered_zigzag_with_storage() { auto id = simplices[28][0]; zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[28]); - realBarcode.emplace_back(0, 0, std::numeric_limits::infinity()); - realBarcode.emplace_back(0, 9, std::numeric_limits::infinity()); - realBarcode.emplace_back(2, 10, std::numeric_limits::infinity()); + realBarcode.emplace_back(0, 0); + realBarcode.emplace_back(0, 9); + realBarcode.emplace_back(2, 10); test_indices(zp, realIndices, filValues); test_barcode(zp, realBarcode); @@ -254,8 +251,8 @@ void test_filtered_zigzag_with_storage_max1() { auto id = simplices[28][0]; zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[28]); - realBarcode.emplace_back(0, 0, std::numeric_limits::infinity()); - realBarcode.emplace_back(0, 9, std::numeric_limits::infinity()); + realBarcode.emplace_back(0, 0); + realBarcode.emplace_back(0, 9); test_indices(zp, realIndices, filValues); test_barcode(zp, realBarcode); @@ -346,7 +343,7 @@ void test_filtered_zigzag() { //there is no real garantee on the order of the infinite bars std::vector infiniteBars; - zp.get_current_infinit_intervals([&](dimension_type dim, filtration_value birth) { + zp.get_current_infinite_intervals([&](dimension_type dim, filtration_value birth) { infiniteBars.emplace_back(dim, birth, std::numeric_limits::infinity()); }); @@ -450,7 +447,7 @@ void test_filtered_zigzag_max1() { //there is no real garantee on the order of the infinite bars std::vector infiniteBars; - zp.get_current_infinit_intervals([&](dimension_type dim, filtration_value birth) { + zp.get_current_infinite_intervals([&](dimension_type dim, filtration_value birth) { if (dim < 1){ infiniteBars.emplace_back(dim, birth, std::numeric_limits::infinity()); } diff --git a/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp b/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp index e255179150..273f3cbbbe 100644 --- a/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp +++ b/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp @@ -141,7 +141,7 @@ BOOST_AUTO_TEST_CASE(zigzag_persistence_single) { realIndices.emplace_back(2, 28, -1); auto start = pairs.size(); - zp.get_current_infinit_intervals(stream_inf); + zp.get_current_infinite_intervals(stream_inf); std::sort(pairs.begin() + start, pairs.end(), [](const Interval& i1, const Interval& i2){ if (i1.dim() != i2.dim()) return i1.dim() < i2.dim(); return i1.birth() < i2.birth(); @@ -197,7 +197,7 @@ BOOST_AUTO_TEST_CASE(zigzag_persistence_single_max1) { realIndices.emplace_back(0, 26, -1); auto start = pairs.size(); - zp.get_current_infinit_intervals(stream_inf); + zp.get_current_infinite_intervals(stream_inf); std::sort(pairs.begin() + start, pairs.end(), [](const Interval& i1, const Interval& i2){ if (i1.dim() != i2.dim()) return i1.dim() < i2.dim(); return i1.birth() < i2.birth(); From 585432cbd234b1ccdb2b9dc4fd23f7e8164f4954 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Fri, 28 Jun 2024 13:59:30 +0200 Subject: [PATCH 29/51] update example cmake --- src/Zigzag_persistence/example/CMakeLists.txt | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/Zigzag_persistence/example/CMakeLists.txt b/src/Zigzag_persistence/example/CMakeLists.txt index dd1f88e1d2..0cb6521839 100644 --- a/src/Zigzag_persistence/example/CMakeLists.txt +++ b/src/Zigzag_persistence/example/CMakeLists.txt @@ -1,18 +1,11 @@ project(Zigzag_persistence_examples) -add_executable ( Zigzag_persistence_example_simple_zigzag_filtration example_simple_zigzag_filtration.cpp ) -if(TARGET TBB::tbb) - target_link_libraries(Zigzag_persistence_example_simple_zigzag_filtration TBB::tbb) -endif() +add_executable_with_targets(Zigzag_persistence_example_simple_zigzag_filtration example_simple_zigzag_filtration.cpp TBB::tbb) add_test(NAME Zigzag_persistence_example_simple_zigzag_filtration COMMAND $) -add_executable ( Zigzag_persistence_example_zzfiltration_from_file example_zzfiltration_from_file.cpp ) -if(TARGET TBB::tbb) - target_link_libraries(Zigzag_persistence_example_zzfiltration_from_file TBB::tbb) -endif() +add_executable_with_targets(Zigzag_persistence_example_zzfiltration_from_file example_zzfiltration_from_file.cpp TBB::tbb) file(COPY "zigzag_filtration_example.txt" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) add_test(NAME Zigzag_persistence_example_zzfiltration_from_file COMMAND $ "${CMAKE_CURRENT_BINARY_DIR}/zigzag_filtration_example.txt") - From 55d510a224187f95d1ccff78ad6f7e13288a2b2b Mon Sep 17 00:00:00 2001 From: hschreiber Date: Mon, 8 Jul 2024 17:44:19 +0200 Subject: [PATCH 30/51] rename files to gudhi convention --- .../example/example_simple_zigzag_filtration.cpp | 2 +- .../example/example_zzfiltration_from_file.cpp | 2 +- ...zigzag_persistence.h => filtered_zigzag_persistence.h} | 4 ++-- .../gudhi/{Zigzag_persistence.h => zigzag_persistence.h} | 4 ++-- src/Zigzag_persistence/test/CMakeLists.txt | 8 ++++---- ...unit_test.cpp => test_filtered_zigzag_persistence.cpp} | 2 +- ...sistence_unit_test.cpp => test_zigzag_persistence.cpp} | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) rename src/Zigzag_persistence/include/gudhi/{Filtered_zigzag_persistence.h => filtered_zigzag_persistence.h} (99%) rename src/Zigzag_persistence/include/gudhi/{Zigzag_persistence.h => zigzag_persistence.h} (99%) rename src/Zigzag_persistence/test/{filtered_zigzag_persistence_unit_test.cpp => test_filtered_zigzag_persistence.cpp} (99%) rename src/Zigzag_persistence/test/{zigzag_persistence_unit_test.cpp => test_zigzag_persistence.cpp} (99%) diff --git a/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp b/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp index 6a35fed140..012893adf5 100644 --- a/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp +++ b/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp @@ -11,7 +11,7 @@ #include #include -#include +#include using ZP = Gudhi::zigzag_persistence::Filtered_zigzag_persistence_with_storage<>; using face_handle = ZP::face_key; diff --git a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp index d0d41171b6..04b2469866 100644 --- a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp +++ b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include using ZP = Gudhi::zigzag_persistence::Filtered_zigzag_persistence<>; using id_handle = ZP::face_key; diff --git a/src/Zigzag_persistence/include/gudhi/Filtered_zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h similarity index 99% rename from src/Zigzag_persistence/include/gudhi/Filtered_zigzag_persistence.h rename to src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h index 93e9251ea8..bc9cba844f 100644 --- a/src/Zigzag_persistence/include/gudhi/Filtered_zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h @@ -12,7 +12,7 @@ */ /** - * @file Filtered_zigzag_persistence.h + * @file filtered_zigzag_persistence.h * @author Clément Maria, Hannah Schreiber * @brief Contains the implementation of the @ref Interval structure and the * @ref Gudhi::zigzag_persistence::Filtered_zigzag_persistence_with_storage and @@ -30,7 +30,7 @@ #include #include -#include +#include #include namespace Gudhi { diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h similarity index 99% rename from src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h rename to src/Zigzag_persistence/include/gudhi/zigzag_persistence.h index f27d51d87c..3cf3895211 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h @@ -12,7 +12,7 @@ */ /** - * @file Zigzag_persistence.h + * @file zigzag_persistence.h * @author Clément Maria, Hannah Schreiber * @brief Contains the implementation of the @ref Gudhi::zigzag_persistence::Zigzag_persistence class. */ @@ -72,7 +72,7 @@ struct Default_zigzag_options { // TODO: erase_birth_history will be moved to the options if it is proven to be useful. In the meantime // it stays here undocumented to ease benchmarks. /** - * @class Zigzag_persistence Zigzag_persistence.h gudhi/Zigzag_persistence.h + * @class Zigzag_persistence zigzag_persistence.h gudhi/zigzag_persistence.h * @brief Class computating the zigzag persistent homology of a zigzag sequence. Algorithm based on \cite zigzag. * * @ingroup zigzag_persistence diff --git a/src/Zigzag_persistence/test/CMakeLists.txt b/src/Zigzag_persistence/test/CMakeLists.txt index b14024ad1d..ee0fe2c4e9 100644 --- a/src/Zigzag_persistence/test/CMakeLists.txt +++ b/src/Zigzag_persistence/test/CMakeLists.txt @@ -2,11 +2,11 @@ project(Zigzag_persistence_tests) include(GUDHI_boost_test) -add_executable_with_targets(Zigzag_persistence_unit_test zigzag_persistence_unit_test.cpp TBB::tbb) -gudhi_add_boost_test(Zigzag_persistence_unit_test) +add_executable_with_targets(Zigzag_persistence_test_zigzag_persistence test_zigzag_persistence.cpp TBB::tbb) +gudhi_add_boost_test(Zigzag_persistence_test_zigzag_persistence) -add_executable_with_targets(Filtered_zigzag_persistence_unit_test filtered_zigzag_persistence_unit_test.cpp TBB::tbb) -gudhi_add_boost_test(Filtered_zigzag_persistence_unit_test) +add_executable_with_targets(Zigzag_persistence_test_filtered_zigzag_persistence test_filtered_zigzag_persistence.cpp TBB::tbb) +gudhi_add_boost_test(Zigzag_persistence_test_filtered_zigzag_persistence) diff --git a/src/Zigzag_persistence/test/filtered_zigzag_persistence_unit_test.cpp b/src/Zigzag_persistence/test/test_filtered_zigzag_persistence.cpp similarity index 99% rename from src/Zigzag_persistence/test/filtered_zigzag_persistence_unit_test.cpp rename to src/Zigzag_persistence/test/test_filtered_zigzag_persistence.cpp index 8cecbddf07..e91e386b4d 100644 --- a/src/Zigzag_persistence/test/filtered_zigzag_persistence_unit_test.cpp +++ b/src/Zigzag_persistence/test/test_filtered_zigzag_persistence.cpp @@ -16,7 +16,7 @@ #define BOOST_TEST_MODULE "zigzag_persistence" #include -#include +#include struct Interval_comparator { Interval_comparator() {} diff --git a/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp b/src/Zigzag_persistence/test/test_zigzag_persistence.cpp similarity index 99% rename from src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp rename to src/Zigzag_persistence/test/test_zigzag_persistence.cpp index 273f3cbbbe..8dd3a7b032 100644 --- a/src/Zigzag_persistence/test/zigzag_persistence_unit_test.cpp +++ b/src/Zigzag_persistence/test/test_zigzag_persistence.cpp @@ -14,7 +14,7 @@ #define BOOST_TEST_MODULE "zigzag_persistence" #include -#include +#include using ZP = Gudhi::zigzag_persistence::Zigzag_persistence<>; // using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; From bf19ed54120e110e0cea7b8806efa1893639d669 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Tue, 9 Jul 2024 16:58:12 +0200 Subject: [PATCH 31/51] fix version problem with boost::unordered_flat_map --- .../include/gudhi/zigzag_persistence.h | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h index 3cf3895211..37b41e9413 100644 --- a/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h @@ -22,12 +22,16 @@ #include #include -// #include #include #include -#include +#include +#if BOOST_VERSION >= 108100 +#include //don't exist for lower versions of boost // #include +#else +#include +#endif #include @@ -88,9 +92,12 @@ class Zigzag_persistence using dimension_type = typename Options::dimension_type; /**< Type for dimension values. */ private: - // using birth_dictionnary = std::unordered_map; /**< Dictionnary type. */ +#if BOOST_VERSION >= 108100 using birth_dictionnary = boost::unordered_flat_map; /**< Dictionnary type. */ // using birth_dictionnary = boost::unordered_map; /**< Dictionnary type. */ +#else + using birth_dictionnary = std::unordered_map; /**< Dictionnary type. */ +#endif using Matrix_options = Zigzag_matrix_options; /**< Matrix options. */ using Matrix_type = Gudhi::persistence_matrix::Matrix; /**< Matrix. */ using matrix_index = typename Matrix_type::index; /**< Matrix indexation type. */ From c42a26384b6454012642e08a35a784c87629eddb Mon Sep 17 00:00:00 2001 From: hschreiber Date: Wed, 10 Jul 2024 18:10:19 +0200 Subject: [PATCH 32/51] corrections from merge with upstream --- .../gudhi/filtered_zigzag_persistence.h | 6 +- .../test/test_filtered_zigzag_persistence.cpp | 72 +++++++++---------- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h index bc9cba844f..c5f2714c63 100644 --- a/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h @@ -156,7 +156,7 @@ class Filtered_zigzag_persistence_with_storage pers_( [&](dimension_type dim, internal_key birth, internal_key death) { if (dimMax_ == -1 || (dimMax_ != -1 && dim < dimMax_)) { // don't record intervals over max dim - persistenceDiagram_.emplace_back(dim, birth, death); + persistenceDiagram_.emplace_back(birth, death, dim); } }, minNumberOfFaces) {} @@ -345,7 +345,7 @@ class Filtered_zigzag_persistence_with_storage } if (death - birth > shortestInterval) { - diag.emplace_back(bar.dim, birth, death); + diag.emplace_back(birth, death, bar.dim); } } @@ -374,7 +374,7 @@ class Filtered_zigzag_persistence_with_storage auto stream_infinit_interval = [&](dimension_type dim, internal_key birthIndex) { if (dimMax_ == -1 || (dimMax_ != -1 && dim < dimMax_)) - diag.emplace_back(dim, birth(birthIndex)); + diag.emplace_back(birth(birthIndex), Filtration_value_interval::inf, dim); }; pers_.get_current_infinite_intervals(stream_infinit_interval); diff --git a/src/Zigzag_persistence/test/test_filtered_zigzag_persistence.cpp b/src/Zigzag_persistence/test/test_filtered_zigzag_persistence.cpp index e91e386b4d..702590aa46 100644 --- a/src/Zigzag_persistence/test/test_filtered_zigzag_persistence.cpp +++ b/src/Zigzag_persistence/test/test_filtered_zigzag_persistence.cpp @@ -147,17 +147,17 @@ void test_filtered_zigzag_with_storage() { zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); } - realIndices.emplace_back(0, 1, 3); - realIndices.emplace_back(0, 2, 4); - realIndices.emplace_back(0, 7, 8); - realIndices.emplace_back(1, 6, 10); - realIndices.emplace_back(0, 9, 11); - realIndices.emplace_back(1, 12, 13); - - realBarcode.emplace_back(0, 0, 1); - realBarcode.emplace_back(0, 0, 1); - realBarcode.emplace_back(1, 2, 3); - realBarcode.emplace_back(1, 3, 4); + realIndices.emplace_back(1, 3, 0); + realIndices.emplace_back(2, 4, 0); + realIndices.emplace_back(7, 8, 0); + realIndices.emplace_back(6, 10, 1); + realIndices.emplace_back(9, 11, 0); + realIndices.emplace_back(12, 13, 1); + + realBarcode.emplace_back(0, 1, 0); + realBarcode.emplace_back(0, 1, 0); + realBarcode.emplace_back(2, 3, 1); + realBarcode.emplace_back(3, 4, 1); for (unsigned int i = 14; i < 16; ++i) { auto id = simplices[i][0]; @@ -168,35 +168,35 @@ void test_filtered_zigzag_with_storage() { zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); } - realIndices.emplace_back(0, 5, 16); - realIndices.emplace_back(1, 14, 17); - realIndices.emplace_back(1, 15, 19); - realIndices.emplace_back(1, 20, 21); - realIndices.emplace_back(1, 18, 22); + realIndices.emplace_back(5, 16, 0); + realIndices.emplace_back(14, 17, 1); + realIndices.emplace_back(15, 19, 1); + realIndices.emplace_back(20, 21, 1); + realIndices.emplace_back(18, 22, 1); - realBarcode.emplace_back(0, 1, 6); - realBarcode.emplace_back(1, 5, 6); - realBarcode.emplace_back(1, 6, 7); + realBarcode.emplace_back(1, 6, 0); + realBarcode.emplace_back(5, 6, 1); + realBarcode.emplace_back(6, 7, 1); for (unsigned int i = 24; i < 27; ++i) { auto id = simplices[i][0]; zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[i]); } - realIndices.emplace_back(1, 24, 25); - realBarcode.emplace_back(1, 8, 9); + realIndices.emplace_back(24, 25, 1); + realBarcode.emplace_back(8, 9, 1); zp.insert_face(27, simplices[27], simplices[27].size() == 0 ? 0 : simplices[27].size() - 1, filValues[27]); - realIndices.emplace_back(2, 23, 27); - realBarcode.emplace_back(2, 7, 9); + realIndices.emplace_back(23, 27, 2); + realBarcode.emplace_back(7, 9, 2); auto id = simplices[28][0]; zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[28]); - realBarcode.emplace_back(0, 0); - realBarcode.emplace_back(0, 9); - realBarcode.emplace_back(2, 10); + realBarcode.emplace_back(0, Interval_filtration::inf, 0); + realBarcode.emplace_back(9, Interval_filtration::inf, 0); + realBarcode.emplace_back(10, Interval_filtration::inf, 2); test_indices(zp, realIndices, filValues); test_barcode(zp, realBarcode); @@ -222,13 +222,13 @@ void test_filtered_zigzag_with_storage_max1() { zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); } - realIndices.emplace_back(0, 1, 3); - realIndices.emplace_back(0, 2, 4); - realIndices.emplace_back(0, 7, 8); - realIndices.emplace_back(0, 9, 11); + realIndices.emplace_back(1, 3, 0); + realIndices.emplace_back(2, 4, 0); + realIndices.emplace_back(7, 8, 0); + realIndices.emplace_back(9, 11, 0); - realBarcode.emplace_back(0, 0, 1); - realBarcode.emplace_back(0, 0, 1); + realBarcode.emplace_back( 0, 1, 0); + realBarcode.emplace_back(0, 1, 0); for (unsigned int i = 14; i < 16; ++i) { auto id = simplices[i][0]; @@ -239,8 +239,8 @@ void test_filtered_zigzag_with_storage_max1() { zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); } - realIndices.emplace_back(0, 5, 16); - realBarcode.emplace_back(0, 1, 6); + realIndices.emplace_back(5, 16, 0); + realBarcode.emplace_back(1, 6, 0); for (unsigned int i = 24; i < 27; ++i) { auto id = simplices[i][0]; @@ -251,8 +251,8 @@ void test_filtered_zigzag_with_storage_max1() { auto id = simplices[28][0]; zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[28]); - realBarcode.emplace_back(0, 0); - realBarcode.emplace_back(0, 9); + realBarcode.emplace_back(0, Interval_filtration::inf, 0); + realBarcode.emplace_back(9, Interval_filtration::inf, 0); test_indices(zp, realIndices, filValues); test_barcode(zp, realBarcode); From efa25461a2ac8aaed066f5bd354dd6aa708d728f Mon Sep 17 00:00:00 2001 From: hschreiber Date: Thu, 11 Jul 2024 11:29:20 +0200 Subject: [PATCH 33/51] remove doxygen warnings --- .../doc/Intro_zigzag_persistence.h | 2 + .../gudhi/filtered_zigzag_persistence.h | 56 +------------------ 2 files changed, 4 insertions(+), 54 deletions(-) diff --git a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h index bf7f52fe01..dc5f77b861 100644 --- a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h +++ b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h @@ -19,6 +19,8 @@ namespace zigzag_persistence { * @{ * \author Clément Maria, Hannah Schreiber * + * \section zigzagintro Zigzag Persistence + * * We refer to the introduction page \ref persistent_cohomology for persistent (co)homology for an introduction * to the topic. * Zigzag persistence is a generalization of the latter. While standard persistence only allows to grow the filtered diff --git a/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h index c5f2714c63..d98f51c820 100644 --- a/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h @@ -14,8 +14,8 @@ /** * @file filtered_zigzag_persistence.h * @author Clément Maria, Hannah Schreiber - * @brief Contains the implementation of the @ref Interval structure and the - * @ref Gudhi::zigzag_persistence::Filtered_zigzag_persistence_with_storage and + * @brief Contains the implementation of the @ref Gudhi::zigzag_persistence::Default_filtered_zigzag_options structure + * and the @ref Gudhi::zigzag_persistence::Filtered_zigzag_persistence_with_storage and * @ref Gudhi::zigzag_persistence::Filtered_zigzag_persistence classes. */ @@ -83,58 +83,6 @@ class Filtered_zigzag_persistence_with_storage */ using Filtration_value_interval = Gudhi::persistence_matrix::Persistence_interval; - // /** \brief Structure to store persistence intervals by their filtration values. - // * - // * \details By convention, interval \f$[b;d]\f$ are - // * closed for finite indices b and d, and open for left-infinite and/or - // * right-infinite endpoints. - // */ - // struct Filtration_value_interval - // : public Gudhi::persistence_matrix::Persistence_interval { - // private: - // using Base = Gudhi::persistence_matrix::Persistence_interval; - - // public: - // /** - // * @brief Default constructor - // */ - // Filtration_value_interval() : Base() {} - // /** - // * @brief Construct a new infinit interval with given parameters. - // * - // * @param dim Dimension of the interval. - // * @param b Start value of the interval. - // */ - // Filtration_value_interval(int dim, filtration_value b) : Base(dim, b) {} - // /** - // * @brief Construct a new interval with given parameters - // * - // * @param dim Dimension of the interval. - // * @param b Start value of the interval. - // * @param d End value of the interval. - // */ - // Filtration_value_interval(int dim, filtration_value b, filtration_value d) : Base(dim, b, d) {} - - // /** - // * @brief Returns the absolute length of the interval \f$|d-b|\f$. - // */ - // filtration_value length() const { - // if (Base::birth == Base::death) { - // return 0; - // } // otherwise inf - inf would return nan. - // return Base::death - Base::birth; - // } - // /** - // * @brief Returns the absolute length of the log values of birth and death, i.e. \f$|\log d - \log b|\f$. - // */ - // filtration_value log_length() const { - // if (Base::birth == Base::death) { - // return 0; - // } // otherwise inf - inf would return nan. - // return std::log2(static_cast(Base::death)) - std::log2(static_cast(Base::birth)); - // } - // }; - /** * @brief Constructor. * @details After construction of the class, the zigzag filtration should be given in a streaming like way, i.e., From cfdc161606e36dfef9fd1ca524c31cda6be317fe Mon Sep 17 00:00:00 2001 From: hschreiber Date: Thu, 11 Jul 2024 14:08:51 +0200 Subject: [PATCH 34/51] dox fix --- .../doc/Intro_zigzag_persistence.h | 32 +++++------ .../example_zzfiltration_from_file.cpp | 2 +- .../gudhi/filtered_zigzag_persistence.h | 54 +++++++++---------- .../include/gudhi/zigzag_persistence.h | 40 +++++++------- 4 files changed, 63 insertions(+), 65 deletions(-) diff --git a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h index dc5f77b861..f39d02f8c3 100644 --- a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h +++ b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h @@ -24,25 +24,25 @@ namespace zigzag_persistence { * We refer to the introduction page \ref persistent_cohomology for persistent (co)homology for an introduction * to the topic. * Zigzag persistence is a generalization of the latter. While standard persistence only allows to grow the filtered - * complex by adding simplices, zigzag persistence also allows removals. Hence the name "zigzag", as the module - * diagram will have arrows alterning between forward and backward. + * complex by adding faces, zigzag persistence also allows removals. Hence the name "zigzag", as the module + * diagram will have arrows alternating between forward and backward. * - * The module is partitioned in two types of classes: filtered and non-filtered. - * - There is one non-filtered class: - * @ref Zigzag_persistence. It computes the persistence by considering only the atomic operations in the filtration. - * If the order in which the operations are made still matters, the filtration values associated to an operation - * is not token into account. For example, if a cycle is born at operation number 6 and dies at operation number 7, it - * will output a bar starting at 6 and ending at 7, even if both operations have the same filtration value and therefore - * the "real" bar has length 0. + * The module is partitioned in two types of classes: filtered by filtration values and filtered by the atomic + * operations. + * - There is one atomic class: + * @ref Zigzag_persistence. It computes the persistence by considering only the index of an atomic operations in the + * filtration and not its possibly associated filtration value. For example, if a cycle is born at operation number 6 + * and dies at operation number 7, it will output a bar starting at 6 and ending at 7, even if both operations have + * the same filtration value in the zigzag filtration and therefore the "real" bar has length 0. * - There are two filtered classes: @ref Filtered_zigzag_persistence and @ref Filtered_zigzag_persistence_with_storage. - * They are both based on @ref Zigzag_persistence and manage additionnaly the filtration values which are ignored by + * They are both based on @ref Zigzag_persistence and manage additionally the filtration values which are ignored by * @ref Zigzag_persistence. They automatically translate the operation numbers into their corresponding filtration - * values and remove bars below a given length threshold. They also have more flexible inputs (the boundaries do not - * have to be ordered, nor identified continously from 0). The two classes diverge on the way they manage the memory: - * @ref Filtered_zigzag_persistence removes systematically all unnecessary information and outputs a pair as soon - * it is closed, while @ref Filtered_zigzag_persistence_with_storage will store all informations about filtration values - * and bars until the end and output the pairs only when asked. Depending on the use and the length of the filtration, - * one will be more efficiant than the other and vice versa. + * values. They also have more flexible inputs (the boundaries do not have to be ordered, nor identified continuously + * from 0). The two classes diverge on the way they manage the memory: @ref Filtered_zigzag_persistence removes + * systematically all unnecessary information and outputs a pair as soon it is closed, while + * @ref Filtered_zigzag_persistence_with_storage will store all informations about filtration values and bars until the + * end and output the pairs only when asked. Depending on the use and the length of the filtration, one will be more + * efficient than the other and vice versa. * * The implementation is based on the algorithm introduced in \cite zigzag. * diff --git a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp index 04b2469866..e4da46e969 100644 --- a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp +++ b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp @@ -113,7 +113,7 @@ int main(int argc, char* const argv[]) { file.setstate(std::ios::failbit); } - //retrieve infinit bars remaining at the end + //retrieve infinite bars remaining at the end //again std::cout could be replaced by any other output stream zp.get_current_infinite_intervals([](dimension_type dim, filtration_value birth) { std::cout << "[" << dim << "] "; diff --git a/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h index d98f51c820..0235597fbb 100644 --- a/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h @@ -6,7 +6,7 @@ * * Modification(s): * - 2023/05 Hannah Schreiber: Rework of the interface, reorganization and debug - * - 2023/05 Hannah Schreiber: Addition of infinit bars + * - 2023/05 Hannah Schreiber: Addition of infinite bars * - 2024/06 Hannah Schreiber: Separation of the zigzag algorithm from the filtration value management * - YYYY/MM Author: Description of the modification */ @@ -42,7 +42,7 @@ namespace zigzag_persistence { * @brief Default options for @ref Filtered_zigzag_persistence_with_storage and @ref Filtered_zigzag_persistence. */ struct Default_filtered_zigzag_options { - using internal_key = int; /**< Face ID used internaly, must be signed. */ + using internal_key = int; /**< Face ID used internally, must be signed. */ using face_key = int; /**< Face ID used in the given boundaries. */ using filtration_value = double; /**< Filtration value type. */ using dimension_type = int; /**< Dimension value type. */ @@ -56,8 +56,8 @@ struct Default_filtered_zigzag_options { /** * @ingroup zigzag_persistence * - * @brief Class computating the zigzag persistent homology of a zigzag filtration. Algorithm based on \cite zigzag. - * Eventhough the insertions and removals are given in a "stream-like" way, the barcode and other values are + * @brief Class computing the zigzag persistent homology of a zigzag filtration. Algorithm based on \cite zigzag. + * Even though the insertions and removals are given in a "stream-like" way, the barcode and other values are * stored during the whole process and not removed. It is therefore suited for smaller filtrations where the clean * ups produce a higher overhead than the memory consumption. * @@ -143,8 +143,7 @@ class Filtered_zigzag_persistence_with_storage GUDHI_CHECK(res.second, "Zigzag_persistence::insert_face - face already in the complex"); - // Reduce the boundary of zzsh in the basis of cycles. - // Compute the keys of the faces of the boundary of zzsh. + // Compute the keys of the faces of the boundary. std::set translatedBoundary; // set maintains the natural order on indices for (auto b : boundary) { translatedBoundary.insert(handleToKey_.at(b)); // TODO: add possibilities of coefficients @@ -184,7 +183,7 @@ class Filtered_zigzag_persistence_with_storage /** * @brief To use when a face is neither inserted nor removed, but the filtration moves along the identity operator - * on homology level. Useful to keep the birth/death indices aligned when insertions/removals are purposly skipped + * on homology level. Useful to keep the birth/death indices aligned when insertions/removals are purposely skipped * to avoid useless computation. */ void apply_identity() { @@ -242,15 +241,15 @@ class Filtered_zigzag_persistence_with_storage * @brief Returns the current persistence diagram. * * @param shortestInterval Threshold. Every bar shorter than the given value will be ignored. Default value: 0. - * @param includeInfinitBars If set to true, infinit bars are included in the diagram. Default value: false. + * @param includeInfiniteBars If set to true, infinite bars are included in the diagram. Default value: false. * @return A vector of pairs of filtration values representing the persistence diagram. */ std::vector get_persistence_diagram(filtration_value shortestInterval = 0., - bool includeInfinitBars = false) { + bool includeInfiniteBars = false) { std::vector diag = _get_persistence_diagram(shortestInterval); - if (includeInfinitBars) { - _retrieve_infinit_bars(diag); + if (includeInfiniteBars) { + _retrieve_infinite_bars(diag); } return diag; @@ -271,7 +270,7 @@ class Filtered_zigzag_persistence_with_storage Zigzag_persistence pers_; /**< Class computing the pairs. */ /** - * @brief Returns the current persistence diagram without infinit bars. + * @brief Returns the current persistence diagram without infinite bars. * * @param shortestInterval Intervals shorter than the given value are ignored. * @return Vector of intervals. @@ -303,9 +302,9 @@ class Filtered_zigzag_persistence_with_storage /** * @brief Computes the births of the current essential cycles. * - * @param diag Reference to vector where to store the infinit bars. + * @param diag Reference to vector where to store the infinite bars. */ - void _retrieve_infinit_bars(std::vector& diag) { + void _retrieve_infinite_bars(std::vector& diag) { auto birth = [this](internal_key birthKey) { auto itBirth = // lower_bound(x) returns leftmost y s.t. x <= y std::lower_bound( @@ -320,19 +319,19 @@ class Filtered_zigzag_persistence_with_storage return itBirth->second; }; - auto stream_infinit_interval = [&](dimension_type dim, internal_key birthIndex) { + auto stream_infinite_interval = [&](dimension_type dim, internal_key birthIndex) { if (dimMax_ == -1 || (dimMax_ != -1 && dim < dimMax_)) diag.emplace_back(birth(birthIndex), Filtration_value_interval::inf, dim); }; - pers_.get_current_infinite_intervals(stream_infinit_interval); + pers_.get_current_infinite_intervals(stream_infinite_interval); } }; // end class Filtered_zigzag_persistence_with_storage /** * @ingroup zigzag_persistence * - * @brief Class computating the zigzag persistent homology of a zigzag filtration. Algorithm based on \cite zigzag. + * @brief Class computing the zigzag persistent homology of a zigzag filtration. Algorithm based on \cite zigzag. * * @tparam FilteredZigzagOptions Structure following the @ref FilteredZigzagOptions concept. * Default value: @ref Default_filtered_zigzag_options. @@ -351,7 +350,7 @@ class Filtered_zigzag_persistence { * @details After construction of the class, the zigzag filtration should be given in a streaming like way, i.e., * call @ref insert_face, @ref remove_face or @ref apply_identity for each step of the filtration in order of * the filtration. The bars of the diagram are retrieved via the given callback method every time - * a pair with non-zero length is closed. To retrieve the open/infinit bars, use @ref get_current_infinite_intervals. + * a pair with non-zero length is closed. To retrieve the open/infinite bars, use @ref get_current_infinite_intervals. * * @param stream_interval Callback method to process the birth and death values of a persistence bar. * Has to take three arguments as input: first the dimension of the cycle, then the birth value of the cycle @@ -402,8 +401,7 @@ class Filtered_zigzag_persistence { keyToFiltrationValue_.try_emplace(numArrow_, filtrationValue); - // Reduce the boundary of zzsh in the basis of cycles. - // Compute the keys of the faces of the boundary of zzsh. + // Compute the keys of the faces of the boundary. std::set translatedBoundary; // set maintains the natural order on indices for (auto b : boundary) { translatedBoundary.insert(handleToKey_.at(b)); // TODO: add possibilities of coefficients @@ -434,7 +432,7 @@ class Filtered_zigzag_persistence { /** * @brief To use when a face is neither inserted nor removed, but the filtration moves along the identity operator - * on homology level. Useful to keep the birth/death indices aligned when insertions/removals are purposly skipped + * on homology level. Useful to keep the birth/death indices aligned when insertions/removals are purposely skipped * to avoid useless computation. */ void apply_identity() { @@ -443,25 +441,25 @@ class Filtered_zigzag_persistence { } /** - * @brief Outputs through the given callback method all current infinit bars. + * @brief Outputs through the given callback method all current infinite bars. * * @tparam F Type of the callback method. Takes two arguments: the dimension of the cycle and the birth value * of the cycle. - * @param stream_infinit_interval Method processing the unpaired birth values. + * @param stream_infinite_interval Method processing the unpaired birth values. */ template - void get_current_infinite_intervals(F&& stream_infinit_interval) { + void get_current_infinite_intervals(F&& stream_infinite_interval) { pers_.get_current_infinite_intervals( - [&](dimension_type dim, internal_key birth) { stream_infinit_interval(dim, keyToFiltrationValue_.at(birth)); }); + [&](dimension_type dim, internal_key birth) { stream_infinite_interval(dim, keyToFiltrationValue_.at(birth)); }); } private: template - using dictionnary = std::unordered_map; // TODO: benchmark with other map types + using dictionary = std::unordered_map; // TODO: benchmark with other map types - dictionnary handleToKey_; /**< Map from input keys to internal keys. */ + dictionary handleToKey_; /**< Map from input keys to internal keys. */ internal_key numArrow_; /**< Current arrow number. */ - dictionnary keyToFiltrationValue_; /**< Face Key to filtration value map. */ + dictionary keyToFiltrationValue_; /**< Face Key to filtration value map. */ std::function stream_interval_; /**< Callback method for finite bars. */ Zigzag_persistence pers_; /**< Class computing the pairs. */ }; // end class Filtered_zigzag_persistence diff --git a/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h index 37b41e9413..850a3b4a98 100644 --- a/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h @@ -6,7 +6,7 @@ * * Modification(s): * - 2023/05 Hannah Schreiber: Rework of the interface, reorganization and debug - * - 2023/05 Hannah Schreiber: Addition of infinit bars + * - 2023/05 Hannah Schreiber: Addition of infinite bars * - 2024/06 Hannah Schreiber: Separation of the zigzag algorithm from the filtration value management * - YYYY/MM Author: Description of the modification */ @@ -62,7 +62,7 @@ struct Zigzag_matrix_options : Gudhi::persistence_matrix::Default_options= 108100 - using birth_dictionnary = boost::unordered_flat_map; /**< Dictionnary type. */ - // using birth_dictionnary = boost::unordered_map; /**< Dictionnary type. */ + using birth_dictionary = boost::unordered_flat_map; /**< Dictionary type. */ + // using birth_dictionary = boost::unordered_map; /**< Dictionary type. */ #else - using birth_dictionnary = std::unordered_map; /**< Dictionnary type. */ + using birth_dictionary = std::unordered_map; /**< Dictionary type. */ #endif using Matrix_options = Zigzag_matrix_options; /**< Matrix options. */ using Matrix_type = Gudhi::persistence_matrix::Matrix; /**< Matrix. */ @@ -176,7 +176,7 @@ class Zigzag_persistence bool reverse_birth_order(index k1, index k2) const { return birthToPos_.at(k1) > birthToPos_.at(k2); } private: - birth_dictionnary birthToPos_; /**< birth_to_pos_[i] < birth_to_pos_[j] iff i - void get_current_infinite_intervals(F&& stream_infinit_interval) { + void get_current_infinite_intervals(F&& stream_infinite_interval) { for (auto& p : births_) { if constexpr (erase_birth_history) { auto& col = matrix_.get_column(p.first); - stream_infinit_interval(col.get_dimension(), p.second); + stream_infinite_interval(col.get_dimension(), p.second); } else { try { auto& col = matrix_.get_column(p.first); if (!col.is_paired()) { - stream_infinit_interval(col.get_dimension(), p.second); + stream_infinite_interval(col.get_dimension(), p.second); } } catch (const std::out_of_range& e) { continue; @@ -326,10 +326,10 @@ class Zigzag_persistence return birthOrdering_.reverse_birth_order(k1, k2); }; // true iff b(k1) >b b(k2) - // available_birth: for all i by >d value of the d_i, + // availableBirth: for all i by >d value of the d_i, // contains at step i all b_j, j > i, and maybe b_i if not stolen std::set availableBirth(cmp_birth); - // for f1 to f_{p} (i by <=d), insertion in available_birth_to_fidx sorts by >=b + // for f1 to f_{p} (i by <=d), insertion in availableBirth sorts by >=b for (auto& chainF : chainsInF) { availableBirth.insert(births_.at(chainF)); } @@ -348,7 +348,7 @@ class Zigzag_persistence if (birthIt == availableBirth.end()) // birth is not available. *chain_f_it { // must become the sum of all chains in F with smaller death index. // this gives as birth the maximal birth of all chains with strictly larger - // death <=> the maximal availabe death. + // death <=> the maximal available death. // Let c_1 ... c_f be the chains s.t. <[c_1+...+c_f]> is the kernel and // death(c_i) >d death(c_i-1). If the birth of c_i is not available, we set // c_i <- c_i + c_i-1 + ... + c_1, which is [c_i + c_i-1 + ... + c_1] on @@ -367,7 +367,7 @@ class Zigzag_persistence } lastModifiedChainIt = chainFIt; // new cumulated c_i+...+c_1 // remove the max available death - auto maxAvailBirthIt = availableBirth.begin(); // max because order by deacr stream_interval_; /**< Callback method for closed pairs. */ From e025cecb20147568946147408f4c774f8a759053 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Thu, 11 Jul 2024 18:39:07 +0200 Subject: [PATCH 35/51] doc fix --- .../doc/Intro_zigzag_persistence.h | 7 ++++--- .../include/gudhi/filtered_zigzag_persistence.h | 4 ++-- .../include/gudhi/zigzag_persistence.h | 13 +++++++++---- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h index f39d02f8c3..26dfffe0b2 100644 --- a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h +++ b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h @@ -27,14 +27,15 @@ namespace zigzag_persistence { * complex by adding faces, zigzag persistence also allows removals. Hence the name "zigzag", as the module * diagram will have arrows alternating between forward and backward. * - * The module is partitioned in two types of classes: filtered by filtration values and filtered by the atomic + * The module is partitioned in two types of classes: filtered by filtration values and filtered by the elementary * operations. - * - There is one atomic class: + * - For the latter, there is one class: * @ref Zigzag_persistence. It computes the persistence by considering only the index of an atomic operations in the * filtration and not its possibly associated filtration value. For example, if a cycle is born at operation number 6 * and dies at operation number 7, it will output a bar starting at 6 and ending at 7, even if both operations have * the same filtration value in the zigzag filtration and therefore the "real" bar has length 0. - * - There are two filtered classes: @ref Filtered_zigzag_persistence and @ref Filtered_zigzag_persistence_with_storage. + * - For the other type, there are two classes: @ref Filtered_zigzag_persistence and + * @ref Filtered_zigzag_persistence_with_storage. * They are both based on @ref Zigzag_persistence and manage additionally the filtration values which are ignored by * @ref Zigzag_persistence. They automatically translate the operation numbers into their corresponding filtration * values. They also have more flexible inputs (the boundaries do not have to be ordered, nor identified continuously diff --git a/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h index 0235597fbb..94bd4f67f7 100644 --- a/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h @@ -112,7 +112,7 @@ class Filtered_zigzag_persistence_with_storage /** * @brief Updates the zigzag persistence diagram after the insertion of the given face. * - * @tparam BoundaryRange Range type needing begin and end members. + * @tparam BoundaryRange Range type needing size, begin and end members. * @param faceID ID representing the inserted face. * @param boundary Boundary of the inserted face. The range should be composed of the IDs of all faces contained in * the boundary (i.e. with non-zero coefficients), using the ID specified as `faceID` when the corresponding face @@ -379,7 +379,7 @@ class Filtered_zigzag_persistence { /** * @brief Updates the zigzag persistence diagram after the insertion of the given face. * - * @tparam BoundaryRange Range type needing begin and end members. + * @tparam BoundaryRange Range type needing size, begin and end members. * @param faceID ID representing the inserted face. * @param boundary Boundary of the inserted face. The range should be composed of the IDs of all faces contained in * the boundary (i.e. with non-zero coefficients), using the ID specified as `faceID` when the corresponding face diff --git a/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h index 850a3b4a98..63020d2a63 100644 --- a/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h @@ -78,6 +78,11 @@ struct Default_zigzag_options { /** * @class Zigzag_persistence zigzag_persistence.h gudhi/zigzag_persistence.h * @brief Class computing the zigzag persistent homology of a zigzag sequence. Algorithm based on \cite zigzag. + * @details After construction of the class, the zigzag filtration should be given in a streaming like way, i.e., + * call @ref insert_face, @ref remove_face or @ref apply_identity for each step of the filtration in order of + * the filtration. The pairs of birth and death indices are retrieved via the given callback method every time + * a pair is closed. To retrieve the open pairs (corresponding to infinite bars), + * use @ref get_current_infinite_intervals. * * @ingroup zigzag_persistence * @@ -194,8 +199,8 @@ class Zigzag_persistence * as input: first the dimension of the cycle, then the birth index of the cycle and third the death index of the * cycle. An index always corresponds to the arrow number the event occurred (one call to @ref insert_face, * @ref remove_face or @ref apply_identity is equal to one arrow and increases the arrow count by one). - * @param minNumberOfFaces Minimum number of faces that will be in a complex at some point in the filtration. - * If the maximal number of faces is known in advance, the memory allocation can be better optimized. + * @param minNumberOfFaces Maximal value among the minimum numbers of faces known to be at the same time in each + * complex of the filtration. It will be used to optimize the memory allocation. * Default value: 0. */ Zigzag_persistence(std::function stream_interval, @@ -214,7 +219,7 @@ class Zigzag_persistence /** * @brief Updates the zigzag persistence diagram after the insertion of the given face. * - * @tparam BoundaryRange Range type needing begin and end members. + * @tparam BoundaryRange Range type needing size, begin and end members. * @param boundary Boundary of the inserted face. The boundary should be represented by all the faces with * non-zero coefficients generating it. A face should be represented by the arrow number when the face appeared for * the first time in the filtration (if a face was inserted and then removed and reinserted etc., only the last @@ -355,7 +360,7 @@ class Zigzag_persistence // the right (of death the maximali <=> the max Date: Thu, 18 Jul 2024 15:01:29 +0200 Subject: [PATCH 36/51] small changes --- .../doc/Intro_zigzag_persistence.h | 26 ++--- .../gudhi/filtered_zigzag_persistence.h | 103 +++++++++++------- .../include/gudhi/zigzag_persistence.h | 17 ++- 3 files changed, 85 insertions(+), 61 deletions(-) diff --git a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h index 26dfffe0b2..e04112092b 100644 --- a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h +++ b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h @@ -27,20 +27,18 @@ namespace zigzag_persistence { * complex by adding faces, zigzag persistence also allows removals. Hence the name "zigzag", as the module * diagram will have arrows alternating between forward and backward. * - * The module is partitioned in two types of classes: filtered by filtration values and filtered by the elementary - * operations. - * - For the latter, there is one class: - * @ref Zigzag_persistence. It computes the persistence by considering only the index of an atomic operations in the - * filtration and not its possibly associated filtration value. For example, if a cycle is born at operation number 6 - * and dies at operation number 7, it will output a bar starting at 6 and ending at 7, even if both operations have - * the same filtration value in the zigzag filtration and therefore the "real" bar has length 0. - * - For the other type, there are two classes: @ref Filtered_zigzag_persistence and - * @ref Filtered_zigzag_persistence_with_storage. - * They are both based on @ref Zigzag_persistence and manage additionally the filtration values which are ignored by - * @ref Zigzag_persistence. They automatically translate the operation numbers into their corresponding filtration - * values. They also have more flexible inputs (the boundaries do not have to be ordered, nor identified continuously - * from 0). The two classes diverge on the way they manage the memory: @ref Filtered_zigzag_persistence removes - * systematically all unnecessary information and outputs a pair as soon it is closed, while + * The module consists of the @ref Zigzag_persistence class and two wrappers @ref Filtered_zigzag_persistence and + * @ref Filtered_zigzag_persistence_with_storage "": + * - @ref Zigzag_persistence computes the persistence of a sequence of insertions and removals. A face can be inserted + * or removed one at a time and the returned persistence pairs / bars are indexed on the operation numbers. + * For example, if a cycle is born at operation number 6 and dies at operation number 7, it will output a bar starting + * at 6 and ending at 7. + * - @ref Filtered_zigzag_persistence and @ref Filtered_zigzag_persistence_with_storage are adding the notion of + * "filtration value" to @ref Zigzag_persistence. At each call, an operation can be associated to a filtration value, + * which will be used to index the returned bars instead (bars with new length 0 are then ignored). The two classes + * also have more flexible inputs (the boundaries do not have to be ordered, nor identified continuously + * from 0). The difference between both classes is on the way they manage the memory: @ref Filtered_zigzag_persistence + * removes systematically all unnecessary information and outputs a pair as soon it is closed, while * @ref Filtered_zigzag_persistence_with_storage will store all informations about filtration values and bars until the * end and output the pairs only when asked. Depending on the use and the length of the filtration, one will be more * efficient than the other and vice versa. diff --git a/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h index 94bd4f67f7..70413c5986 100644 --- a/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h @@ -60,6 +60,10 @@ struct Default_filtered_zigzag_options { * Even though the insertions and removals are given in a "stream-like" way, the barcode and other values are * stored during the whole process and not removed. It is therefore suited for smaller filtrations where the clean * ups produce a higher overhead than the memory consumption. + * @details After construction of the class, the zigzag filtration should be given in a streaming like way, i.e., + * call @ref insert_face, @ref remove_face or @ref apply_identity for each step of the filtration in order of + * the filtration. To retrieve the current persistence diagram at any moment of the filtration, + * use @ref get_persistence_diagram or @ref get_index_persistence_diagram. * * @tparam FilteredZigzagOptions Structure following the @ref FilteredZigzagOptions concept. * Default value: @ref Default_filtered_zigzag_options. @@ -90,13 +94,14 @@ class Filtered_zigzag_persistence_with_storage * the filtration. To retrieve the current persistence diagram at any moment of the filtration, * use @ref get_persistence_diagram or @ref get_index_persistence_diagram. * - * @param minNumberOfFaces Minimum number of faces that will be in a complex at some point in the filtration. - * If the maximal number of faces is known in advance, the memory allocation can be better optimized. - * Default value: 0. + * @param preallocationSize Space for @p preallocationSize faces are reserved in the underlying structure. + * Theoretically, any values works therefore, but for better performances, it is better to be as close as possible + * to the maximal value of the number of faces stored at the same time. At a same time are stored faces which were + * inserted before that time but not removed until that time. Default value: 0. * @param ignoreCyclesAboveDim Ignores cycles in dimension larger or equal in the final diagram. * If -1, no cycles are ignored. Default value: -1. */ - Filtered_zigzag_persistence_with_storage(unsigned int minNumberOfFaces = 0, int ignoreCyclesAboveDim = -1) + Filtered_zigzag_persistence_with_storage(unsigned int preallocationSize = 0, int ignoreCyclesAboveDim = -1) : dimMax_(ignoreCyclesAboveDim), persistenceDiagram_(), numArrow_(-1), @@ -107,7 +112,7 @@ class Filtered_zigzag_persistence_with_storage persistenceDiagram_.emplace_back(birth, death, dim); } }, - minNumberOfFaces) {} + preallocationSize) {} /** * @brief Updates the zigzag persistence diagram after the insertion of the given face. @@ -119,18 +124,21 @@ class Filtered_zigzag_persistence_with_storage * was previously inserted (recall that the faces should be inserted in order of filtration). * @param dimension Dimension of the inserted face. * @param filtrationValue Filtration value associated to the face. - * Assumed to be larger or equal to previously used filtration values. + * Assumed to be always larger or equal to previously used filtration values or always smaller or equal than previous + * values, ie. the changes are monotonous. + * @return Number of the operation. */ template > - void insert_face(face_key faceID, - const BoundaryRange& boundary, - dimension_type dimension, - filtration_value filtrationValue) { + internal_key insert_face(face_key faceID, + const BoundaryRange& boundary, + dimension_type dimension, + filtration_value filtrationValue) + { ++numArrow_; if (dimMax_ != -1 && dimension > dimMax_) { pers_.apply_identity(); - return; + return numArrow_; } if (filtrationValue != previousFiltrationValue_) // check whether the filt value has changed @@ -150,6 +158,8 @@ class Filtered_zigzag_persistence_with_storage } pers_.insert_face(translatedBoundary, dimension); + + return numArrow_; } /** @@ -158,14 +168,16 @@ class Filtered_zigzag_persistence_with_storage * @param faceID ID representing the face to remove. Should be the same than the one used to insert it. * @param dimension Dimension of the face. * @param filtrationValue Filtration value associated to the removal. - * Assumed to be larger or equal to previously used filtration values. + * Assumed to be always larger or equal to previously used filtration values or always smaller or equal than previous + * values, ie. the changes are monotonous. + * @return Number of the operation. */ - void remove_face(face_key faceID, dimension_type dimension, filtration_value filtrationValue) { + internal_key remove_face(face_key faceID, dimension_type dimension, filtration_value filtrationValue) { ++numArrow_; if (dimMax_ != -1 && dimension > dimMax_) { pers_.apply_identity(); - return; + return numArrow_; } auto it = handleToKey_.find(faceID); @@ -179,16 +191,20 @@ class Filtered_zigzag_persistence_with_storage pers_.remove_face(it->second, dimension); handleToKey_.erase(it); + + return numArrow_; } /** * @brief To use when a face is neither inserted nor removed, but the filtration moves along the identity operator * on homology level. Useful to keep the birth/death indices aligned when insertions/removals are purposely skipped * to avoid useless computation. + * @return Number of the operation. */ - void apply_identity() { + internal_key apply_identity() { ++numArrow_; pers_.apply_identity(); + return numArrow_; } /** @@ -214,9 +230,9 @@ class Filtered_zigzag_persistence_with_storage auto itBirth = // lower_bound(x) returns leftmost y s.t. x <= y std::lower_bound( filtrationValues_.begin(), filtrationValues_.end(), - std::pair(birthKey, std::numeric_limits::infinity()), - [](std::pair p1, std::pair p2) { - return p1.first < p2.first; + birthKey, + [](std::pair p, internal_key k) { + return p.first < k; }); if (itBirth == filtrationValues_.end() || itBirth->first > birthKey) { --itBirth; @@ -226,9 +242,9 @@ class Filtered_zigzag_persistence_with_storage auto itDeath = // std::lower_bound( filtrationValues_.begin(), filtrationValues_.end(), - std::pair(deathKey, std::numeric_limits::infinity()), - [](std::pair p1, std::pair p2) { - return p1.first < p2.first; + deathKey, + [](std::pair p, internal_key k) { + return p.first < k; }); if (itDeath == filtrationValues_.end() || itDeath->first > deathKey) { --itDeath; @@ -279,10 +295,10 @@ class Filtered_zigzag_persistence_with_storage std::vector diag; diag.reserve(persistenceDiagram_.size()); - std::stable_sort(filtrationValues_.begin(), filtrationValues_.end(), - [](std::pair p1, std::pair p2) { - return p1.first < p2.first; - }); + // std::stable_sort(filtrationValues_.begin(), filtrationValues_.end(), + // [](std::pair p1, std::pair p2) { + // return p1.first < p2.first; + // }); for (auto bar : persistenceDiagram_) { filtration_value birth, death; @@ -309,10 +325,10 @@ class Filtered_zigzag_persistence_with_storage auto itBirth = // lower_bound(x) returns leftmost y s.t. x <= y std::lower_bound( filtrationValues_.begin(), filtrationValues_.end(), - std::pair(birthKey, std::numeric_limits::infinity()), - [](std::pair p1, std::pair p2) { - return p1.first < p2.first; - }); + birthKey, + [](std::pair p, internal_key k) { + return p.first < k; + }); if (itBirth == filtrationValues_.end() || itBirth->first > birthKey) { --itBirth; } @@ -332,6 +348,10 @@ class Filtered_zigzag_persistence_with_storage * @ingroup zigzag_persistence * * @brief Class computing the zigzag persistent homology of a zigzag filtration. Algorithm based on \cite zigzag. + * @details After construction of the class, the zigzag filtration should be given in a streaming like way, i.e., + * call @ref insert_face, @ref remove_face or @ref apply_identity for each step of the filtration in order of + * the filtration. The bars of the diagram are retrieved via the given callback method every time + * a pair with non-zero length is closed. To retrieve the open/infinite bars, use @ref get_current_infinite_intervals. * * @tparam FilteredZigzagOptions Structure following the @ref FilteredZigzagOptions concept. * Default value: @ref Default_filtered_zigzag_options. @@ -356,25 +376,25 @@ class Filtered_zigzag_persistence { * Has to take three arguments as input: first the dimension of the cycle, then the birth value of the cycle * and third the death value of the cycle. The values corresponds to the filtration values which were given at * insertions or removals. - * @param minNumberOfFaces Minimum number of faces that will be in a complex at some point in the filtration. + * @param preallocationSize Minimum number of faces that will be in a complex at some point in the filtration. * If the maximal number of faces is known in advance, the memory allocation can be better optimized. * Default value: 0. + * @tparam F Type of callback method. */ - Filtered_zigzag_persistence(std::function stream_interval, - unsigned int minNumberOfFaces = 0) - : handleToKey_(minNumberOfFaces), + template + Filtered_zigzag_persistence(F&& stream_interval, unsigned int preallocationSize = 0) + : handleToKey_(preallocationSize), numArrow_(-1), - keyToFiltrationValue_(minNumberOfFaces), - stream_interval_(std::move(stream_interval)), + keyToFiltrationValue_(preallocationSize), pers_( - [&](dimension_type dim, internal_key birth, internal_key death) { + [&,stream_interval](dimension_type dim, internal_key birth, internal_key death) { auto itB = keyToFiltrationValue_.find(birth); auto itD = keyToFiltrationValue_.find(death); - if (itB->second != itD->second) stream_interval_(dim, itB->second, itD->second); + if (itB->second != itD->second) stream_interval(dim, itB->second, itD->second); keyToFiltrationValue_.erase(itB); keyToFiltrationValue_.erase(itD); }, - minNumberOfFaces) {} + preallocationSize) {} /** * @brief Updates the zigzag persistence diagram after the insertion of the given face. @@ -386,7 +406,8 @@ class Filtered_zigzag_persistence { * was previously inserted (recall that the faces should be inserted in order of filtration). * @param dimension Dimension of the inserted face. * @param filtrationValue Filtration value associated to the face. - * Assumed to be larger or equal to previously used filtration values. + * Assumed to be always larger or equal to previously used filtration values or always smaller or equal than previous + * values, ie. the changes are monotonous. */ template > void insert_face(face_key faceID, @@ -416,7 +437,8 @@ class Filtered_zigzag_persistence { * @param faceID ID representing the face to remove. Should be the same than the one used to insert it. * @param dimension Dimension of the face. * @param filtrationValue Filtration value associated to the removal. - * Assumed to be larger or equal to previously used filtration values. + * Assumed to be always larger or equal to previously used filtration values or always smaller or equal than previous + * values, ie. the changes are monotonous. */ void remove_face(face_key faceID, dimension_type dimension, filtration_value filtrationValue) { ++numArrow_; @@ -460,7 +482,6 @@ class Filtered_zigzag_persistence { dictionary handleToKey_; /**< Map from input keys to internal keys. */ internal_key numArrow_; /**< Current arrow number. */ dictionary keyToFiltrationValue_; /**< Face Key to filtration value map. */ - std::function stream_interval_; /**< Callback method for finite bars. */ Zigzag_persistence pers_; /**< Class computing the pairs. */ }; // end class Filtered_zigzag_persistence diff --git a/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h index 63020d2a63..b99bec84a3 100644 --- a/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h @@ -199,14 +199,14 @@ class Zigzag_persistence * as input: first the dimension of the cycle, then the birth index of the cycle and third the death index of the * cycle. An index always corresponds to the arrow number the event occurred (one call to @ref insert_face, * @ref remove_face or @ref apply_identity is equal to one arrow and increases the arrow count by one). - * @param minNumberOfFaces Maximal value among the minimum numbers of faces known to be at the same time in each + * @param preallocationSize Maximal value among the minimum numbers of faces known to be at the same time in each * complex of the filtration. It will be used to optimize the memory allocation. * Default value: 0. */ Zigzag_persistence(std::function stream_interval, - unsigned int minNumberOfFaces = 0) + unsigned int preallocationSize = 0) : matrix_( - minNumberOfFaces, + preallocationSize, [this](matrix_index columnIndex1, matrix_index columnIndex2) -> bool { if (matrix_.get_column(columnIndex1).is_paired()) { return matrix_.get_pivot(columnIndex1) < matrix_.get_pivot(columnIndex2); @@ -225,11 +225,13 @@ class Zigzag_persistence * the first time in the filtration (if a face was inserted and then removed and reinserted etc., only the last * insertion counts). The face range should be ordered by increasing arrow numbers. * @param dimension Dimension of the inserted face. + * @return Number of the operation. */ template > - void insert_face(const BoundaryRange& boundary, dimension_type dimension) { + index insert_face(const BoundaryRange& boundary, dimension_type dimension) { ++numArrow_; _process_forward_arrow(boundary, dimension); + return numArrow_; } /** @@ -237,18 +239,21 @@ class Zigzag_persistence * * @param arrowNumber Arrow number of when the face to remove was inserted for the last time. * @param dimension Dimension of the face to remove. + * @return Number of the operation. */ - void remove_face(index arrowNumber, dimension_type dimension) { + index remove_face(index arrowNumber, dimension_type dimension) { ++numArrow_; _process_backward_arrow(arrowNumber, dimension); + return numArrow_; } /** * @brief To use when a face is neither inserted nor removed, but the filtration moves along the identity operator * on homology level. Useful to keep the birth/death indices aligned when insertions/removals are purposely skipped * to avoid useless computation. Increases the arrow number by one. + * @return Number of the operation. */ - void apply_identity() { ++numArrow_; } + index apply_identity() { return ++numArrow_; } /** * @brief Outputs through the given callback method all birth indices which are currently not paired with From 6dc421933f550205977a0f956afe9bab383589be Mon Sep 17 00:00:00 2001 From: hschreiber <48448038+hschreiber@users.noreply.github.com> Date: Thu, 18 Jul 2024 15:42:02 +0200 Subject: [PATCH 37/51] Update src/Zigzag_persistence/include/gudhi/zigzag_persistence.h Co-authored-by: Marc Glisse --- src/Zigzag_persistence/include/gudhi/zigzag_persistence.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h index b99bec84a3..837fde39d0 100644 --- a/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h @@ -302,10 +302,7 @@ class Zigzag_persistence if constexpr (erase_birth_history) { births_.emplace_hint(births_.end(), matrix_.get_column_with_pivot(numArrow_), numArrow_); } else { - auto res = births_.try_emplace(matrix_.get_column_with_pivot(numArrow_), numArrow_); - if (!res.second) { - res.first->second = numArrow_; - } + births_[matrix_.get_column_with_pivot(numArrow_)] = numArrow_; } } } From 9446badaf87adcc52f64b2d996b53fe3e47933b5 Mon Sep 17 00:00:00 2001 From: hschreiber <48448038+hschreiber@users.noreply.github.com> Date: Thu, 18 Jul 2024 15:42:22 +0200 Subject: [PATCH 38/51] Update src/Zigzag_persistence/include/gudhi/zigzag_persistence.h Co-authored-by: Marc Glisse --- src/Zigzag_persistence/include/gudhi/zigzag_persistence.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h index 837fde39d0..20731df242 100644 --- a/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h @@ -275,7 +275,7 @@ class Zigzag_persistence if (!col.is_paired()) { stream_infinite_interval(col.get_dimension(), p.second); } - } catch (const std::out_of_range& e) { + } catch (const std::out_of_range&) { continue; } } From bbd3d90d862e2455f37e3d70c3a9f64ef56317f3 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Fri, 19 Jul 2024 15:19:19 +0200 Subject: [PATCH 39/51] doc --- .../doc/Intro_zigzag_persistence.h | 142 +++++++++++++++++- .../gudhi/filtered_zigzag_persistence.h | 2 +- .../include/gudhi/zigzag_persistence.h | 2 +- 3 files changed, 141 insertions(+), 5 deletions(-) diff --git a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h index e04112092b..ed9d4fc540 100644 --- a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h +++ b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h @@ -53,11 +53,147 @@ namespace zigzag_persistence { * information about the filtration "on the fly" to avoid loading the whole filtration at once. Information about the * current barcode can be retrieved between any steps via callback methods. * - * \subsection zigzagexamples Examples + * \section zigzagexamples Examples + * + * \subsection zzminusage Minimalistic example of usage + * + * ### Includes + * + * #### Zigzag_persistence + * ``` + * #include + * ``` + * #### Filtered_zigzag_persistence and Filtered_zigzag_persistence_with_storage + * ``` + * #include + * ``` + * + * ### Useful aliases + * + * ``` + * using Zigzag_persistence = Gudhi::zigzag_persistence::Zigzag_persistence<>; + * using Filtered_zigzag_persistence = Gudhi::zigzag_persistence::Filtered_zigzag_persistence<>; + * using Filtered_zigzag_persistence_with_storage = Gudhi::zigzag_persistence::Filtered_zigzag_persistence_with_storage<>; + * + * using dimension_type = Zigzag_persistence::dimension_type; + * using index_type = Zigzag_persistence::index; + * using filtration_value_type = Filtered_zigzag_persistence::filtration_value; + * ``` + * + * ### Construction with default values + * + * #### Zigzag_persistence + * ``` + * //Zigzag_persistence(callback) with for example callback method as a anonymous lambda + * Zigzag_persistence zp([](dimension_type dim, index_type birth, index_type death) { + * std::cout << "[" << dim << "] " << birth << " - " << death << std::endl; + * }); + * ``` + * + * #### Filtered_zigzag_persistence + * ``` + * //Filtered_zigzag_persistence(callback) with for example callback method as a anonymous lambda + * Filtered_zigzag_persistence zp([](dimension_type dim, filtration_value_type birth, filtration_value_type death) { + * std::cout << "[" << dim << "] " << birth << " - " << death << std::endl; + * }); + * ``` + * + * #### Filtered_zigzag_persistence_with_storage + * ``` + * Filtered_zigzag_persistence_with_storage zp; + * ``` + * + * ### Input of the zigzag sequence/filtration + * + * In all cases, it is important that the operations of insertions and removals are made **in the same order** + * as in the zigzag filtration ones wants to compute the barcode from. + * + * #### Zigzag_persistence + * + * A face has to be identified in the boundaries by the operation number the face was inserted with in the sequence. + * + * ``` + * //inserts vertex 0 -> birth at 0 of 0-cycle + * zp.insert_face({}, 0); + * //inserts vertex 1 -> birth at 1 of 0-cycle + * zp.insert_face({}, 0); + * //inserts edge 2 = (0,1) -> death at 2 -> outputs (0, 1, 2) + * zp.insert_face({0, 1}, 1); + * //inserts vertex 3 -> birth at 3 of 0-cycle + * zp.insert_face({}, 0); + * //inserts edge 4 = (0,3) -> death at 4 -> outputs (0, 3, 4) + * zp.insert_face({0, 3}, 1); + * //inserts edge 5 = (1,3) -> birth at 5 of 1-cycle + * zp.insert_face({1, 3}, 1); + * //removes edge 4 -> death at 6 -> outputs (1, 5, 6) + * zp.remove_face(4, 1); + * //removes edge 2 -> birth at 7 of 0-cycle + * zp.remove_face(2, 1); + * ``` + * + * #### Filtered_zigzag_persistence and Filtered_zigzag_persistence_with_storage + * + * A face can be identified in the boundaries by any given numerical label, it is just important that the given + * filtration values are monotonous (ie., either only increasing or only decreasing). + * + * ``` + * //inserts vertex 2 at filtration value 0.1 -> birth at 0.1 of 0-cycle + * zp.insert_face(2, {}, 0, 0.1); + * //inserts vertex 4 at filtration value 0.1 -> birth at 0.1 of 0-cycle + * zp.insert_face(4, {}, 0, 0.1); + * //inserts edge 5 = (2,4) at filtration value 0.3 -> death at 0.3 -> outputs/stores (0, 0.1, 0.3) + * zp.insert_face(5, {2, 4}, 1, 0.3); + * //inserts vertex 3 at filtration value 0.4 -> birth at 0.4 of 0-cycle + * zp.insert_face(3, {}, 0, 0.4); + * //inserts edge 6 = (2,3) at filtration value 0.4 -> death at 0.4 of the cycle born at 0.4 -> outputs/stores nothing + * zp.insert_face(6, {2, 3}, 1, 0.4); + * //inserts edge 9 = (3,4) at filtration value 1.2 -> birth at 1.2 of 1-cycle + * zp.insert_face(9, {4, 3}, 1, 1.2); + * //removes edge 6 at filtration value 1.5 -> death at 1.5 -> outputs/stores (1, 1.2, 1.5) + * zp.remove_face(6, 1, 1.5); + * //removes edge 5 at filtration value 2.0 -> birth at 2.0 of 0-cycle + * zp.remove_face(5, 1, 2.0); + * ``` + * + * ### Finalizations + * + * For Zigzag_persistence and Filtered_zigzag_persistence, only the closed bars where output so far, so + * the open/infinite bars still need to be retrieved. For Filtered_zigzag_persistence_with_storage, the bars are + * stored within the class and where not output at all for now. + * + * #### Zigzag_persistence + * ``` + * //retrieve infinite bars, that is cycles which were born but did not die. + * //in this example, outputs (0, 0) and (0, 7) + * zp.get_current_infinite_intervals([](dimension_type dim, index_type birth){ + * std::cout << "[" << dim << "] " << birth << " - inf" << std::endl; + * }); + * ``` + * + * #### Filtered_zigzag_persistence + * ``` + * //retrieve infinite bars, that is cycles which were born but did not die. + * //in this example, outputs (0, 0.1) and (0, 2.0) + * zp.get_current_infinite_intervals([](dimension_type dim, filtration_value_type birth){ + * std::cout << "[" << dim << "] " << birth << " - inf" << std::endl; + * }); + * ``` + * + * #### Filtered_zigzag_persistence_with_storage + * ``` + * //get all bars in a vector + * auto barcode = zp.get_persistence_diagram(); + * + * //do something with the vector, e.g., stream out content: + * for (auto& bar : barcode) { + * std::cout << bar << std::endl; + * } + * ``` + * + * \subsection zzexamples More elaborate examples * - * Here is a list of zigzag persistence examples : * \li \gudhi_example_link{Zigzag_persistence,example_simple_zigzag_filtration.cpp} - A simple example to showcase how - * to use the @ref Filtered_zigzag_persistence_with_storage class. + * to use the @ref Filtered_zigzag_persistence_with_storage class within an input loop. * * \li \gudhi_example_link{Zigzag_persistence,example_zzfiltration_from_file.cpp} - An example of a "stream-like" usage * with @ref Filtered_zigzag_persistence by reading off the filtration from a file. diff --git a/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h index 70413c5986..c246c1bb63 100644 --- a/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h @@ -261,7 +261,7 @@ class Filtered_zigzag_persistence_with_storage * @return A vector of pairs of filtration values representing the persistence diagram. */ std::vector get_persistence_diagram(filtration_value shortestInterval = 0., - bool includeInfiniteBars = false) { + bool includeInfiniteBars = true) { std::vector diag = _get_persistence_diagram(shortestInterval); if (includeInfiniteBars) { diff --git a/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h index 20731df242..e70582b1c7 100644 --- a/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h @@ -446,7 +446,7 @@ class Zigzag_persistence private: Matrix_type matrix_; /**< Matrix storing a base of the current chain complex. */ - birth_dictionary births_; /**< Map face index in F to corresponding birth. */ + birth_dictionary births_; /**< Map face index in F to corresponding birth. */ Birth_ordering birthOrdering_; /**< Maintains stream_interval_; /**< Callback method for closed pairs. */ From c7edb9108afdaf346b2d298463f9967e89debcd5 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Fri, 19 Jul 2024 15:53:15 +0200 Subject: [PATCH 40/51] replacing map_index_to_filtration_value->pair with get_filtration_value_from_index->value --- .../gudhi/filtered_zigzag_persistence.h | 122 +++++++----------- .../test/test_filtered_zigzag_persistence.cpp | 5 +- 2 files changed, 51 insertions(+), 76 deletions(-) diff --git a/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h index c246c1bb63..b981f825b0 100644 --- a/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h @@ -141,11 +141,7 @@ class Filtered_zigzag_persistence_with_storage return numArrow_; } - if (filtrationValue != previousFiltrationValue_) // check whether the filt value has changed - { // consecutive pairs (i,f), (j,f') mean faces of index k in [i,j-1] have - previousFiltrationValue_ = filtrationValue; // filtration value f - filtrationValues_.emplace_back(numArrow_, previousFiltrationValue_); - } + _store_filtration_value(filtrationValue); [[maybe_unused]] auto res = handleToKey_.try_emplace(faceID, numArrow_); @@ -183,11 +179,7 @@ class Filtered_zigzag_persistence_with_storage auto it = handleToKey_.find(faceID); GUDHI_CHECK(it != handleToKey_.end(), "Zigzag_persistence::remove_face - face not in the complex"); - if (filtrationValue != previousFiltrationValue_) // check whether the filt value has changed - { // consecutive pairs (i,f), (j,f') mean faces of index k in [i,j-1] have - previousFiltrationValue_ = filtrationValue; // filtration value f - filtrationValues_.emplace_back(numArrow_, previousFiltrationValue_); - } + _store_filtration_value(filtrationValue); pers_.remove_face(it->second, dimension); handleToKey_.erase(it); @@ -217,47 +209,30 @@ class Filtered_zigzag_persistence_with_storage const std::vector& get_index_persistence_diagram() const { return persistenceDiagram_; } /** - * @brief Returns the filtration values \f$[f(b),f(d)]\f$ associated to the indices \f$[b,d]\f$ which are retrieved + * @brief Returns the filtration value \f$f(idx)\f$ associated to the index \f$idx\f$ returned * by @ref get_index_persistence_diagram. - * - * @param birthKey Birth index - * @param deathKey Death index - * @return A pair of filtration values associated to the given indices. + * + * @param idx Birth or death index + * @return filtration_value Filtration value associated to @p idx. */ - std::pair map_index_to_filtration_value(internal_key birthKey, - internal_key deathKey) const { - // filtration_values_ must be sorted by increasing keys. - auto itBirth = // lower_bound(x) returns leftmost y s.t. x <= y - std::lower_bound( - filtrationValues_.begin(), filtrationValues_.end(), - birthKey, - [](std::pair p, internal_key k) { - return p.first < k; - }); - if (itBirth == filtrationValues_.end() || itBirth->first > birthKey) { + filtration_value get_filtration_value_from_index(internal_key idx) { + // lower_bound(x) returns leftmost y s.t. x <= y + auto itBirth = + std::lower_bound(filtrationValues_.begin(), + filtrationValues_.end(), + idx, + [](std::pair p, internal_key k) { return p.first < k; }); + if (itBirth == filtrationValues_.end() || itBirth->first > idx) { --itBirth; } - // it points to the rightmost z such that z <= x - - auto itDeath = // - std::lower_bound( - filtrationValues_.begin(), filtrationValues_.end(), - deathKey, - [](std::pair p, internal_key k) { - return p.first < k; - }); - if (itDeath == filtrationValues_.end() || itDeath->first > deathKey) { - --itDeath; - } - - return std::make_pair(itBirth->second, itDeath->second); - } + return itBirth->second; + }; /** * @brief Returns the current persistence diagram. * * @param shortestInterval Threshold. Every bar shorter than the given value will be ignored. Default value: 0. - * @param includeInfiniteBars If set to true, infinite bars are included in the diagram. Default value: false. + * @param includeInfiniteBars If set to true, infinite bars are included in the diagram. Default value: true. * @return A vector of pairs of filtration values representing the persistence diagram. */ std::vector get_persistence_diagram(filtration_value shortestInterval = 0., @@ -272,11 +247,11 @@ class Filtered_zigzag_persistence_with_storage } private: - std::unordered_map handleToKey_; /**< Map from input keys to internal keys. */ - dimension_type dimMax_; /**< Maximal dimension of a bar to record. */ - std::vector persistenceDiagram_; /**< Stores current closed persistence intervals. */ - internal_key numArrow_; /**< Current arrow number. */ - filtration_value previousFiltrationValue_; /**< Filtration value of the previous arrow. */ + std::unordered_map handleToKey_; /**< Map from input keys to internal keys. */ + dimension_type dimMax_; /**< Maximal dimension of a bar to record. */ + std::vector persistenceDiagram_; /**< Stores current closed persistence intervals. */ + internal_key numArrow_; /**< Current arrow number. */ + filtration_value previousFiltrationValue_; /**< Filtration value of the previous arrow. */ /** * @brief filtrationValues_ stores consecutive pairs (i,f) , (j,f') with f != f', * meaning that all inserted faces with key in [i;j-1] have filtration value f, @@ -285,6 +260,21 @@ class Filtered_zigzag_persistence_with_storage std::vector > filtrationValues_; Zigzag_persistence pers_; /**< Class computing the pairs. */ + /** + * @brief Stores the filtration value if the value is new. Assumes that the given value is either greater (or equal) + * than previous ones or smaller (or equal) than previous ones. + * + * @param filtrationValue Filtration value to store. + */ + void _store_filtration_value(filtration_value filtrationValue) { + if (filtrationValue != previousFiltrationValue_) // check whether the filt value has changed + { + // consecutive pairs (i,f), (j,f') mean faces of index k in [i,j-1] have filtration value f + previousFiltrationValue_ = filtrationValue; + filtrationValues_.emplace_back(numArrow_, previousFiltrationValue_); + } + } + /** * @brief Returns the current persistence diagram without infinite bars. * @@ -301,8 +291,8 @@ class Filtered_zigzag_persistence_with_storage // }); for (auto bar : persistenceDiagram_) { - filtration_value birth, death; - std::tie(birth, death) = map_index_to_filtration_value(bar.birth, bar.death); + filtration_value birth = get_filtration_value_from_index(bar.birth); + filtration_value death = get_filtration_value_from_index(bar.death); if (birth > death) { std::swap(birth, death); } @@ -321,23 +311,9 @@ class Filtered_zigzag_persistence_with_storage * @param diag Reference to vector where to store the infinite bars. */ void _retrieve_infinite_bars(std::vector& diag) { - auto birth = [this](internal_key birthKey) { - auto itBirth = // lower_bound(x) returns leftmost y s.t. x <= y - std::lower_bound( - filtrationValues_.begin(), filtrationValues_.end(), - birthKey, - [](std::pair p, internal_key k) { - return p.first < k; - }); - if (itBirth == filtrationValues_.end() || itBirth->first > birthKey) { - --itBirth; - } - return itBirth->second; - }; - auto stream_infinite_interval = [&](dimension_type dim, internal_key birthIndex) { if (dimMax_ == -1 || (dimMax_ != -1 && dim < dimMax_)) - diag.emplace_back(birth(birthIndex), Filtration_value_interval::inf, dim); + diag.emplace_back(get_filtration_value_from_index(birthIndex), Filtration_value_interval::inf, dim); }; pers_.get_current_infinite_intervals(stream_infinite_interval); @@ -359,11 +335,11 @@ class Filtered_zigzag_persistence_with_storage template class Filtered_zigzag_persistence { public: - using Options = FilteredZigzagOptions; /**< Zigzag options. */ - using internal_key = typename Options::internal_key; /**< Key and index type, has to be signed. */ - using face_key = typename Options::face_key; /**< Face ID type from external inputs. */ - using filtration_value = typename Options::filtration_value; /**< Type for filtration values. */ - using dimension_type = typename Options::dimension_type; /**< Type for dimension values. */ + using Options = FilteredZigzagOptions; /**< Zigzag options. */ + using internal_key = typename Options::internal_key; /**< Key and index type, has to be signed. */ + using face_key = typename Options::face_key; /**< Face ID type from external inputs. */ + using filtration_value = typename Options::filtration_value; /**< Type for filtration values. */ + using dimension_type = typename Options::dimension_type; /**< Type for dimension values. */ /** * @brief Constructor. @@ -479,10 +455,10 @@ class Filtered_zigzag_persistence { template using dictionary = std::unordered_map; // TODO: benchmark with other map types - dictionary handleToKey_; /**< Map from input keys to internal keys. */ - internal_key numArrow_; /**< Current arrow number. */ - dictionary keyToFiltrationValue_; /**< Face Key to filtration value map. */ - Zigzag_persistence pers_; /**< Class computing the pairs. */ + dictionary handleToKey_; /**< Map from input keys to internal keys. */ + internal_key numArrow_; /**< Current arrow number. */ + dictionary keyToFiltrationValue_; /**< Face Key to filtration value map. */ + Zigzag_persistence pers_; /**< Class computing the pairs. */ }; // end class Filtered_zigzag_persistence } // namespace zigzag_persistence diff --git a/src/Zigzag_persistence/test/test_filtered_zigzag_persistence.cpp b/src/Zigzag_persistence/test/test_filtered_zigzag_persistence.cpp index 702590aa46..111892bc85 100644 --- a/src/Zigzag_persistence/test/test_filtered_zigzag_persistence.cpp +++ b/src/Zigzag_persistence/test/test_filtered_zigzag_persistence.cpp @@ -73,9 +73,8 @@ void test_indices(ZP& zp, std::vector& indices, BOOST_CHECK_EQUAL(interval.dim, it->dim); BOOST_CHECK_EQUAL(interval.birth, it->birth); BOOST_CHECK_EQUAL(interval.death, it->death); - auto p = zp.map_index_to_filtration_value(interval.birth, interval.death); - BOOST_CHECK_EQUAL(p.first, indexToFil[interval.birth]); - BOOST_CHECK_EQUAL(p.second, indexToFil[interval.death]); + BOOST_CHECK_EQUAL(zp.get_filtration_value_from_index(interval.birth), indexToFil[interval.birth]); + BOOST_CHECK_EQUAL(zp.get_filtration_value_from_index(interval.death), indexToFil[interval.death]); ++it; } BOOST_CHECK(it == indices.end()); From 34721602983d2c6b8ca650aa37b40b8e6409b49e Mon Sep 17 00:00:00 2001 From: hschreiber Date: Fri, 19 Jul 2024 17:37:14 +0200 Subject: [PATCH 41/51] doc --- src/Zigzag_persistence/concept/ZigzagOptions.h | 1 + .../include/gudhi/filtered_zigzag_persistence.h | 13 ++++++++----- .../include/gudhi/zigzag_persistence.h | 13 +++++-------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/Zigzag_persistence/concept/ZigzagOptions.h b/src/Zigzag_persistence/concept/ZigzagOptions.h index 712d5df2a1..af9636a720 100644 --- a/src/Zigzag_persistence/concept/ZigzagOptions.h +++ b/src/Zigzag_persistence/concept/ZigzagOptions.h @@ -32,6 +32,7 @@ struct FilteredZigzagOptions { /** * @brief Type for the face IDs used at insertion and in the boundaries given as argument. + * Has to be usable as key in a hashtable, so "hashable" and comparable. */ using face_key = unspecified; diff --git a/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h index b981f825b0..f0a0a92f48 100644 --- a/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h @@ -352,18 +352,21 @@ class Filtered_zigzag_persistence { * Has to take three arguments as input: first the dimension of the cycle, then the birth value of the cycle * and third the death value of the cycle. The values corresponds to the filtration values which were given at * insertions or removals. - * @param preallocationSize Minimum number of faces that will be in a complex at some point in the filtration. - * If the maximal number of faces is known in advance, the memory allocation can be better optimized. - * Default value: 0. + * @param preallocationSize Space for @p preallocationSize faces are reserved in the underlying structure. + * Theoretically, any values works therefore, but for better performances, it is better to be as close as possible + * to the maximal value of the number of faces stored at the same time. At a same time are stored faces which were + * inserted before that time but not removed until that time. Default value: 0. * @tparam F Type of callback method. */ - template + template Filtered_zigzag_persistence(F&& stream_interval, unsigned int preallocationSize = 0) : handleToKey_(preallocationSize), numArrow_(-1), keyToFiltrationValue_(preallocationSize), pers_( - [&,stream_interval](dimension_type dim, internal_key birth, internal_key death) { + [&, stream_interval = std::forward(stream_interval)](dimension_type dim, + internal_key birth, + internal_key death) { auto itB = keyToFiltrationValue_.find(birth); auto itD = keyToFiltrationValue_.find(death); if (itB->second != itD->second) stream_interval(dim, itB->second, itD->second); diff --git a/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h index e70582b1c7..d266935867 100644 --- a/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h @@ -199,9 +199,10 @@ class Zigzag_persistence * as input: first the dimension of the cycle, then the birth index of the cycle and third the death index of the * cycle. An index always corresponds to the arrow number the event occurred (one call to @ref insert_face, * @ref remove_face or @ref apply_identity is equal to one arrow and increases the arrow count by one). - * @param preallocationSize Maximal value among the minimum numbers of faces known to be at the same time in each - * complex of the filtration. It will be used to optimize the memory allocation. - * Default value: 0. + * @param preallocationSize Space for @p preallocationSize faces are reserved in the underlying structure. + * Theoretically, any values works therefore, but for better performances, it is better to be as close as possible + * to the maximal value of the number of faces stored at the same time. At a same time are stored faces which were + * inserted before that time but not removed until that time. Default value: 0. */ Zigzag_persistence(std::function stream_interval, unsigned int preallocationSize = 0) @@ -299,11 +300,7 @@ class Zigzag_persistence _apply_surjective_reflection_diamond(dim, chainsInF); } else { birthOrdering_.add_birth_forward(numArrow_); - if constexpr (erase_birth_history) { - births_.emplace_hint(births_.end(), matrix_.get_column_with_pivot(numArrow_), numArrow_); - } else { - births_[matrix_.get_column_with_pivot(numArrow_)] = numArrow_; - } + births_[matrix_.get_column_with_pivot(numArrow_)] = numArrow_; } } From 744ac4702749246e4114a67c1f5636e3e8033576 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Mon, 22 Jul 2024 14:38:23 +0200 Subject: [PATCH 42/51] dim bug fix --- .../concept/ZigzagOptions.h | 4 +- .../doc/Intro_zigzag_persistence.h | 8 ++-- .../example_simple_zigzag_filtration.cpp | 3 +- .../example_zzfiltration_from_file.cpp | 2 +- .../gudhi/filtered_zigzag_persistence.h | 47 ++++++++++--------- .../include/gudhi/zigzag_persistence.h | 18 +++---- .../test/test_filtered_zigzag_persistence.cpp | 28 +++++------ .../test/test_zigzag_persistence.cpp | 12 ++--- 8 files changed, 62 insertions(+), 60 deletions(-) diff --git a/src/Zigzag_persistence/concept/ZigzagOptions.h b/src/Zigzag_persistence/concept/ZigzagOptions.h index af9636a720..cc83a8285e 100644 --- a/src/Zigzag_persistence/concept/ZigzagOptions.h +++ b/src/Zigzag_persistence/concept/ZigzagOptions.h @@ -26,7 +26,7 @@ namespace zigzag_persistence { */ struct FilteredZigzagOptions { /** - * @brief Type for the face IDs used internally and other indexations. It must be signed. + * @brief Numerical type for the face IDs used internally and other indexations. It must be signed. */ using internal_key = unspecified; @@ -59,7 +59,7 @@ struct FilteredZigzagOptions { */ struct ZigzagOptions { /** - * @brief Type for the face IDs used internally and other indexations. It must be signed. + * @brief Numerical type for the face IDs used internally and other indexations. It must be signed. */ using internal_key = unspecified; diff --git a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h index ed9d4fc540..0524713c7f 100644 --- a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h +++ b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h @@ -126,9 +126,9 @@ namespace zigzag_persistence { * //inserts edge 5 = (1,3) -> birth at 5 of 1-cycle * zp.insert_face({1, 3}, 1); * //removes edge 4 -> death at 6 -> outputs (1, 5, 6) - * zp.remove_face(4, 1); + * zp.remove_face(4); * //removes edge 2 -> birth at 7 of 0-cycle - * zp.remove_face(2, 1); + * zp.remove_face(2); * ``` * * #### Filtered_zigzag_persistence and Filtered_zigzag_persistence_with_storage @@ -150,9 +150,9 @@ namespace zigzag_persistence { * //inserts edge 9 = (3,4) at filtration value 1.2 -> birth at 1.2 of 1-cycle * zp.insert_face(9, {4, 3}, 1, 1.2); * //removes edge 6 at filtration value 1.5 -> death at 1.5 -> outputs/stores (1, 1.2, 1.5) - * zp.remove_face(6, 1, 1.5); + * zp.remove_face(6, 1.5); * //removes edge 5 at filtration value 2.0 -> birth at 2.0 of 0-cycle - * zp.remove_face(5, 1, 2.0); + * zp.remove_face(5, 2.0); * ``` * * ### Finalizations diff --git a/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp b/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp index 012893adf5..17dcdb816f 100644 --- a/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp +++ b/src/Zigzag_persistence/example/example_simple_zigzag_filtration.cpp @@ -128,8 +128,7 @@ int main(int argc, char* const argv[]) { zp.insert_face(i, simplices[i], dim, fils[i]); } else { auto id = simplices[i][0]; - int dim = simplices[id].size() == 0 ? 0 : simplices[id].size() - 1; - zp.remove_face(id, dim, fils[i]); + zp.remove_face(id, fils[i]); } } diff --git a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp index e4da46e969..5f85f2a900 100644 --- a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp +++ b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp @@ -103,7 +103,7 @@ int main(int argc, char* const argv[]) { zp.insert_face(id, data, dim, timestamp); } else if (type == REMOVAL) { ++id; - zp.remove_face(data[0], data[1], timestamp); + zp.remove_face(data[0], timestamp); } } diff --git a/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h index f0a0a92f48..01ec87d042 100644 --- a/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h @@ -134,13 +134,12 @@ class Filtered_zigzag_persistence_with_storage dimension_type dimension, filtration_value filtrationValue) { - ++numArrow_; - if (dimMax_ != -1 && dimension > dimMax_) { - pers_.apply_identity(); - return numArrow_; + return apply_identity(); } + ++numArrow_; + _store_filtration_value(filtrationValue); [[maybe_unused]] auto res = handleToKey_.try_emplace(faceID, numArrow_); @@ -159,29 +158,28 @@ class Filtered_zigzag_persistence_with_storage } /** - * @brief Updates the zigzag persistence diagram after the removal of the given face. + * @brief Updates the zigzag persistence diagram after the removal of the given face if the face was contained + * in the current complex (note that it will not contain faces of dimension > ignoreCyclesAboveDim if the latter was + * non negative at construction of the class). Otherwise, just increases the operation count by one. * * @param faceID ID representing the face to remove. Should be the same than the one used to insert it. - * @param dimension Dimension of the face. * @param filtrationValue Filtration value associated to the removal. * Assumed to be always larger or equal to previously used filtration values or always smaller or equal than previous * values, ie. the changes are monotonous. * @return Number of the operation. */ - internal_key remove_face(face_key faceID, dimension_type dimension, filtration_value filtrationValue) { - ++numArrow_; + internal_key remove_face(face_key faceID, filtration_value filtrationValue) { + auto it = handleToKey_.find(faceID); - if (dimMax_ != -1 && dimension > dimMax_) { - pers_.apply_identity(); - return numArrow_; + if (it == handleToKey_.end()) { + return apply_identity(); } - auto it = handleToKey_.find(faceID); - GUDHI_CHECK(it != handleToKey_.end(), "Zigzag_persistence::remove_face - face not in the complex"); + ++numArrow_; _store_filtration_value(filtrationValue); - pers_.remove_face(it->second, dimension); + pers_.remove_face(it->second); handleToKey_.erase(it); return numArrow_; @@ -389,10 +387,11 @@ class Filtered_zigzag_persistence { * values, ie. the changes are monotonous. */ template > - void insert_face(face_key faceID, - const BoundaryRange& boundary, - dimension_type dimension, - filtration_value filtrationValue) { + internal_key insert_face(face_key faceID, + const BoundaryRange& boundary, + dimension_type dimension, + filtration_value filtrationValue) + { ++numArrow_; [[maybe_unused]] auto res = handleToKey_.try_emplace(faceID, numArrow_); @@ -408,18 +407,19 @@ class Filtered_zigzag_persistence { } pers_.insert_face(translatedBoundary, dimension); + + return numArrow_; } /** * @brief Updates the zigzag persistence diagram after the removal of the given face. * * @param faceID ID representing the face to remove. Should be the same than the one used to insert it. - * @param dimension Dimension of the face. * @param filtrationValue Filtration value associated to the removal. * Assumed to be always larger or equal to previously used filtration values or always smaller or equal than previous * values, ie. the changes are monotonous. */ - void remove_face(face_key faceID, dimension_type dimension, filtration_value filtrationValue) { + internal_key remove_face(face_key faceID, filtration_value filtrationValue) { ++numArrow_; auto it = handleToKey_.find(faceID); @@ -427,8 +427,10 @@ class Filtered_zigzag_persistence { keyToFiltrationValue_.try_emplace(numArrow_, filtrationValue); - pers_.remove_face(it->second, dimension); + pers_.remove_face(it->second); handleToKey_.erase(it); + + return numArrow_; } /** @@ -436,9 +438,10 @@ class Filtered_zigzag_persistence { * on homology level. Useful to keep the birth/death indices aligned when insertions/removals are purposely skipped * to avoid useless computation. */ - void apply_identity() { + internal_key apply_identity() { ++numArrow_; pers_.apply_identity(); + return numArrow_; } /** diff --git a/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h index d266935867..3bca4df194 100644 --- a/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h @@ -239,12 +239,11 @@ class Zigzag_persistence * @brief Updates the zigzag persistence diagram after the removal of the given face. * * @param arrowNumber Arrow number of when the face to remove was inserted for the last time. - * @param dimension Dimension of the face to remove. * @return Number of the operation. */ - index remove_face(index arrowNumber, dimension_type dimension) { + index remove_face(index arrowNumber) { ++numArrow_; - _process_backward_arrow(arrowNumber, dimension); + _process_backward_arrow(arrowNumber); return numArrow_; } @@ -294,7 +293,7 @@ class Zigzag_persistence */ template void _process_forward_arrow(const BoundaryRange& boundary, dimension_type dim) { - std::vector chainsInF = matrix_.insert_boundary(numArrow_, boundary); + std::vector chainsInF = matrix_.insert_boundary(numArrow_, boundary, dim); if (!chainsInF.empty()) { _apply_surjective_reflection_diamond(dim, chainsInF); @@ -396,7 +395,7 @@ class Zigzag_persistence * @param faceID Internal ID of the face to remove. * @param dim Dimension of the face to remove. */ - void _process_backward_arrow(index faceID, dimension_type dim) { + void _process_backward_arrow(index faceID) { // column whose key is the one of the removed face matrix_index currCol = matrix_.get_column_with_pivot(faceID); @@ -417,9 +416,10 @@ class Zigzag_persistence } // curr_col points to the column to remove by restriction of K to K-{\sigma} - if (!matrix_.get_column(currCol).is_paired()) { // in F + auto& col = matrix_.get_column(currCol); + if (!col.is_paired()) { // in F auto it = births_.find(currCol); - stream_interval_(dim, it->second, numArrow_); + stream_interval_(col.get_dimension(), it->second, numArrow_); if constexpr (erase_birth_history) { birthOrdering_.remove_birth(it->second); births_.erase(it); @@ -428,9 +428,9 @@ class Zigzag_persistence // maintain the <=b order birthOrdering_.add_birth_backward(numArrow_); if constexpr (erase_birth_history) { - births_.try_emplace(matrix_.get_column(currCol).get_paired_chain_index(), numArrow_); + births_.try_emplace(col.get_paired_chain_index(), numArrow_); } else { - auto res = births_.try_emplace(matrix_.get_column(currCol).get_paired_chain_index(), numArrow_); + auto res = births_.try_emplace(col.get_paired_chain_index(), numArrow_); if (!res.second) { res.first->second = numArrow_; } diff --git a/src/Zigzag_persistence/test/test_filtered_zigzag_persistence.cpp b/src/Zigzag_persistence/test/test_filtered_zigzag_persistence.cpp index 111892bc85..6219f63bdd 100644 --- a/src/Zigzag_persistence/test/test_filtered_zigzag_persistence.cpp +++ b/src/Zigzag_persistence/test/test_filtered_zigzag_persistence.cpp @@ -160,7 +160,7 @@ void test_filtered_zigzag_with_storage() { for (unsigned int i = 14; i < 16; ++i) { auto id = simplices[i][0]; - zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[i]); + zp.remove_face(id, filValues[i]); } for (unsigned int i = 16; i < 24; ++i) { @@ -179,7 +179,7 @@ void test_filtered_zigzag_with_storage() { for (unsigned int i = 24; i < 27; ++i) { auto id = simplices[i][0]; - zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[i]); + zp.remove_face(id, filValues[i]); } realIndices.emplace_back(24, 25, 1); @@ -191,7 +191,7 @@ void test_filtered_zigzag_with_storage() { realBarcode.emplace_back(7, 9, 2); auto id = simplices[28][0]; - zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[28]); + zp.remove_face(id, filValues[28]); realBarcode.emplace_back(0, Interval_filtration::inf, 0); realBarcode.emplace_back(9, Interval_filtration::inf, 0); @@ -231,7 +231,7 @@ void test_filtered_zigzag_with_storage_max1() { for (unsigned int i = 14; i < 16; ++i) { auto id = simplices[i][0]; - zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[i]); + zp.remove_face(id, filValues[i]); } for (unsigned int i = 16; i < 24; ++i) { @@ -243,12 +243,12 @@ void test_filtered_zigzag_with_storage_max1() { for (unsigned int i = 24; i < 27; ++i) { auto id = simplices[i][0]; - zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[i]); + zp.remove_face(id, filValues[i]); } zp.insert_face(27, simplices[27], simplices[27].size() == 0 ? 0 : simplices[27].size() - 1, filValues[27]); auto id = simplices[28][0]; - zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[28]); + zp.remove_face(id, filValues[28]); realBarcode.emplace_back(0, Interval_filtration::inf, 0); realBarcode.emplace_back(9, Interval_filtration::inf, 0); @@ -319,7 +319,7 @@ void test_filtered_zigzag() { for (unsigned int i = 14; i < 16; ++i) { interval = realBarcode[i]; auto id = simplices[i][0]; - zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[i]); + zp.remove_face(id, filValues[i]); } for (unsigned int i = 16; i < 24; ++i) { @@ -330,7 +330,7 @@ void test_filtered_zigzag() { for (unsigned int i = 24; i < 27; ++i) { interval = realBarcode[i]; auto id = simplices[i][0]; - zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[i]); + zp.remove_face(id, filValues[i]); } interval = realBarcode[27]; @@ -338,9 +338,9 @@ void test_filtered_zigzag() { interval = realBarcode[28]; auto id = simplices[28][0]; - zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[28]); + zp.remove_face(id, filValues[28]); - //there is no real garantee on the order of the infinite bars + //there is no real guarantee on the order of the infinite bars std::vector infiniteBars; zp.get_current_infinite_intervals([&](dimension_type dim, filtration_value birth) { infiniteBars.emplace_back(dim, birth, std::numeric_limits::infinity()); @@ -423,7 +423,7 @@ void test_filtered_zigzag_max1() { for (unsigned int i = 14; i < 16; ++i) { interval = realBarcode[i]; auto id = simplices[i][0]; - zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[i]); + zp.remove_face(id, filValues[i]); } for (unsigned int i = 16; i < 24; ++i) { @@ -434,7 +434,7 @@ void test_filtered_zigzag_max1() { for (unsigned int i = 24; i < 27; ++i) { interval = realBarcode[i]; auto id = simplices[i][0]; - zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[i]); + zp.remove_face(id, filValues[i]); } interval = realBarcode[27]; @@ -442,9 +442,9 @@ void test_filtered_zigzag_max1() { interval = realBarcode[28]; auto id = simplices[28][0]; - zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1, filValues[28]); + zp.remove_face(id, filValues[28]); - //there is no real garantee on the order of the infinite bars + //there is no real guarantee on the order of the infinite bars std::vector infiniteBars; zp.get_current_infinite_intervals([&](dimension_type dim, filtration_value birth) { if (dim < 1){ diff --git a/src/Zigzag_persistence/test/test_zigzag_persistence.cpp b/src/Zigzag_persistence/test/test_zigzag_persistence.cpp index 8dd3a7b032..99eb62f375 100644 --- a/src/Zigzag_persistence/test/test_zigzag_persistence.cpp +++ b/src/Zigzag_persistence/test/test_zigzag_persistence.cpp @@ -109,7 +109,7 @@ BOOST_AUTO_TEST_CASE(zigzag_persistence_single) { for (unsigned int i = 14; i < 16; ++i) { auto id = simplices[i][0]; - zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1); + zp.remove_face(id); } for (unsigned int i = 16; i < 24; ++i) { @@ -124,7 +124,7 @@ BOOST_AUTO_TEST_CASE(zigzag_persistence_single) { for (unsigned int i = 24; i < 27; ++i) { auto id = simplices[i][0]; - zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1); + zp.remove_face(id); } realIndices.emplace_back(1, 24, 25); @@ -134,7 +134,7 @@ BOOST_AUTO_TEST_CASE(zigzag_persistence_single) { realIndices.emplace_back(2, 23, 27); auto id = simplices[28][0]; - zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1); + zp.remove_face(id); realIndices.emplace_back(0, 0, -1); realIndices.emplace_back(0, 26, -1); @@ -175,7 +175,7 @@ BOOST_AUTO_TEST_CASE(zigzag_persistence_single_max1) { for (unsigned int i = 14; i < 16; ++i) { auto id = simplices[i][0]; - zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1); + zp.remove_face(id); } for (unsigned int i = 16; i < 24; ++i) { @@ -186,12 +186,12 @@ BOOST_AUTO_TEST_CASE(zigzag_persistence_single_max1) { for (unsigned int i = 24; i < 27; ++i) { auto id = simplices[i][0]; - zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1); + zp.remove_face(id); } zp.insert_face(simplices[27], simplices[27].size() == 0 ? 0 : simplices[27].size() - 1); auto id = simplices[28][0]; - zp.remove_face(id, simplices[id].size() == 0 ? 0 : simplices[id].size() - 1); + zp.remove_face(id); realIndices.emplace_back(0, 0, -1); realIndices.emplace_back(0, 26, -1); From 7d84dbfa984c79c6f4bb5da8b34206d3ea161859 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Mon, 22 Jul 2024 18:05:02 +0200 Subject: [PATCH 43/51] doc --- .../doc/Intro_zigzag_persistence.h | 154 ++---------------- src/Zigzag_persistence/example/CMakeLists.txt | 14 +- ...ample_zigzag_filtration_as_input_loop.cpp} | 0 .../gudhi/filtered_zigzag_persistence.h | 136 ++++++++++++++-- .../include/gudhi/zigzag_persistence.h | 76 +++++++-- src/common/doc/examples.h | 5 +- 6 files changed, 222 insertions(+), 163 deletions(-) rename src/Zigzag_persistence/example/{example_simple_zigzag_filtration.cpp => example_zigzag_filtration_as_input_loop.cpp} (100%) diff --git a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h index 0524713c7f..9c5537b230 100644 --- a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h +++ b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h @@ -55,146 +55,28 @@ namespace zigzag_persistence { * * \section zigzagexamples Examples * - * \subsection zzminusage Minimalistic example of usage - * - * ### Includes - * - * #### Zigzag_persistence - * ``` - * #include - * ``` - * #### Filtered_zigzag_persistence and Filtered_zigzag_persistence_with_storage - * ``` - * #include - * ``` - * - * ### Useful aliases - * - * ``` - * using Zigzag_persistence = Gudhi::zigzag_persistence::Zigzag_persistence<>; - * using Filtered_zigzag_persistence = Gudhi::zigzag_persistence::Filtered_zigzag_persistence<>; - * using Filtered_zigzag_persistence_with_storage = Gudhi::zigzag_persistence::Filtered_zigzag_persistence_with_storage<>; - * - * using dimension_type = Zigzag_persistence::dimension_type; - * using index_type = Zigzag_persistence::index; - * using filtration_value_type = Filtered_zigzag_persistence::filtration_value; - * ``` - * - * ### Construction with default values - * - * #### Zigzag_persistence - * ``` - * //Zigzag_persistence(callback) with for example callback method as a anonymous lambda - * Zigzag_persistence zp([](dimension_type dim, index_type birth, index_type death) { - * std::cout << "[" << dim << "] " << birth << " - " << death << std::endl; - * }); - * ``` - * - * #### Filtered_zigzag_persistence - * ``` - * //Filtered_zigzag_persistence(callback) with for example callback method as a anonymous lambda - * Filtered_zigzag_persistence zp([](dimension_type dim, filtration_value_type birth, filtration_value_type death) { - * std::cout << "[" << dim << "] " << birth << " - " << death << std::endl; - * }); - * ``` - * - * #### Filtered_zigzag_persistence_with_storage - * ``` - * Filtered_zigzag_persistence_with_storage zp; - * ``` - * - * ### Input of the zigzag sequence/filtration - * - * In all cases, it is important that the operations of insertions and removals are made **in the same order** - * as in the zigzag filtration ones wants to compute the barcode from. - * - * #### Zigzag_persistence - * - * A face has to be identified in the boundaries by the operation number the face was inserted with in the sequence. - * - * ``` - * //inserts vertex 0 -> birth at 0 of 0-cycle - * zp.insert_face({}, 0); - * //inserts vertex 1 -> birth at 1 of 0-cycle - * zp.insert_face({}, 0); - * //inserts edge 2 = (0,1) -> death at 2 -> outputs (0, 1, 2) - * zp.insert_face({0, 1}, 1); - * //inserts vertex 3 -> birth at 3 of 0-cycle - * zp.insert_face({}, 0); - * //inserts edge 4 = (0,3) -> death at 4 -> outputs (0, 3, 4) - * zp.insert_face({0, 3}, 1); - * //inserts edge 5 = (1,3) -> birth at 5 of 1-cycle - * zp.insert_face({1, 3}, 1); - * //removes edge 4 -> death at 6 -> outputs (1, 5, 6) - * zp.remove_face(4); - * //removes edge 2 -> birth at 7 of 0-cycle - * zp.remove_face(2); - * ``` - * - * #### Filtered_zigzag_persistence and Filtered_zigzag_persistence_with_storage - * - * A face can be identified in the boundaries by any given numerical label, it is just important that the given - * filtration values are monotonous (ie., either only increasing or only decreasing). - * - * ``` - * //inserts vertex 2 at filtration value 0.1 -> birth at 0.1 of 0-cycle - * zp.insert_face(2, {}, 0, 0.1); - * //inserts vertex 4 at filtration value 0.1 -> birth at 0.1 of 0-cycle - * zp.insert_face(4, {}, 0, 0.1); - * //inserts edge 5 = (2,4) at filtration value 0.3 -> death at 0.3 -> outputs/stores (0, 0.1, 0.3) - * zp.insert_face(5, {2, 4}, 1, 0.3); - * //inserts vertex 3 at filtration value 0.4 -> birth at 0.4 of 0-cycle - * zp.insert_face(3, {}, 0, 0.4); - * //inserts edge 6 = (2,3) at filtration value 0.4 -> death at 0.4 of the cycle born at 0.4 -> outputs/stores nothing - * zp.insert_face(6, {2, 3}, 1, 0.4); - * //inserts edge 9 = (3,4) at filtration value 1.2 -> birth at 1.2 of 1-cycle - * zp.insert_face(9, {4, 3}, 1, 1.2); - * //removes edge 6 at filtration value 1.5 -> death at 1.5 -> outputs/stores (1, 1.2, 1.5) - * zp.remove_face(6, 1.5); - * //removes edge 5 at filtration value 2.0 -> birth at 2.0 of 0-cycle - * zp.remove_face(5, 2.0); - * ``` - * - * ### Finalizations - * - * For Zigzag_persistence and Filtered_zigzag_persistence, only the closed bars where output so far, so - * the open/infinite bars still need to be retrieved. For Filtered_zigzag_persistence_with_storage, the bars are - * stored within the class and where not output at all for now. - * - * #### Zigzag_persistence - * ``` - * //retrieve infinite bars, that is cycles which were born but did not die. - * //in this example, outputs (0, 0) and (0, 7) - * zp.get_current_infinite_intervals([](dimension_type dim, index_type birth){ - * std::cout << "[" << dim << "] " << birth << " - inf" << std::endl; - * }); - * ``` - * - * #### Filtered_zigzag_persistence - * ``` - * //retrieve infinite bars, that is cycles which were born but did not die. - * //in this example, outputs (0, 0.1) and (0, 2.0) - * zp.get_current_infinite_intervals([](dimension_type dim, filtration_value_type birth){ - * std::cout << "[" << dim << "] " << birth << " - inf" << std::endl; - * }); - * ``` - * - * #### Filtered_zigzag_persistence_with_storage - * ``` - * //get all bars in a vector - * auto barcode = zp.get_persistence_diagram(); - * - * //do something with the vector, e.g., stream out content: - * for (auto& bar : barcode) { - * std::cout << bar << std::endl; - * } - * ``` + * \subsection zzminusage Minimalistic examples + * + * \li \gudhi_example_link{Zigzag_persistence,example_usage_zigzag_persistence.cpp} - A simple example to showcase how + * to use the @ref Zigzag_persistence class to compute a barcode. + *

+ * @include example_usage_zigzag_persistence.cpp + *
+ * \li \gudhi_example_link{Zigzag_persistence,example_usage_filtered_zigzag_persistence.cpp} - A simple example to + * showcase how to use the @ref Filtered_zigzag_persistence class to compute a barcode. + *
+ * @include example_usage_filtered_zigzag_persistence.cpp + *
+ * \li \gudhi_example_link{Zigzag_persistence,example_usage_filtered_zigzag_persistence_with_storage.cpp} - A simple + * example to showcase how to use the @ref Filtered_zigzag_persistence_with_storage class to compute a barcode. + *
+ * @include example_usage_filtered_zigzag_persistence_with_storage.cpp + *
* * \subsection zzexamples More elaborate examples * - * \li \gudhi_example_link{Zigzag_persistence,example_simple_zigzag_filtration.cpp} - A simple example to showcase how + * \li \gudhi_example_link{Zigzag_persistence,example_zigzag_filtration_as_input_loop.cpp} - A simple example to showcase how * to use the @ref Filtered_zigzag_persistence_with_storage class within an input loop. - * * \li \gudhi_example_link{Zigzag_persistence,example_zzfiltration_from_file.cpp} - An example of a "stream-like" usage * with @ref Filtered_zigzag_persistence by reading off the filtration from a file. * diff --git a/src/Zigzag_persistence/example/CMakeLists.txt b/src/Zigzag_persistence/example/CMakeLists.txt index 0cb6521839..ac434df9e6 100644 --- a/src/Zigzag_persistence/example/CMakeLists.txt +++ b/src/Zigzag_persistence/example/CMakeLists.txt @@ -1,7 +1,16 @@ project(Zigzag_persistence_examples) -add_executable_with_targets(Zigzag_persistence_example_simple_zigzag_filtration example_simple_zigzag_filtration.cpp TBB::tbb) -add_test(NAME Zigzag_persistence_example_simple_zigzag_filtration COMMAND $) +add_executable_with_targets(Zigzag_persistence_example_usage_zigzag_persistence example_usage_zigzag_persistence.cpp TBB::tbb) +add_test(NAME Zigzag_persistence_example_usage_zigzag_persistence COMMAND $) + +add_executable_with_targets(Zigzag_persistence_example_usage_filtered_zigzag_persistence example_usage_filtered_zigzag_persistence.cpp TBB::tbb) +add_test(NAME Zigzag_persistence_example_usage_filtered_zigzag_persistence COMMAND $) + +add_executable_with_targets(Zigzag_persistence_example_usage_filtered_zigzag_persistence_with_storage example_usage_filtered_zigzag_persistence_with_storage.cpp TBB::tbb) +add_test(NAME Zigzag_persistence_example_usage_filtered_zigzag_persistence_with_storage COMMAND $) + +add_executable_with_targets(Zigzag_persistence_example_zigzag_filtration_as_input_loop example_zigzag_filtration_as_input_loop.cpp TBB::tbb) +add_test(NAME Zigzag_persistence_example_zigzag_filtration_as_input_loop COMMAND $) add_executable_with_targets(Zigzag_persistence_example_zzfiltration_from_file example_zzfiltration_from_file.cpp TBB::tbb) file(COPY "zigzag_filtration_example.txt" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) @@ -9,3 +18,4 @@ add_test(NAME Zigzag_persistence_example_zzfiltration_from_file COMMAND $ + * ``` + * + * #### Useful aliases + * ``` + * using Filtered_zigzag_persistence_with_storage = Gudhi::zigzag_persistence::Filtered_zigzag_persistence_with_storage<>; + * ``` + * + * #### Construction with default values + * ``` + * Filtered_zigzag_persistence_with_storage zp; + * ``` + * + * #### Input of the zigzag sequence/filtration + * ``` + * // In all cases, it is important that the operations of insertions and removals are made **in the same order** + * // as in the zigzag filtration ones wants to compute the barcode from. + * + * // A face can be identified in the boundaries by any given numerical label, it is just important that the given + * // filtration values are monotonous (ie., either only increasing or only decreasing). + * + * //inserts vertex 2 at filtration value 0.1 -> birth at 0.1 of 0-cycle + * zp.insert_face(2, {}, 0, 0.1); + * //inserts vertex 4 at filtration value 0.1 -> birth at 0.1 of 0-cycle + * zp.insert_face(4, {}, 0, 0.1); + * //inserts edge 5 = (2,4) at filtration value 0.3 -> death at 0.3 -> outputs/stores (0, 0.1, 0.3) + * zp.insert_face(5, {2, 4}, 1, 0.3); + * //inserts vertex 3 at filtration value 0.4 -> birth at 0.4 of 0-cycle + * zp.insert_face(3, {}, 0, 0.4); + * //inserts edge 6 = (2,3) at filtration value 0.4 -> death at 0.4 of the cycle born at 0.4 -> outputs/stores nothing + * zp.insert_face(6, {2, 3}, 1, 0.4); + * //inserts edge 9 = (3,4) at filtration value 1.2 -> birth at 1.2 of 1-cycle + * zp.insert_face(9, {4, 3}, 1, 1.2); + * //removes edge 6 at filtration value 1.5 -> death at 1.5 -> outputs/stores (1, 1.2, 1.5) + * zp.remove_face(6, 1.5); + * //removes edge 5 at filtration value 2.0 -> birth at 2.0 of 0-cycle + * zp.remove_face(5, 2.0); + * ``` + * + * #### Finalizations + * ``` + * // The bars are stored within the class and where not output at all for now. + * + * //get all bars in a vector + * auto barcode = zp.get_persistence_diagram(); + * + * //do something with the vector, e.g., stream out content: + * for (auto& bar : barcode) { + * std::cout << bar << std::endl; + * } + * ``` * * @tparam FilteredZigzagOptions Structure following the @ref FilteredZigzagOptions concept. * Default value: @ref Default_filtered_zigzag_options. @@ -94,10 +150,10 @@ class Filtered_zigzag_persistence_with_storage * the filtration. To retrieve the current persistence diagram at any moment of the filtration, * use @ref get_persistence_diagram or @ref get_index_persistence_diagram. * - * @param preallocationSize Space for @p preallocationSize faces are reserved in the underlying structure. - * Theoretically, any values works therefore, but for better performances, it is better to be as close as possible - * to the maximal value of the number of faces stored at the same time. At a same time are stored faces which were - * inserted before that time but not removed until that time. Default value: 0. + * @param preallocationSize Reserves space for @p preallocationSize faces in the internal data structure. + * This is optional and just helps skip a few reallocations. The optimal value (no reallocation, no wasted space) is + * the number of faces in the biggest complex of the filtration. + * Default value: 0. * @param ignoreCyclesAboveDim Ignores cycles in dimension larger or equal in the final diagram. * If -1, no cycles are ignored. Default value: -1. */ @@ -229,7 +285,7 @@ class Filtered_zigzag_persistence_with_storage /** * @brief Returns the current persistence diagram. * - * @param shortestInterval Threshold. Every bar shorter than the given value will be ignored. Default value: 0. + * @param shortestInterval Threshold. Every bar shorter than the given value will not be returned. Default value: 0. * @param includeInfiniteBars If set to true, infinite bars are included in the diagram. Default value: true. * @return A vector of pairs of filtration values representing the persistence diagram. */ @@ -326,6 +382,64 @@ class Filtered_zigzag_persistence_with_storage * call @ref insert_face, @ref remove_face or @ref apply_identity for each step of the filtration in order of * the filtration. The bars of the diagram are retrieved via the given callback method every time * a pair with non-zero length is closed. To retrieve the open/infinite bars, use @ref get_current_infinite_intervals. + * + * ### Minimalistic example of usage + * + * #### Includes + * ``` + * #include + * ``` + * + * #### Useful aliases + * ``` + * using Filtered_zigzag_persistence = Gudhi::zigzag_persistence::Filtered_zigzag_persistence<>; + * using dimension_type = Filtered_zigzag_persistence::dimension_type; + * using filtration_value_type = Filtered_zigzag_persistence::filtration_value; + * ``` + * + * #### Construction with default values + * ``` + * //Filtered_zigzag_persistence(callback) with for example callback method as a anonymous lambda + * Filtered_zigzag_persistence zp([](dimension_type dim, filtration_value_type birth, filtration_value_type death) { + * std::cout << "[" << dim << "] " << birth << " - " << death << std::endl; + * }); + * ``` + * + * #### Input of the zigzag sequence/filtration + * ``` + * // In all cases, it is important that the operations of insertions and removals are made **in the same order** + * // as in the zigzag filtration ones wants to compute the barcode from. + * + * // A face can be identified in the boundaries by any given numerical label, it is just important that the given + * // filtration values are monotonous (ie., either only increasing or only decreasing). + * + * //inserts vertex 2 at filtration value 0.1 -> birth at 0.1 of 0-cycle + * zp.insert_face(2, {}, 0, 0.1); + * //inserts vertex 4 at filtration value 0.1 -> birth at 0.1 of 0-cycle + * zp.insert_face(4, {}, 0, 0.1); + * //inserts edge 5 = (2,4) at filtration value 0.3 -> death at 0.3 -> outputs/stores (0, 0.1, 0.3) + * zp.insert_face(5, {2, 4}, 1, 0.3); + * //inserts vertex 3 at filtration value 0.4 -> birth at 0.4 of 0-cycle + * zp.insert_face(3, {}, 0, 0.4); + * //inserts edge 6 = (2,3) at filtration value 0.4 -> death at 0.4 of the cycle born at 0.4 -> outputs/stores nothing + * zp.insert_face(6, {2, 3}, 1, 0.4); + * //inserts edge 9 = (3,4) at filtration value 1.2 -> birth at 1.2 of 1-cycle + * zp.insert_face(9, {4, 3}, 1, 1.2); + * //removes edge 6 at filtration value 1.5 -> death at 1.5 -> outputs/stores (1, 1.2, 1.5) + * zp.remove_face(6, 1.5); + * //removes edge 5 at filtration value 2.0 -> birth at 2.0 of 0-cycle + * zp.remove_face(5, 2.0); + * ``` + * + * #### Finalizations + * ``` + * // Only the closed bars where output so far, so the open/infinite bars still need to be retrieved. + * + * //in this example, outputs (0, 0.1) and (0, 2.0) + * zp.get_current_infinite_intervals([](dimension_type dim, filtration_value_type birth){ + * std::cout << "[" << dim << "] " << birth << " - inf" << std::endl; + * }); + * ``` * * @tparam FilteredZigzagOptions Structure following the @ref FilteredZigzagOptions concept. * Default value: @ref Default_filtered_zigzag_options. @@ -349,11 +463,11 @@ class Filtered_zigzag_persistence { * @param stream_interval Callback method to process the birth and death values of a persistence bar. * Has to take three arguments as input: first the dimension of the cycle, then the birth value of the cycle * and third the death value of the cycle. The values corresponds to the filtration values which were given at - * insertions or removals. - * @param preallocationSize Space for @p preallocationSize faces are reserved in the underlying structure. - * Theoretically, any values works therefore, but for better performances, it is better to be as close as possible - * to the maximal value of the number of faces stored at the same time. At a same time are stored faces which were - * inserted before that time but not removed until that time. Default value: 0. + * insertions or removals. Note that bars of length 0 will not be token into account. + * @param preallocationSize Reserves space for @p preallocationSize faces in the internal data structure. + * This is optional and just helps skip a few reallocations. The optimal value (no reallocation, no wasted space) is + * the number of faces in the biggest complex of the filtration. + * Default value: 0. * @tparam F Type of callback method. */ template @@ -413,7 +527,7 @@ class Filtered_zigzag_persistence { /** * @brief Updates the zigzag persistence diagram after the removal of the given face. - * + *preallocationSize * @param faceID ID representing the face to remove. Should be the same than the one used to insert it. * @param filtrationValue Filtration value associated to the removal. * Assumed to be always larger or equal to previously used filtration values or always smaller or equal than previous diff --git a/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h index 3bca4df194..7485424a81 100644 --- a/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h @@ -84,6 +84,64 @@ struct Default_zigzag_options { * a pair is closed. To retrieve the open pairs (corresponding to infinite bars), * use @ref get_current_infinite_intervals. * + * ### Minimalistic example of usage + * + * #### Includes + * ``` + * #include + * ``` + * + * #### Useful aliases + * ``` + * using Zigzag_persistence = Gudhi::zigzag_persistence::Zigzag_persistence<>; + * + * using dimension_type = Zigzag_persistence::dimension_type; + * using index = Zigzag_persistence::index; + * ``` + * + * #### Construction with default values + * ``` + * //Zigzag_persistence(callback) with for example callback method as a anonymous lambda + * Zigzag_persistence zp([](dimension_type dim, index birth, index death) { + * std::cout << "[" << dim << "] " << birth << " - " << death << std::endl; + * }); + * ``` + * + * #### Input of the zigzag sequence/filtration + * ``` + * // In all cases, it is important that the operations of insertions and removals are made **in the same order** + * // as in the zigzag filtration ones wants to compute the barcode from. + * + * // A face has to be identified in the boundaries by the operation number the face was inserted with in the sequence. + * + * //inserts vertex 0 -> birth at 0 of 0-cycle + * zp.insert_face({}, 0); + * //inserts vertex 1 -> birth at 1 of 0-cycle + * zp.insert_face({}, 0); + * //inserts edge 2 = (0,1) -> death at 2 -> outputs (0, 1, 2) + * zp.insert_face({0, 1}, 1); + * //inserts vertex 3 -> birth at 3 of 0-cycle + * zp.insert_face({}, 0); + * //inserts edge 4 = (0,3) -> death at 4 -> outputs (0, 3, 4) + * zp.insert_face({0, 3}, 1); + * //inserts edge 5 = (1,3) -> birth at 5 of 1-cycle + * zp.insert_face({1, 3}, 1); + * //removes edge 4 -> death at 6 -> outputs (1, 5, 6) + * zp.remove_face(4); + * //removes edge 2 -> birth at 7 of 0-cycle + * zp.remove_face(2); + * ``` + * + * #### Finalizations + * ``` + * // Only the closed bars where output so far, so the open/infinite bars still need to be retrieved. + * + * //in this example, outputs (0, 0) and (0, 7) + * zp.get_current_infinite_intervals([](dimension_type dim, index birth){ + * std::cout << "[" << dim << "] " << birth << " - inf" << std::endl; + * }); + * ``` + * * @ingroup zigzag_persistence * * @tparam ZigzagOptions Structure following the @ref ZigzagOptions concept. Default value: @ref Default_zigzag_options. @@ -199,10 +257,10 @@ class Zigzag_persistence * as input: first the dimension of the cycle, then the birth index of the cycle and third the death index of the * cycle. An index always corresponds to the arrow number the event occurred (one call to @ref insert_face, * @ref remove_face or @ref apply_identity is equal to one arrow and increases the arrow count by one). - * @param preallocationSize Space for @p preallocationSize faces are reserved in the underlying structure. - * Theoretically, any values works therefore, but for better performances, it is better to be as close as possible - * to the maximal value of the number of faces stored at the same time. At a same time are stored faces which were - * inserted before that time but not removed until that time. Default value: 0. + * @param preallocationSize Reserves space for @p preallocationSize faces in the internal data structure. + * This is optional and just helps skip a few reallocations. The optimal value (no reallocation, no wasted space) is + * the number of faces in the biggest complex of the filtration. + * Default value: 0. */ Zigzag_persistence(std::function stream_interval, unsigned int preallocationSize = 0) @@ -393,7 +451,6 @@ class Zigzag_persistence * @brief Removes the given face by pushing up the matrix the corresponding column and erasing it. * * @param faceID Internal ID of the face to remove. - * @param dim Dimension of the face to remove. */ void _process_backward_arrow(index faceID) { // column whose key is the one of the removed face @@ -427,14 +484,7 @@ class Zigzag_persistence } else { // in H -> paired with c_g, that now belongs to F now // maintain the <=b order birthOrdering_.add_birth_backward(numArrow_); - if constexpr (erase_birth_history) { - births_.try_emplace(col.get_paired_chain_index(), numArrow_); - } else { - auto res = births_.try_emplace(col.get_paired_chain_index(), numArrow_); - if (!res.second) { - res.first->second = numArrow_; - } - } + births_[col.get_paired_chain_index()] = numArrow_; } // cannot be in G as the removed face is maximal diff --git a/src/common/doc/examples.h b/src/common/doc/examples.h index ef146fd44c..dd2464afa8 100644 --- a/src/common/doc/examples.h +++ b/src/common/doc/examples.h @@ -130,7 +130,10 @@ * @example example_one_skeleton_rips_from_points.cpp * @example example_rips_complex_from_off_file.cpp * \section Zigzag_persistence_example_section Zigzag_persistence - * @example example_simple_zigzag_filtration.cpp + * @example example_usage_zigzag_persistence.cpp + * @example example_usage_filtered_zigzag_persistence.cpp + * @example example_usage_filtered_zigzag_persistence_with_storage.cpp + * @example example_zigzag_filtration_as_input_loop.cpp * @example example_zzfiltration_from_file.cpp * \section Persistence_matrix_example_section Persistence_matrix * @example representative_cycles_from_matrix.cpp From 564f751a96fd324f8441afb333981c0cec477be8 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Mon, 22 Jul 2024 18:13:05 +0200 Subject: [PATCH 44/51] doc --- ...mple_usage_filtered_zigzag_persistence.cpp | 57 +++++++++++++++++++ ...ltered_zigzag_persistence_with_storage.cpp | 55 ++++++++++++++++++ .../example_usage_zigzag_persistence.cpp | 55 ++++++++++++++++++ 3 files changed, 167 insertions(+) create mode 100644 src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence.cpp create mode 100644 src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence_with_storage.cpp create mode 100644 src/Zigzag_persistence/example/example_usage_zigzag_persistence.cpp diff --git a/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence.cpp b/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence.cpp new file mode 100644 index 0000000000..1e6c43ea84 --- /dev/null +++ b/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence.cpp @@ -0,0 +1,57 @@ +/* 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): Hannah Schreiber + * + * Copyright (C) 2024 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#include + +#include + +using Zigzag_persistence = Gudhi::zigzag_persistence::Filtered_zigzag_persistence<>; +using dimension_type = Zigzag_persistence::dimension_type; +using filtration_value_type = Zigzag_persistence::filtration_value; + +int main(int argc, char* const argv[]) { + std::clog << "********* Minimalistic example of usage of the Filtered_zigzag_persistence class ********" << std::endl; + + // Filtered_zigzag_persistence(callback) with for example callback method as a anonymous lambda + Zigzag_persistence zp([](dimension_type dim, filtration_value_type birth, filtration_value_type death) { + std::cout << "[" << dim << "] " << birth << " - " << death << std::endl; + }); + + // It is important that the operations of insertions and removals are made **in the same order** as in the zigzag + // filtration ones wants to compute the barcode from. + // A face can be identified in the boundaries by any given numerical label, it is just important that the given + // filtration values are monotonous (ie., either only increasing or only decreasing). + + // inserts vertex 2 at filtration value 0.1 -> birth at 0.1 of 0-cycle + zp.insert_face(2, {}, 0, 0.1); + // inserts vertex 4 at filtration value 0.1 -> birth at 0.1 of 0-cycle + zp.insert_face(4, {}, 0, 0.1); + // inserts edge 5 = (2,4) at filtration value 0.3 -> death at 0.3 -> outputs/stores (0, 0.1, 0.3) + zp.insert_face(5, {2, 4}, 1, 0.3); + // inserts vertex 3 at filtration value 0.4 -> birth at 0.4 of 0-cycle + zp.insert_face(3, {}, 0, 0.4); + // inserts edge 6 = (2,3) at filtration value 0.4 -> death at 0.4 of the cycle born at 0.4 -> outputs/stores nothing + zp.insert_face(6, {2, 3}, 1, 0.4); + // inserts edge 9 = (3,4) at filtration value 1.2 -> birth at 1.2 of 1-cycle + zp.insert_face(9, {4, 3}, 1, 1.2); + // removes edge 6 at filtration value 1.5 -> death at 1.5 -> outputs/stores (1, 1.2, 1.5) + zp.remove_face(6, 1.5); + // removes edge 5 at filtration value 2.0 -> birth at 2.0 of 0-cycle + zp.remove_face(5, 2.0); + + // Only the closed bars where output so far, so the open/infinite bars still need to be retrieved. + + // in this example, outputs (0, 0.1) and (0, 2.0) + zp.get_current_infinite_intervals([](dimension_type dim, filtration_value_type birth) { + std::cout << "[" << dim << "] " << birth << " - inf" << std::endl; + }); + + return 0; +} diff --git a/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence_with_storage.cpp b/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence_with_storage.cpp new file mode 100644 index 0000000000..8c897cc087 --- /dev/null +++ b/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence_with_storage.cpp @@ -0,0 +1,55 @@ +/* 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): Hannah Schreiber + * + * Copyright (C) 2024 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#include + +#include + +using Zigzag_persistence = Gudhi::zigzag_persistence::Filtered_zigzag_persistence_with_storage<>; + +int main(int argc, char* const argv[]) { + std::clog << "** Minimalistic example of usage of the Filtered_zigzag_persistence_with_storage class **" << std::endl; + + Zigzag_persistence zp; + + // It is important that the operations of insertions and removals are made **in the same order** as in the zigzag + // filtration ones wants to compute the barcode from. + // A face can be identified in the boundaries by any given numerical label, it is just important that the given + // filtration values are monotonous (ie., either only increasing or only decreasing). + + // inserts vertex 2 at filtration value 0.1 -> birth at 0.1 of 0-cycle + zp.insert_face(2, {}, 0, 0.1); + // inserts vertex 4 at filtration value 0.1 -> birth at 0.1 of 0-cycle + zp.insert_face(4, {}, 0, 0.1); + // inserts edge 5 = (2,4) at filtration value 0.3 -> death at 0.3 -> outputs/stores (0, 0.1, 0.3) + zp.insert_face(5, {2, 4}, 1, 0.3); + // inserts vertex 3 at filtration value 0.4 -> birth at 0.4 of 0-cycle + zp.insert_face(3, {}, 0, 0.4); + // inserts edge 6 = (2,3) at filtration value 0.4 -> death at 0.4 of the cycle born at 0.4 -> outputs/stores nothing + zp.insert_face(6, {2, 3}, 1, 0.4); + // inserts edge 9 = (3,4) at filtration value 1.2 -> birth at 1.2 of 1-cycle + zp.insert_face(9, {4, 3}, 1, 1.2); + // removes edge 6 at filtration value 1.5 -> death at 1.5 -> outputs/stores (1, 1.2, 1.5) + zp.remove_face(6, 1.5); + // removes edge 5 at filtration value 2.0 -> birth at 2.0 of 0-cycle + zp.remove_face(5, 2.0); + + // The bars are stored within the class and where not output at all for now. + + // get all bars in a vector + auto barcode = zp.get_persistence_diagram(); + + // do something with the vector, e.g., stream out content: + for (auto& bar : barcode) { + std::cout << bar << std::endl; + } + + return 0; +} diff --git a/src/Zigzag_persistence/example/example_usage_zigzag_persistence.cpp b/src/Zigzag_persistence/example/example_usage_zigzag_persistence.cpp new file mode 100644 index 0000000000..a024cfe0af --- /dev/null +++ b/src/Zigzag_persistence/example/example_usage_zigzag_persistence.cpp @@ -0,0 +1,55 @@ +/* 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): Hannah Schreiber + * + * Copyright (C) 2024 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#include + +#include + +using Zigzag_persistence = Gudhi::zigzag_persistence::Zigzag_persistence<>; +using dimension_type = Zigzag_persistence::dimension_type; +using index_type = Zigzag_persistence::index; + +int main(int argc, char* const argv[]) { + std::clog << "************* Minimalistic example of usage of the Zigzag_persistence class *************" << std::endl; + + // Zigzag_persistence(callback) with for example callback method as a anonymous lambda + Zigzag_persistence zp([](dimension_type dim, index_type birth, index_type death) { + std::cout << "[" << dim << "] " << birth << " - " << death << std::endl; + }); + + // It is important that the operations of insertions and removals are made **in the same order** as in the zigzag + // filtration ones wants to compute the barcode from. + // A face has to be identified in the boundaries by the operation number the face was inserted with in the sequence. + + // inserts vertex 0 -> birth at 0 of 0-cycle + zp.insert_face({}, 0); + // inserts vertex 1 -> birth at 1 of 0-cycle + zp.insert_face({}, 0); + // inserts edge 2 = (0,1) -> death at 2 -> outputs (0, 1, 2) + zp.insert_face({0, 1}, 1); + // inserts vertex 3 -> birth at 3 of 0-cycle + zp.insert_face({}, 0); + // inserts edge 4 = (0,3) -> death at 4 -> outputs (0, 3, 4) + zp.insert_face({0, 3}, 1); + // inserts edge 5 = (1,3) -> birth at 5 of 1-cycle + zp.insert_face({1, 3}, 1); + // removes edge 4 -> death at 6 -> outputs (1, 5, 6) + zp.remove_face(4); + // removes edge 2 -> birth at 7 of 0-cycle + zp.remove_face(2); + + // Only the closed bars where output so far, so the open/infinite bars still need to be retrieved. + + // in this example, outputs (0, 0) and (0, 7) + zp.get_current_infinite_intervals( + [](dimension_type dim, index_type birth) { std::cout << "[" << dim << "] " << birth << " - inf" << std::endl; }); + + return 0; +} From bd6d9b565bd61e5414f964e245c02e8ab731933b Mon Sep 17 00:00:00 2001 From: hschreiber Date: Tue, 23 Jul 2024 13:54:29 +0200 Subject: [PATCH 45/51] doc --- .../doc/Intro_zigzag_persistence.h | 15 ++++++++++++--- .../example_usage_filtered_zigzag_persistence.cpp | 4 ++-- ...e_filtered_zigzag_persistence_with_storage.cpp | 2 +- .../example/example_usage_zigzag_persistence.cpp | 6 +++--- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h index 9c5537b230..0e7ddac996 100644 --- a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h +++ b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h @@ -60,17 +60,26 @@ namespace zigzag_persistence { * \li \gudhi_example_link{Zigzag_persistence,example_usage_zigzag_persistence.cpp} - A simple example to showcase how * to use the @ref Zigzag_persistence class to compute a barcode. *
- * @include example_usage_zigzag_persistence.cpp + * @dontinclude example_usage_zigzag_persistence.cpp + * @skip #include + * @until return 0; + * @skipline } *
* \li \gudhi_example_link{Zigzag_persistence,example_usage_filtered_zigzag_persistence.cpp} - A simple example to * showcase how to use the @ref Filtered_zigzag_persistence class to compute a barcode. *
- * @include example_usage_filtered_zigzag_persistence.cpp + * @dontinclude example_usage_filtered_zigzag_persistence.cpp + * @skip #include + * @until return 0; + * @skipline } *
* \li \gudhi_example_link{Zigzag_persistence,example_usage_filtered_zigzag_persistence_with_storage.cpp} - A simple * example to showcase how to use the @ref Filtered_zigzag_persistence_with_storage class to compute a barcode. *
- * @include example_usage_filtered_zigzag_persistence_with_storage.cpp + * @dontinclude example_usage_filtered_zigzag_persistence_with_storage.cpp + * @skip #include + * @until return 0; + * @skipline } *
* * \subsection zzexamples More elaborate examples diff --git a/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence.cpp b/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence.cpp index 1e6c43ea84..e15d8e8c08 100644 --- a/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence.cpp +++ b/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence.cpp @@ -16,7 +16,7 @@ using Zigzag_persistence = Gudhi::zigzag_persistence::Filtered_zigzag_persistenc using dimension_type = Zigzag_persistence::dimension_type; using filtration_value_type = Zigzag_persistence::filtration_value; -int main(int argc, char* const argv[]) { +int main() { std::clog << "********* Minimalistic example of usage of the Filtered_zigzag_persistence class ********" << std::endl; // Filtered_zigzag_persistence(callback) with for example callback method as a anonymous lambda @@ -48,7 +48,7 @@ int main(int argc, char* const argv[]) { // Only the closed bars where output so far, so the open/infinite bars still need to be retrieved. - // in this example, outputs (0, 0.1) and (0, 2.0) + // in this example, computes (0, 0.1) and (0, 2.0) zp.get_current_infinite_intervals([](dimension_type dim, filtration_value_type birth) { std::cout << "[" << dim << "] " << birth << " - inf" << std::endl; }); diff --git a/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence_with_storage.cpp b/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence_with_storage.cpp index 8c897cc087..5136d5eb2a 100644 --- a/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence_with_storage.cpp +++ b/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence_with_storage.cpp @@ -14,7 +14,7 @@ using Zigzag_persistence = Gudhi::zigzag_persistence::Filtered_zigzag_persistence_with_storage<>; -int main(int argc, char* const argv[]) { +int main() { std::clog << "** Minimalistic example of usage of the Filtered_zigzag_persistence_with_storage class **" << std::endl; Zigzag_persistence zp; diff --git a/src/Zigzag_persistence/example/example_usage_zigzag_persistence.cpp b/src/Zigzag_persistence/example/example_usage_zigzag_persistence.cpp index a024cfe0af..26946e1ece 100644 --- a/src/Zigzag_persistence/example/example_usage_zigzag_persistence.cpp +++ b/src/Zigzag_persistence/example/example_usage_zigzag_persistence.cpp @@ -16,7 +16,7 @@ using Zigzag_persistence = Gudhi::zigzag_persistence::Zigzag_persistence<>; using dimension_type = Zigzag_persistence::dimension_type; using index_type = Zigzag_persistence::index; -int main(int argc, char* const argv[]) { +int main() { std::clog << "************* Minimalistic example of usage of the Zigzag_persistence class *************" << std::endl; // Zigzag_persistence(callback) with for example callback method as a anonymous lambda @@ -45,9 +45,9 @@ int main(int argc, char* const argv[]) { // removes edge 2 -> birth at 7 of 0-cycle zp.remove_face(2); - // Only the closed bars where output so far, so the open/infinite bars still need to be retrieved. + // Only the closed bars were output so far, so the open/infinite bars still need to be retrieved. - // in this example, outputs (0, 0) and (0, 7) + // in this example, computes (0, 0) and (0, 7) zp.get_current_infinite_intervals( [](dimension_type dim, index_type birth) { std::cout << "[" << dim << "] " << birth << " - inf" << std::endl; }); From 4f40d8bf25c61798d53ae213d90b4d3aa76cd211 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Tue, 17 Sep 2024 17:42:08 +0200 Subject: [PATCH 46/51] upstream merge --- src/Zigzag_persistence/include/gudhi/zigzag_persistence.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h index 7485424a81..7d6d20c6d8 100644 --- a/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h @@ -33,7 +33,7 @@ #include #endif -#include +#include namespace Gudhi { namespace zigzag_persistence { @@ -163,7 +163,7 @@ class Zigzag_persistence #endif using Matrix_options = Zigzag_matrix_options; /**< Matrix options. */ using Matrix_type = Gudhi::persistence_matrix::Matrix; /**< Matrix. */ - using matrix_index = typename Matrix_type::index; /**< Matrix indexation type. */ + using matrix_index = typename Matrix_type::Index; /**< Matrix indexation type. */ /** \brief Maintains the birth ordering \f$\leq_b\f$. * From 6a0fad29a151774ebb4e91ac88784867a8669525 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Tue, 17 Sep 2024 18:04:21 +0200 Subject: [PATCH 47/51] renaming after Gudhi convention --- .../concept/ZigzagOptions.h | 12 +- ...mple_usage_filtered_zigzag_persistence.cpp | 8 +- .../example_usage_zigzag_persistence.cpp | 8 +- ...xample_zigzag_filtration_as_input_loop.cpp | 12 +- .../example_zzfiltration_from_file.cpp | 16 +-- .../gudhi/filtered_zigzag_persistence.h | 114 +++++++++--------- .../include/gudhi/zigzag_persistence.h | 88 +++++++------- .../test/test_filtered_zigzag_persistence.cpp | 56 ++++----- .../test/test_zigzag_persistence.cpp | 16 +-- 9 files changed, 165 insertions(+), 165 deletions(-) diff --git a/src/Zigzag_persistence/concept/ZigzagOptions.h b/src/Zigzag_persistence/concept/ZigzagOptions.h index cc83a8285e..79ee25995a 100644 --- a/src/Zigzag_persistence/concept/ZigzagOptions.h +++ b/src/Zigzag_persistence/concept/ZigzagOptions.h @@ -28,23 +28,23 @@ struct FilteredZigzagOptions { /** * @brief Numerical type for the face IDs used internally and other indexations. It must be signed. */ - using internal_key = unspecified; + using Internal_key = unspecified; /** * @brief Type for the face IDs used at insertion and in the boundaries given as argument. * Has to be usable as key in a hashtable, so "hashable" and comparable. */ - using face_key = unspecified; + using Face_key = unspecified; /** * @brief Type for filtration values. */ - using filtration_value = unspecified; + using Filtration_value = unspecified; /** * @brief Type for the dimension values. */ - using dimension_type = unspecified; + using Dimension = unspecified; /** * @brief Column type used by the internal matrix. @@ -61,12 +61,12 @@ struct ZigzagOptions { /** * @brief Numerical type for the face IDs used internally and other indexations. It must be signed. */ - using internal_key = unspecified; + using Internal_key = unspecified; /** * @brief Type for the dimension values. */ - using dimension_type = unspecified; + using Dimension = unspecified; /** * @brief Column type used by the internal matrix. diff --git a/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence.cpp b/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence.cpp index e15d8e8c08..50bc50f894 100644 --- a/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence.cpp +++ b/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence.cpp @@ -13,14 +13,14 @@ #include using Zigzag_persistence = Gudhi::zigzag_persistence::Filtered_zigzag_persistence<>; -using dimension_type = Zigzag_persistence::dimension_type; -using filtration_value_type = Zigzag_persistence::filtration_value; +using Dimension = Zigzag_persistence::Dimension; +using Filtration_value = Zigzag_persistence::Filtration_value; int main() { std::clog << "********* Minimalistic example of usage of the Filtered_zigzag_persistence class ********" << std::endl; // Filtered_zigzag_persistence(callback) with for example callback method as a anonymous lambda - Zigzag_persistence zp([](dimension_type dim, filtration_value_type birth, filtration_value_type death) { + Zigzag_persistence zp([](Dimension dim, Filtration_value birth, Filtration_value death) { std::cout << "[" << dim << "] " << birth << " - " << death << std::endl; }); @@ -49,7 +49,7 @@ int main() { // Only the closed bars where output so far, so the open/infinite bars still need to be retrieved. // in this example, computes (0, 0.1) and (0, 2.0) - zp.get_current_infinite_intervals([](dimension_type dim, filtration_value_type birth) { + zp.get_current_infinite_intervals([](Dimension dim, Filtration_value birth) { std::cout << "[" << dim << "] " << birth << " - inf" << std::endl; }); diff --git a/src/Zigzag_persistence/example/example_usage_zigzag_persistence.cpp b/src/Zigzag_persistence/example/example_usage_zigzag_persistence.cpp index 26946e1ece..e987b90ef4 100644 --- a/src/Zigzag_persistence/example/example_usage_zigzag_persistence.cpp +++ b/src/Zigzag_persistence/example/example_usage_zigzag_persistence.cpp @@ -13,14 +13,14 @@ #include using Zigzag_persistence = Gudhi::zigzag_persistence::Zigzag_persistence<>; -using dimension_type = Zigzag_persistence::dimension_type; -using index_type = Zigzag_persistence::index; +using Dimension = Zigzag_persistence::Dimension; +using Index = Zigzag_persistence::Index; int main() { std::clog << "************* Minimalistic example of usage of the Zigzag_persistence class *************" << std::endl; // Zigzag_persistence(callback) with for example callback method as a anonymous lambda - Zigzag_persistence zp([](dimension_type dim, index_type birth, index_type death) { + Zigzag_persistence zp([](Dimension dim, Index birth, Index death) { std::cout << "[" << dim << "] " << birth << " - " << death << std::endl; }); @@ -49,7 +49,7 @@ int main() { // in this example, computes (0, 0) and (0, 7) zp.get_current_infinite_intervals( - [](dimension_type dim, index_type birth) { std::cout << "[" << dim << "] " << birth << " - inf" << std::endl; }); + [](Dimension dim, Index birth) { std::cout << "[" << dim << "] " << birth << " - inf" << std::endl; }); return 0; } diff --git a/src/Zigzag_persistence/example/example_zigzag_filtration_as_input_loop.cpp b/src/Zigzag_persistence/example/example_zigzag_filtration_as_input_loop.cpp index 17dcdb816f..046e599b88 100644 --- a/src/Zigzag_persistence/example/example_zigzag_filtration_as_input_loop.cpp +++ b/src/Zigzag_persistence/example/example_zigzag_filtration_as_input_loop.cpp @@ -14,8 +14,8 @@ #include using ZP = Gudhi::zigzag_persistence::Filtered_zigzag_persistence_with_storage<>; -using face_handle = ZP::face_key; -using filtration_value = ZP::filtration_value; +using Face_handle = ZP::Face_key; +using Filtration_value = ZP::Filtration_value; using Interval_filtration = ZP::Filtration_value_interval; void print_barcode(ZP& zp) { @@ -50,7 +50,7 @@ void print_indices(ZP& zp) { } } -std::vector > get_boundaries() { +std::vector > get_boundaries() { return {{}, {}, {}, @@ -82,7 +82,7 @@ std::vector > get_boundaries() { {27}}; // remove } -std::vector get_filtration_values() { +std::vector get_filtration_values() { return {0, 0, 0, 1, 1, 1, 2, 2, 2, @@ -114,8 +114,8 @@ int main(int argc, char* const argv[]) { ZP zp; - std::vector > simplices = get_boundaries(); - std::vector fils = get_filtration_values(); + std::vector > simplices = get_boundaries(); + std::vector fils = get_filtration_values(); std::vector dirs = get_directions(); for (unsigned int i = 0; i < simplices.size(); ++i) { diff --git a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp index 5f85f2a900..333194be6f 100644 --- a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp +++ b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp @@ -16,16 +16,16 @@ #include using ZP = Gudhi::zigzag_persistence::Filtered_zigzag_persistence<>; -using id_handle = ZP::face_key; -using filtration_value = ZP::filtration_value; -using dimension_type = ZP::dimension_type; +using ID_handle = ZP::Face_key; +using Filtration_value = ZP::Filtration_value; +using Dimension = ZP::Dimension; enum lineType : int { INCLUSION, REMOVAL, COMMENT }; -lineType read_operation(std::string& line, std::vector& faces, double& timestamp) { +lineType read_operation(std::string& line, std::vector& faces, double& timestamp) { lineType type; faces.clear(); - id_handle num; + ID_handle num; size_t current = line.find_first_not_of(' ', 0); if (current == std::string::npos) return COMMENT; @@ -74,14 +74,14 @@ int main(int argc, char* const argv[]) { std::ifstream file(argv[1]); //std::cout could be replaced by any other output stream - ZP zp([](dimension_type dim, filtration_value birth, filtration_value death) { + ZP zp([](Dimension dim, Filtration_value birth, Filtration_value death) { std::cout << "[" << dim << "] "; std::cout << birth << " - " << death; std::cout << std::endl; }); if (file.is_open()) { - std::vector data; + std::vector data; unsigned int id = 0; double timestamp; lineType type; @@ -115,7 +115,7 @@ int main(int argc, char* const argv[]) { //retrieve infinite bars remaining at the end //again std::cout could be replaced by any other output stream - zp.get_current_infinite_intervals([](dimension_type dim, filtration_value birth) { + zp.get_current_infinite_intervals([](Dimension dim, Filtration_value birth) { std::cout << "[" << dim << "] "; std::cout << birth << " - inf"; std::cout << std::endl; diff --git a/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h index f820ae1f3b..dabaf31cf5 100644 --- a/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h @@ -42,10 +42,10 @@ namespace zigzag_persistence { * @brief Default options for @ref Filtered_zigzag_persistence_with_storage and @ref Filtered_zigzag_persistence. */ struct Default_filtered_zigzag_options { - using internal_key = int; /**< Face ID used internally, must be signed. */ - using face_key = int; /**< Face ID used in the given boundaries. */ - using filtration_value = double; /**< Filtration value type. */ - using dimension_type = int; /**< Dimension value type. */ + using Internal_key = int; /**< Face ID used internally, must be signed. */ + using Face_key = int; /**< Face ID used in the given boundaries. */ + using Filtration_value = double; /**< Filtration value type. */ + using Dimension = int; /**< Dimension value type. */ /** * @brief Column type use by the internal matrix. */ @@ -129,19 +129,19 @@ class Filtered_zigzag_persistence_with_storage { public: using Options = FilteredZigzagOptions; /**< Zigzag options. */ - using internal_key = typename Options::internal_key; /**< Key and index type, has to be signed. */ - using face_key = typename Options::face_key; /**< Face ID type from external inputs. */ - using filtration_value = typename Options::filtration_value; /**< Type for filtration values. */ - using dimension_type = typename Options::dimension_type; /**< Type for dimension values. */ + using Internal_key = typename Options::Internal_key; /**< Key and index type, has to be signed. */ + using Face_key = typename Options::Face_key; /**< Face ID type from external inputs. */ + using Filtration_value = typename Options::Filtration_value; /**< Type for filtration values. */ + using Dimension = typename Options::Dimension; /**< Type for dimension values. */ /** * @brief Persistence index interval type. */ - using Index_interval = Gudhi::persistence_matrix::Persistence_interval; + using Index_interval = Gudhi::persistence_matrix::Persistence_interval; /** * @brief Persistence filtration interval type. */ - using Filtration_value_interval = Gudhi::persistence_matrix::Persistence_interval; + using Filtration_value_interval = Gudhi::persistence_matrix::Persistence_interval; /** * @brief Constructor. @@ -161,9 +161,9 @@ class Filtered_zigzag_persistence_with_storage : dimMax_(ignoreCyclesAboveDim), persistenceDiagram_(), numArrow_(-1), - previousFiltrationValue_(std::numeric_limits::infinity()), + previousFiltrationValue_(std::numeric_limits::infinity()), pers_( - [&](dimension_type dim, internal_key birth, internal_key death) { + [&](Dimension dim, Internal_key birth, Internal_key death) { if (dimMax_ == -1 || (dimMax_ != -1 && dim < dimMax_)) { // don't record intervals over max dim persistenceDiagram_.emplace_back(birth, death, dim); } @@ -184,11 +184,11 @@ class Filtered_zigzag_persistence_with_storage * values, ie. the changes are monotonous. * @return Number of the operation. */ - template > - internal_key insert_face(face_key faceID, + template > + Internal_key insert_face(Face_key faceID, const BoundaryRange& boundary, - dimension_type dimension, - filtration_value filtrationValue) + Dimension dimension, + Filtration_value filtrationValue) { if (dimMax_ != -1 && dimension > dimMax_) { return apply_identity(); @@ -203,7 +203,7 @@ class Filtered_zigzag_persistence_with_storage GUDHI_CHECK(res.second, "Zigzag_persistence::insert_face - face already in the complex"); // Compute the keys of the faces of the boundary. - std::set translatedBoundary; // set maintains the natural order on indices + std::set translatedBoundary; // set maintains the natural order on indices for (auto b : boundary) { translatedBoundary.insert(handleToKey_.at(b)); // TODO: add possibilities of coefficients } @@ -224,7 +224,7 @@ class Filtered_zigzag_persistence_with_storage * values, ie. the changes are monotonous. * @return Number of the operation. */ - internal_key remove_face(face_key faceID, filtration_value filtrationValue) { + Internal_key remove_face(Face_key faceID, Filtration_value filtrationValue) { auto it = handleToKey_.find(faceID); if (it == handleToKey_.end()) { @@ -247,7 +247,7 @@ class Filtered_zigzag_persistence_with_storage * to avoid useless computation. * @return Number of the operation. */ - internal_key apply_identity() { + Internal_key apply_identity() { ++numArrow_; pers_.apply_identity(); return numArrow_; @@ -267,15 +267,15 @@ class Filtered_zigzag_persistence_with_storage * by @ref get_index_persistence_diagram. * * @param idx Birth or death index - * @return filtration_value Filtration value associated to @p idx. + * @return Filtration_value Filtration value associated to @p idx. */ - filtration_value get_filtration_value_from_index(internal_key idx) { + Filtration_value get_filtration_value_from_index(Internal_key idx) { // lower_bound(x) returns leftmost y s.t. x <= y auto itBirth = std::lower_bound(filtrationValues_.begin(), filtrationValues_.end(), idx, - [](std::pair p, internal_key k) { return p.first < k; }); + [](std::pair p, Internal_key k) { return p.first < k; }); if (itBirth == filtrationValues_.end() || itBirth->first > idx) { --itBirth; } @@ -289,7 +289,7 @@ class Filtered_zigzag_persistence_with_storage * @param includeInfiniteBars If set to true, infinite bars are included in the diagram. Default value: true. * @return A vector of pairs of filtration values representing the persistence diagram. */ - std::vector get_persistence_diagram(filtration_value shortestInterval = 0., + std::vector get_persistence_diagram(Filtration_value shortestInterval = 0., bool includeInfiniteBars = true) { std::vector diag = _get_persistence_diagram(shortestInterval); @@ -301,17 +301,17 @@ class Filtered_zigzag_persistence_with_storage } private: - std::unordered_map handleToKey_; /**< Map from input keys to internal keys. */ - dimension_type dimMax_; /**< Maximal dimension of a bar to record. */ + std::unordered_map handleToKey_; /**< Map from input keys to internal keys. */ + Dimension dimMax_; /**< Maximal dimension of a bar to record. */ std::vector persistenceDiagram_; /**< Stores current closed persistence intervals. */ - internal_key numArrow_; /**< Current arrow number. */ - filtration_value previousFiltrationValue_; /**< Filtration value of the previous arrow. */ + Internal_key numArrow_; /**< Current arrow number. */ + Filtration_value previousFiltrationValue_; /**< Filtration value of the previous arrow. */ /** * @brief filtrationValues_ stores consecutive pairs (i,f) , (j,f') with f != f', * meaning that all inserted faces with key in [i;j-1] have filtration value f, * i is the smallest face index whose face has filtration value f. */ - std::vector > filtrationValues_; + std::vector > filtrationValues_; Zigzag_persistence pers_; /**< Class computing the pairs. */ /** @@ -320,7 +320,7 @@ class Filtered_zigzag_persistence_with_storage * * @param filtrationValue Filtration value to store. */ - void _store_filtration_value(filtration_value filtrationValue) { + void _store_filtration_value(Filtration_value filtrationValue) { if (filtrationValue != previousFiltrationValue_) // check whether the filt value has changed { // consecutive pairs (i,f), (j,f') mean faces of index k in [i,j-1] have filtration value f @@ -335,18 +335,18 @@ class Filtered_zigzag_persistence_with_storage * @param shortestInterval Intervals shorter than the given value are ignored. * @return Vector of intervals. */ - std::vector _get_persistence_diagram(filtration_value shortestInterval) { + std::vector _get_persistence_diagram(Filtration_value shortestInterval) { std::vector diag; diag.reserve(persistenceDiagram_.size()); // std::stable_sort(filtrationValues_.begin(), filtrationValues_.end(), - // [](std::pair p1, std::pair p2) { + // [](std::pair p1, std::pair p2) { // return p1.first < p2.first; // }); for (auto bar : persistenceDiagram_) { - filtration_value birth = get_filtration_value_from_index(bar.birth); - filtration_value death = get_filtration_value_from_index(bar.death); + Filtration_value birth = get_filtration_value_from_index(bar.birth); + Filtration_value death = get_filtration_value_from_index(bar.death); if (birth > death) { std::swap(birth, death); } @@ -365,7 +365,7 @@ class Filtered_zigzag_persistence_with_storage * @param diag Reference to vector where to store the infinite bars. */ void _retrieve_infinite_bars(std::vector& diag) { - auto stream_infinite_interval = [&](dimension_type dim, internal_key birthIndex) { + auto stream_infinite_interval = [&](Dimension dim, Internal_key birthIndex) { if (dimMax_ == -1 || (dimMax_ != -1 && dim < dimMax_)) diag.emplace_back(get_filtration_value_from_index(birthIndex), Filtration_value_interval::inf, dim); }; @@ -393,14 +393,14 @@ class Filtered_zigzag_persistence_with_storage * #### Useful aliases * ``` * using Filtered_zigzag_persistence = Gudhi::zigzag_persistence::Filtered_zigzag_persistence<>; - * using dimension_type = Filtered_zigzag_persistence::dimension_type; - * using filtration_value_type = Filtered_zigzag_persistence::filtration_value; + * using Dimension = Filtered_zigzag_persistence::Dimension; + * using filtration_value_type = Filtered_zigzag_persistence::Filtration_value; * ``` * * #### Construction with default values * ``` * //Filtered_zigzag_persistence(callback) with for example callback method as a anonymous lambda - * Filtered_zigzag_persistence zp([](dimension_type dim, filtration_value_type birth, filtration_value_type death) { + * Filtered_zigzag_persistence zp([](Dimension dim, filtration_value_type birth, filtration_value_type death) { * std::cout << "[" << dim << "] " << birth << " - " << death << std::endl; * }); * ``` @@ -436,7 +436,7 @@ class Filtered_zigzag_persistence_with_storage * // Only the closed bars where output so far, so the open/infinite bars still need to be retrieved. * * //in this example, outputs (0, 0.1) and (0, 2.0) - * zp.get_current_infinite_intervals([](dimension_type dim, filtration_value_type birth){ + * zp.get_current_infinite_intervals([](Dimension dim, filtration_value_type birth){ * std::cout << "[" << dim << "] " << birth << " - inf" << std::endl; * }); * ``` @@ -448,10 +448,10 @@ template class Filtered_zigzag_persistence { public: using Options = FilteredZigzagOptions; /**< Zigzag options. */ - using internal_key = typename Options::internal_key; /**< Key and index type, has to be signed. */ - using face_key = typename Options::face_key; /**< Face ID type from external inputs. */ - using filtration_value = typename Options::filtration_value; /**< Type for filtration values. */ - using dimension_type = typename Options::dimension_type; /**< Type for dimension values. */ + using Internal_key = typename Options::Internal_key; /**< Key and index type, has to be signed. */ + using Face_key = typename Options::Face_key; /**< Face ID type from external inputs. */ + using Filtration_value = typename Options::Filtration_value; /**< Type for filtration values. */ + using Dimension = typename Options::Dimension; /**< Type for dimension values. */ /** * @brief Constructor. @@ -476,9 +476,9 @@ class Filtered_zigzag_persistence { numArrow_(-1), keyToFiltrationValue_(preallocationSize), pers_( - [&, stream_interval = std::forward(stream_interval)](dimension_type dim, - internal_key birth, - internal_key death) { + [&, stream_interval = std::forward(stream_interval)](Dimension dim, + Internal_key birth, + Internal_key death) { auto itB = keyToFiltrationValue_.find(birth); auto itD = keyToFiltrationValue_.find(death); if (itB->second != itD->second) stream_interval(dim, itB->second, itD->second); @@ -500,11 +500,11 @@ class Filtered_zigzag_persistence { * Assumed to be always larger or equal to previously used filtration values or always smaller or equal than previous * values, ie. the changes are monotonous. */ - template > - internal_key insert_face(face_key faceID, + template > + Internal_key insert_face(Face_key faceID, const BoundaryRange& boundary, - dimension_type dimension, - filtration_value filtrationValue) + Dimension dimension, + Filtration_value filtrationValue) { ++numArrow_; @@ -515,7 +515,7 @@ class Filtered_zigzag_persistence { keyToFiltrationValue_.try_emplace(numArrow_, filtrationValue); // Compute the keys of the faces of the boundary. - std::set translatedBoundary; // set maintains the natural order on indices + std::set translatedBoundary; // set maintains the natural order on indices for (auto b : boundary) { translatedBoundary.insert(handleToKey_.at(b)); // TODO: add possibilities of coefficients } @@ -533,7 +533,7 @@ class Filtered_zigzag_persistence { * Assumed to be always larger or equal to previously used filtration values or always smaller or equal than previous * values, ie. the changes are monotonous. */ - internal_key remove_face(face_key faceID, filtration_value filtrationValue) { + Internal_key remove_face(Face_key faceID, Filtration_value filtrationValue) { ++numArrow_; auto it = handleToKey_.find(faceID); @@ -552,7 +552,7 @@ class Filtered_zigzag_persistence { * on homology level. Useful to keep the birth/death indices aligned when insertions/removals are purposely skipped * to avoid useless computation. */ - internal_key apply_identity() { + Internal_key apply_identity() { ++numArrow_; pers_.apply_identity(); return numArrow_; @@ -568,16 +568,16 @@ class Filtered_zigzag_persistence { template void get_current_infinite_intervals(F&& stream_infinite_interval) { pers_.get_current_infinite_intervals( - [&](dimension_type dim, internal_key birth) { stream_infinite_interval(dim, keyToFiltrationValue_.at(birth)); }); + [&](Dimension dim, Internal_key birth) { stream_infinite_interval(dim, keyToFiltrationValue_.at(birth)); }); } private: template - using dictionary = std::unordered_map; // TODO: benchmark with other map types + using Dictionary = std::unordered_map; // TODO: benchmark with other map types - dictionary handleToKey_; /**< Map from input keys to internal keys. */ - internal_key numArrow_; /**< Current arrow number. */ - dictionary keyToFiltrationValue_; /**< Face Key to filtration value map. */ + Dictionary handleToKey_; /**< Map from input keys to internal keys. */ + Internal_key numArrow_; /**< Current arrow number. */ + Dictionary keyToFiltrationValue_; /**< Face Key to filtration value map. */ Zigzag_persistence pers_; /**< Class computing the pairs. */ }; // end class Filtered_zigzag_persistence diff --git a/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h index 7d6d20c6d8..cdb32a6fe1 100644 --- a/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h @@ -62,8 +62,8 @@ struct Zigzag_matrix_options : Gudhi::persistence_matrix::Default_options; * - * using dimension_type = Zigzag_persistence::dimension_type; - * using index = Zigzag_persistence::index; + * using Dimension = Zigzag_persistence::Dimension; + * using Index = Zigzag_persistence::Index; * ``` * * #### Construction with default values * ``` * //Zigzag_persistence(callback) with for example callback method as a anonymous lambda - * Zigzag_persistence zp([](dimension_type dim, index birth, index death) { + * Zigzag_persistence zp([](Dimension dim, Index birth, Index death) { * std::cout << "[" << dim << "] " << birth << " - " << death << std::endl; * }); * ``` @@ -137,7 +137,7 @@ struct Default_zigzag_options { * // Only the closed bars where output so far, so the open/infinite bars still need to be retrieved. * * //in this example, outputs (0, 0) and (0, 7) - * zp.get_current_infinite_intervals([](dimension_type dim, index birth){ + * zp.get_current_infinite_intervals([](Dimension dim, Index birth){ * std::cout << "[" << dim << "] " << birth << " - inf" << std::endl; * }); * ``` @@ -150,20 +150,20 @@ template = 108100 - using birth_dictionary = boost::unordered_flat_map; /**< Dictionary type. */ - // using birth_dictionary = boost::unordered_map; /**< Dictionary type. */ + using Birth_dictionary = boost::unordered_flat_map; /**< Dictionary type. */ + // using Birth_dictionary = boost::unordered_map; /**< Dictionary type. */ #else - using birth_dictionary = std::unordered_map; /**< Dictionary type. */ + using Birth_dictionary = std::unordered_map; /**< Dictionary type. */ #endif - using Matrix_options = Zigzag_matrix_options; /**< Matrix options. */ - using Matrix_type = Gudhi::persistence_matrix::Matrix; /**< Matrix. */ - using matrix_index = typename Matrix_type::Index; /**< Matrix indexation type. */ + using Matrix_options = Zigzag_matrix_options; /**< Matrix options. */ + using Matrix = Gudhi::persistence_matrix::Matrix; /**< Matrix. */ + using Matrix_index = typename Matrix::Index; /**< Matrix indexation type. */ /** \brief Maintains the birth ordering \f$\leq_b\f$. * @@ -197,7 +197,7 @@ class Zigzag_persistence * * @param arrowNumber Forward arrow number. */ - void add_birth_forward(index arrowNumber) { // amortized constant time + void add_birth_forward(Index arrowNumber) { // amortized constant time birthToPos_.emplace_hint(birthToPos_.end(), arrowNumber, maxBirthPos_); ++maxBirthPos_; } @@ -208,7 +208,7 @@ class Zigzag_persistence * * @param arrowNumber Backward arrow number. */ - void add_birth_backward(index arrowNumber) { // amortized constant time + void add_birth_backward(Index arrowNumber) { // amortized constant time birthToPos_.emplace_hint(birthToPos_.end(), arrowNumber, minBirthPos_); --minBirthPos_; } @@ -220,7 +220,7 @@ class Zigzag_persistence * * @param birth Birth to remove. */ - void remove_birth(index birth) { birthToPos_.erase(birth); } + void remove_birth(Index birth) { birthToPos_.erase(birth); } /** * @brief Increasing birth order <=b, true iff k1 b k2. * @@ -236,12 +236,12 @@ class Zigzag_persistence * @param k2 * @return true if k1 >b k2, false otherwise. */ - bool reverse_birth_order(index k1, index k2) const { return birthToPos_.at(k1) > birthToPos_.at(k2); } + bool reverse_birth_order(Index k1, Index k2) const { return birthToPos_.at(k1) > birthToPos_.at(k2); } private: - birth_dictionary birthToPos_; /**< birth_to_pos_[i] < birth_to_pos_[j] iff i stream_interval, + Zigzag_persistence(std::function stream_interval, unsigned int preallocationSize = 0) : matrix_( preallocationSize, - [this](matrix_index columnIndex1, matrix_index columnIndex2) -> bool { + [this](Matrix_index columnIndex1, Matrix_index columnIndex2) -> bool { if (matrix_.get_column(columnIndex1).is_paired()) { return matrix_.get_pivot(columnIndex1) < matrix_.get_pivot(columnIndex2); } return birthOrdering_.birth_order(births_.at(columnIndex1), births_.at(columnIndex2)); }, - [this](matrix_index columnIndex1, matrix_index columnIndex2) -> bool { return false; }), + [this](Matrix_index columnIndex1, Matrix_index columnIndex2) -> bool { return false; }), numArrow_(-1), stream_interval_(std::move(stream_interval)) {} /** @@ -286,8 +286,8 @@ class Zigzag_persistence * @param dimension Dimension of the inserted face. * @return Number of the operation. */ - template > - index insert_face(const BoundaryRange& boundary, dimension_type dimension) { + template > + Index insert_face(const BoundaryRange& boundary, Dimension dimension) { ++numArrow_; _process_forward_arrow(boundary, dimension); return numArrow_; @@ -299,7 +299,7 @@ class Zigzag_persistence * @param arrowNumber Arrow number of when the face to remove was inserted for the last time. * @return Number of the operation. */ - index remove_face(index arrowNumber) { + Index remove_face(Index arrowNumber) { ++numArrow_; _process_backward_arrow(arrowNumber); return numArrow_; @@ -311,7 +311,7 @@ class Zigzag_persistence * to avoid useless computation. Increases the arrow number by one. * @return Number of the operation. */ - index apply_identity() { return ++numArrow_; } + Index apply_identity() { return ++numArrow_; } /** * @brief Outputs through the given callback method all birth indices which are currently not paired with @@ -350,8 +350,8 @@ class Zigzag_persistence * @param dim Dimension of the inserted face. */ template - void _process_forward_arrow(const BoundaryRange& boundary, dimension_type dim) { - std::vector chainsInF = matrix_.insert_boundary(numArrow_, boundary, dim); + void _process_forward_arrow(const BoundaryRange& boundary, Dimension dim) { + std::vector chainsInF = matrix_.insert_boundary(numArrow_, boundary, dim); if (!chainsInF.empty()) { _apply_surjective_reflection_diamond(dim, chainsInF); @@ -372,7 +372,7 @@ class Zigzag_persistence * @param dim Dimension of the inserted face. * @param chainsInF Indices of the non paired columns in the matrix. */ - void _apply_surjective_reflection_diamond(dimension_type dim, const std::vector& chainsInF) { + void _apply_surjective_reflection_diamond(Dimension dim, const std::vector& chainsInF) { // fp is the largest death index for <=d // Set col_fp: col_fp <- col_f1+...+col_fp (now in G); preserves lowest idx auto chainFp = chainsInF[0]; // col_fp, with largest death bool { + auto cmp_birth = [this](Index k1, Index k2) -> bool { return birthOrdering_.reverse_birth_order(k1, k2); }; // true iff b(k1) >b b(k2) // availableBirth: for all i by >d value of the d_i, // contains at step i all b_j, j > i, and maybe b_i if not stolen - std::set availableBirth(cmp_birth); + std::set availableBirth(cmp_birth); // for f1 to f_{p} (i by <=d), insertion in availableBirth sorts by >=b for (auto& chainF : chainsInF) { availableBirth.insert(births_.at(chainF)); @@ -429,7 +429,7 @@ class Zigzag_persistence lastModifiedChainIt = chainFIt; // new cumulated c_i+...+c_1 // remove the max available death auto maxAvailBirthIt = availableBirth.begin(); // max because order by decreasing modifiedColumns; + std::vector modifiedColumns; const auto& row = matrix_.get_row(faceID); modifiedColumns.reserve(row.size()); std::transform(row.begin(), row.end(), std::back_inserter(modifiedColumns), [](const auto& cell) { return cell.get_column_index(); }); // Sort by left-to-right order in the matrix_ (no order maintained in rows) - std::stable_sort(modifiedColumns.begin(), modifiedColumns.end(), [this](matrix_index i1, matrix_index i2) { + std::stable_sort(modifiedColumns.begin(), modifiedColumns.end(), [this](Matrix_index i1, Matrix_index i2) { return matrix_.get_pivot(i1) < matrix_.get_pivot(i2); }); @@ -492,11 +492,11 @@ class Zigzag_persistence } private: - Matrix_type matrix_; /**< Matrix storing a base of the current chain complex. */ - birth_dictionary births_; /**< Map face index in F to corresponding birth. */ + Matrix matrix_; /**< Matrix storing a base of the current chain complex. */ + Birth_dictionary births_; /**< Map face index in F to corresponding birth. */ Birth_ordering birthOrdering_; /**< Maintains stream_interval_; /**< Callback method for closed pairs. */ + Index numArrow_; /**< Current arrow number. */ + std::function stream_interval_; /**< Callback method for closed pairs. */ }; // end class Zigzag_persistence } // namespace zigzag_persistence diff --git a/src/Zigzag_persistence/test/test_filtered_zigzag_persistence.cpp b/src/Zigzag_persistence/test/test_filtered_zigzag_persistence.cpp index 6219f63bdd..4e20047a51 100644 --- a/src/Zigzag_persistence/test/test_filtered_zigzag_persistence.cpp +++ b/src/Zigzag_persistence/test/test_filtered_zigzag_persistence.cpp @@ -67,7 +67,7 @@ void test_barcode(ZP& zp, std::vector& b template void test_indices(ZP& zp, std::vector& indices, - std::vector& indexToFil) { + std::vector& indexToFil) { auto it = indices.begin(); for (const auto& interval : zp.get_index_persistence_diagram()) { BOOST_CHECK_EQUAL(interval.dim, it->dim); @@ -128,8 +128,8 @@ std::vector get_filtration_values() { template void test_filtered_zigzag_with_storage() { - using face_handle = typename ZP::face_key; - using filtration_value = typename ZP::filtration_value; + using face_handle = typename ZP::Face_key; + using Filtration_value = typename ZP::Filtration_value; using Interval_index = typename ZP::Index_interval; using Interval_filtration = typename ZP::Filtration_value_interval; @@ -140,7 +140,7 @@ void test_filtered_zigzag_with_storage() { realBarcode.reserve(9); std::vector > simplices = get_boundaries(); - std::vector filValues = get_filtration_values(); + std::vector filValues = get_filtration_values(); for (unsigned int i = 0; i < 14; ++i) { zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); @@ -203,8 +203,8 @@ void test_filtered_zigzag_with_storage() { template void test_filtered_zigzag_with_storage_max1() { - using face_handle = typename ZP::face_key; - using filtration_value = typename ZP::filtration_value; + using face_handle = typename ZP::Face_key; + using Filtration_value = typename ZP::Filtration_value; using Interval_index = typename ZP::Index_interval; using Interval_filtration = typename ZP::Filtration_value_interval; @@ -215,7 +215,7 @@ void test_filtered_zigzag_with_storage_max1() { realBarcode.reserve(3); std::vector > simplices = get_boundaries(); - std::vector filValues = get_filtration_values(); + std::vector filValues = get_filtration_values(); for (unsigned int i = 0; i < 14; ++i) { zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); @@ -264,13 +264,13 @@ BOOST_AUTO_TEST_CASE(filtered_zigzag_persistence_with_storage) { template void test_filtered_zigzag() { - using face_handle = typename ZP::face_key; - using filtration_value = typename ZP::filtration_value; - using dimension_type = typename ZP::dimension_type; - using Interval = std::tuple; + using face_handle = typename ZP::Face_key; + using Filtration_value = typename ZP::Filtration_value; + using Dimension = typename ZP::Dimension; + using Interval = std::tuple; Interval interval; - ZP zp([&](dimension_type dim, filtration_value birth, filtration_value death){ + ZP zp([&](Dimension dim, Filtration_value birth, Filtration_value death){ BOOST_CHECK_EQUAL(std::get<0>(interval), dim); BOOST_CHECK_EQUAL(std::get<1>(interval), birth); BOOST_CHECK_EQUAL(std::get<2>(interval), death); @@ -309,7 +309,7 @@ void test_filtered_zigzag() { realBarcode.emplace_back(3, 0, 28); //dummy std::vector > simplices = get_boundaries(); - std::vector filValues = get_filtration_values(); + std::vector filValues = get_filtration_values(); for (unsigned int i = 0; i < 14; ++i) { interval = realBarcode[i]; @@ -342,14 +342,14 @@ void test_filtered_zigzag() { //there is no real guarantee on the order of the infinite bars std::vector infiniteBars; - zp.get_current_infinite_intervals([&](dimension_type dim, filtration_value birth) { - infiniteBars.emplace_back(dim, birth, std::numeric_limits::infinity()); + zp.get_current_infinite_intervals([&](Dimension dim, Filtration_value birth) { + infiniteBars.emplace_back(dim, birth, std::numeric_limits::infinity()); }); realBarcode.clear(); - realBarcode.emplace_back(0, 0, std::numeric_limits::infinity()); - realBarcode.emplace_back(0, 9, std::numeric_limits::infinity()); - realBarcode.emplace_back(2, 10, std::numeric_limits::infinity()); + realBarcode.emplace_back(0, 0, std::numeric_limits::infinity()); + realBarcode.emplace_back(0, 9, std::numeric_limits::infinity()); + realBarcode.emplace_back(2, 10, std::numeric_limits::infinity()); std::sort(infiniteBars.begin(), infiniteBars.end()); std::sort(realBarcode.begin(), realBarcode.end()); @@ -364,13 +364,13 @@ void test_filtered_zigzag() { template void test_filtered_zigzag_max1() { - using face_handle = typename ZP::face_key; - using filtration_value = typename ZP::filtration_value; - using dimension_type = typename ZP::dimension_type; - using Interval = std::tuple; + using face_handle = typename ZP::Face_key; + using Filtration_value = typename ZP::Filtration_value; + using Dimension = typename ZP::Dimension; + using Interval = std::tuple; Interval interval; - ZP zp([&](dimension_type dim, filtration_value birth, filtration_value death){ + ZP zp([&](Dimension dim, Filtration_value birth, Filtration_value death){ if (dim < 1){ BOOST_CHECK_EQUAL(std::get<0>(interval), dim); BOOST_CHECK_EQUAL(std::get<1>(interval), birth); @@ -413,7 +413,7 @@ void test_filtered_zigzag_max1() { realBarcode.emplace_back(1, 0, 28); //dummy std::vector > simplices = get_boundaries(); - std::vector filValues = get_filtration_values(); + std::vector filValues = get_filtration_values(); for (unsigned int i = 0; i < 14; ++i) { interval = realBarcode[i]; @@ -446,15 +446,15 @@ void test_filtered_zigzag_max1() { //there is no real guarantee on the order of the infinite bars std::vector infiniteBars; - zp.get_current_infinite_intervals([&](dimension_type dim, filtration_value birth) { + zp.get_current_infinite_intervals([&](Dimension dim, Filtration_value birth) { if (dim < 1){ - infiniteBars.emplace_back(dim, birth, std::numeric_limits::infinity()); + infiniteBars.emplace_back(dim, birth, std::numeric_limits::infinity()); } }); realBarcode.clear(); - realBarcode.emplace_back(0, 0, std::numeric_limits::infinity()); - realBarcode.emplace_back(0, 9, std::numeric_limits::infinity()); + realBarcode.emplace_back(0, 0, std::numeric_limits::infinity()); + realBarcode.emplace_back(0, 9, std::numeric_limits::infinity()); std::sort(infiniteBars.begin(), infiniteBars.end()); std::sort(realBarcode.begin(), realBarcode.end()); diff --git a/src/Zigzag_persistence/test/test_zigzag_persistence.cpp b/src/Zigzag_persistence/test/test_zigzag_persistence.cpp index 99eb62f375..c589fa57b6 100644 --- a/src/Zigzag_persistence/test/test_zigzag_persistence.cpp +++ b/src/Zigzag_persistence/test/test_zigzag_persistence.cpp @@ -21,7 +21,7 @@ using ZP = Gudhi::zigzag_persistence::Zigzag_persistence<>; struct Interval { Interval() {} - Interval(int dim, ZP::index b, ZP::index d) : dim_(dim), b_(b), d_(d) {} + Interval(int dim, ZP::Index b, ZP::Index d) : dim_(dim), b_(b), d_(d) {} int dim() const { return dim_; } int birth() const { return b_; } @@ -29,13 +29,13 @@ struct Interval { private: int dim_; - ZP::index b_; - ZP::index d_; + ZP::Index b_; + ZP::Index d_; }; BOOST_AUTO_TEST_CASE(constructor) { std::vector pairs; - auto stream = [&](int dim, ZP::index birth, ZP::index death){ pairs.emplace_back(dim, birth, death); }; + auto stream = [&](int dim, ZP::Index birth, ZP::Index death){ pairs.emplace_back(dim, birth, death); }; BOOST_CHECK_NO_THROW(ZP zp(stream)); BOOST_CHECK_NO_THROW(ZP zp(stream, 28)); @@ -88,8 +88,8 @@ std::vector > get_boundaries() { BOOST_AUTO_TEST_CASE(zigzag_persistence_single) { std::vector pairs; - auto stream = [&](int dim, ZP::index birth, ZP::index death) { pairs.emplace_back(dim, birth, death); }; - auto stream_inf = [&](int dim, ZP::index birth) { pairs.emplace_back(dim, birth, -1); }; + auto stream = [&](int dim, ZP::Index birth, ZP::Index death) { pairs.emplace_back(dim, birth, death); }; + auto stream_inf = [&](int dim, ZP::Index birth) { pairs.emplace_back(dim, birth, -1); }; ZP zp(stream, 28); std::vector realIndices; realIndices.reserve(13); @@ -152,10 +152,10 @@ BOOST_AUTO_TEST_CASE(zigzag_persistence_single) { BOOST_AUTO_TEST_CASE(zigzag_persistence_single_max1) { std::vector pairs; - auto stream = [&](int dim, ZP::index birth, ZP::index death) { + auto stream = [&](int dim, ZP::Index birth, ZP::Index death) { if (dim < 1) pairs.emplace_back(dim, birth, death); }; - auto stream_inf = [&](int dim, ZP::index birth) { + auto stream_inf = [&](int dim, ZP::Index birth) { if (dim < 1) pairs.emplace_back(dim, birth, -1); }; ZP zp(stream, 28); From 2b2a569a463cc5324def5d61dc1004afd9c17095 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Tue, 17 Sep 2024 18:17:20 +0200 Subject: [PATCH 48/51] doc --- .../example/example_usage_filtered_zigzag_persistence.cpp | 6 +++--- ...ample_usage_filtered_zigzag_persistence_with_storage.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence.cpp b/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence.cpp index 50bc50f894..0419dd79f9 100644 --- a/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence.cpp +++ b/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence.cpp @@ -33,15 +33,15 @@ int main() { zp.insert_face(2, {}, 0, 0.1); // inserts vertex 4 at filtration value 0.1 -> birth at 0.1 of 0-cycle zp.insert_face(4, {}, 0, 0.1); - // inserts edge 5 = (2,4) at filtration value 0.3 -> death at 0.3 -> outputs/stores (0, 0.1, 0.3) + // inserts edge 5 = (2,4) at filtration value 0.3 -> death at 0.3 -> outputs (0, 0.1, 0.3) zp.insert_face(5, {2, 4}, 1, 0.3); // inserts vertex 3 at filtration value 0.4 -> birth at 0.4 of 0-cycle zp.insert_face(3, {}, 0, 0.4); - // inserts edge 6 = (2,3) at filtration value 0.4 -> death at 0.4 of the cycle born at 0.4 -> outputs/stores nothing + // inserts edge 6 = (2,3) at filtration value 0.4 -> death at 0.4 of the cycle born at 0.4 -> outputs nothing zp.insert_face(6, {2, 3}, 1, 0.4); // inserts edge 9 = (3,4) at filtration value 1.2 -> birth at 1.2 of 1-cycle zp.insert_face(9, {4, 3}, 1, 1.2); - // removes edge 6 at filtration value 1.5 -> death at 1.5 -> outputs/stores (1, 1.2, 1.5) + // removes edge 6 at filtration value 1.5 -> death at 1.5 -> outputs (1, 1.2, 1.5) zp.remove_face(6, 1.5); // removes edge 5 at filtration value 2.0 -> birth at 2.0 of 0-cycle zp.remove_face(5, 2.0); diff --git a/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence_with_storage.cpp b/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence_with_storage.cpp index 5136d5eb2a..7cc0c866e9 100644 --- a/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence_with_storage.cpp +++ b/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence_with_storage.cpp @@ -28,15 +28,15 @@ int main() { zp.insert_face(2, {}, 0, 0.1); // inserts vertex 4 at filtration value 0.1 -> birth at 0.1 of 0-cycle zp.insert_face(4, {}, 0, 0.1); - // inserts edge 5 = (2,4) at filtration value 0.3 -> death at 0.3 -> outputs/stores (0, 0.1, 0.3) + // inserts edge 5 = (2,4) at filtration value 0.3 -> death at 0.3 -> stores (0, 0.1, 0.3) zp.insert_face(5, {2, 4}, 1, 0.3); // inserts vertex 3 at filtration value 0.4 -> birth at 0.4 of 0-cycle zp.insert_face(3, {}, 0, 0.4); - // inserts edge 6 = (2,3) at filtration value 0.4 -> death at 0.4 of the cycle born at 0.4 -> outputs/stores nothing + // inserts edge 6 = (2,3) at filtration value 0.4 -> death at 0.4 of the cycle born at 0.4 -> stores nothing zp.insert_face(6, {2, 3}, 1, 0.4); // inserts edge 9 = (3,4) at filtration value 1.2 -> birth at 1.2 of 1-cycle zp.insert_face(9, {4, 3}, 1, 1.2); - // removes edge 6 at filtration value 1.5 -> death at 1.5 -> outputs/stores (1, 1.2, 1.5) + // removes edge 6 at filtration value 1.5 -> death at 1.5 -> stores (1, 1.2, 1.5) zp.remove_face(6, 1.5); // removes edge 5 at filtration value 2.0 -> birth at 2.0 of 0-cycle zp.remove_face(5, 2.0); From 1e31a4b2a2966251f50ae070d35f40a867cc2cea Mon Sep 17 00:00:00 2001 From: hschreiber Date: Wed, 18 Sep 2024 14:07:36 +0200 Subject: [PATCH 49/51] doc --- src/Zigzag_persistence/doc/zigzag_ex.png | Bin 2547 -> 3629 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/Zigzag_persistence/doc/zigzag_ex.png b/src/Zigzag_persistence/doc/zigzag_ex.png index a58cc6ed5470ec17ce16cbf34cb96a15a4972c9e..ba7cb7011a86731a2526c2c070cff0f2e0bde025 100644 GIT binary patch literal 3629 zcmb7Hdpwiv|DQt^vK;!VXjDRoo?=eJo-%|GC5MPcPBX`9-YEXb>FZ1zTek%eXh^@`h4Ek=f3a4L*8y`Dmp44 z5J=7a0MZWxk{gshr>|9#Uj5F!yCuCTp*-D?AV79p?`tARM>5@!`;Md$=RRb#ojjR7 zu)qtE%ir7-uJK=c?d^pd5@B6+0}#u!=VfWnH9r~FUc7(wn8(hzaY�U)YA%sz{T< zKv-$uUwhg|o0}Eg_U(D`dLqztjmsKjZR*06=o(^HQAW+ZACp}EWA17;evp{@!)fKw z9d`HhJ(|D?U3I!y)mXu;L-?+byjb5^^N87{o0DO`DWJ5S-03Y`9w9^P2ksYTt!9vE z@J#{G2MGcq6imFuM1ym8%dg}b%X44ot*mEEBwUziAuj2*->T)wZD5X)(S^z=tt6*; zHB^&m4MTbrF6!5o>8o0^*_-wA?%0Oj|}Vx#%J;hfJ@ulfPMVy-oH z62eF)cWQz2XbON=BnLEa#HdA9-`7e!_?R~E=dm(Dw!tF%JUIb{PkVN8K(ax&VZqzM zV-BE*2AD7h}s7$*piof*B&Tc-lEU6j^&c#o+&dJH zt88)nw1fdUd06$m2aALABJ34~Xhyr*V|GtSclwltqNKKw=2T?6a@HxyC<*C_XfiYT zW)oKq9?Wh7e>1WNI*f)w3GoI-OHE_(#(bZ`*EH74qft|9#T!S73uhVQ0smOocU=5s7&OQ*gm9CsnkRqYQ!C7X;=Bx7BiRh1dX zDbUpx?!*2Df43?OWIhGe7o+Mq#a=ml{7ySYvX<#rN|WbR%nR-M&)}>GG~r|U%%VVS z6+4dEwj5U8K6MK5cP>26Vac^FPvF4>zGyP>mE$Sz|C^fA1gdGYrES(9rS<~ zP^<$@SN9#>Xt~4SK-_Qr68U zGrG}Eq>i9&(P)3r(>k+4onjC^&0ln)V)( zyOSrCTN4HF0wY4Q7ffU1_6X0|6ilz?3$X>3w_|M(X^7-A#}_Rr1_^cZ5C220q5+!G zz8e*I29*A4hqLj}Wc4h=`FQ58G@InDedRrum@ej5Iy zO-@o?__h$kSjxp}Vc5qCVRu~+QKI%X_JS#M*P^6?O1D~G$*xDih(^E^^c4o(q6kl?8RD2Ur+PJhJh3?7HzBL%B$MRNeE8qFSH} zCpvH0rLUN&oVF=R?eDxfGOYmUu0Oz`gk!$h*kSVPdWk-b@{iqgfDBkL=WU^+fpvk8I@=nhJZ-V-tH52vb~dJT(E{W1slp0OyZikw zM=I!$3Ic25RMy3vBFxqy3qU~})@e}j;o~WsBl8i(n9^}Z%ggA>EY{W~O+M+v=T8H2 zVR~7aJCnck%P;oE4nX&3U$(%+gGUB+u)Pp6Kdh$y492F+`@(wAb|EzoZ0LZh#4G6Khyv1yfY~n4l6>L`QA_ucbI-*ju+V`W zb~0Jg`O%c|DWc|KQY-!v2e8pDOo(j@FZY1s zwGzOQym@t<+iZydrxpO(J(r>G=VQbI)-nGyQ48e?aNj<(N6Ue8mlhq>Tfk#{oWgw1 zw|s-sLn6|-(=nxOra%UK%U^F`96)8F*8jG>s5{-wnI#U(;})9+Rf(q_)z{9Rl2DVq zUfbXGIBElQc*Ye-83g7Z9gG?Tx{!nZIx4?y@6FKJP_^??9v#j%BhiDT5xCC9X*Lit zv7_C&H4;5S;^KAWe-BRQ^Qy2V@PCmYQBah^{_R#vM&f}7O5&6L)9u6&N#b)hlr`|1 z^Po6`17*GVjlRf85^VumF%lX)>pmtkwsTLA_+Wk+yGlX{M=XAGe)p1uo%Wsm7g3}f zJ<6qw2ghq0A@{0qmDrNo4IE&@FUp}a^o$-;e+uxQHt7OF*>pH>C+uIuk*>C}Uqrgl zzHg<%(QRKG6ko7yT#el z{4*S|<~93dad2L}A%we-c6?(oj%GN1bpyY~iu=km$W*jMfzLXdihRnXIos7JTX9EY z2@rWQDF0}ywOk_DdyVyXc*#U#8L*`iYpouX4*wgg zxR@OrIwbOW8boGgt>5pioASvUBBHjZ54@<#oGAv5r#oKqDD#xX@?Ei~B;(WKAErUx zq9s~XOROD>WFw0^Q6{W6cZqxi;fD4TaI)j2yRHvdbVvZqq+5)oNRv^9mZYMmeJfH% zaM7~7+Q~9GV#X0;qi&s!X}Lg@iIH8JDnVC9|CCT!h9var8;2Asjw>7Q{147TDx$L& zih-lj_zzwvk;=xZbsLKWb9qzSX!3u!EQt0~`p&~r=~p~|#i!d!(>bXwkWxHCQy0*8 z%HScHzu8T35?%igXt0juj4ZvT&JU7>sm{MtQ^ZQ)4;J=6hL@C(4#nH}7C!;{A8ojJnpk&Y_ z_F?VwoAn+dyx5G)=T>HH$-7|A`qk*;L3q(cK1ohICA9RvU&5D}n?SRCbbfEZ?NSfD zz13Fn)4m>f+nBS(CH&DJ4JV{~cVc+oSYAU(=M7^uX^|j!Ln`!kG=98>eSJ$>ZanK8 z;E~g1KF*fIn0{>mNr=All9bY2Cf>al(7Hr_lO>KKy*|`t9qW+ykN7k>^$$R&+Fpc} zblw7G>i1~b6kkX*TR<0EZ1?c9(IgptO?XMVkmx7Dof7~zU#!PVD*h;C$C5+)-(403 z#A*w8V#Ilp&tXGLz_eV{`Q-dD=}W0Kk{yO`2wGjIz5Evaqxh?r^#2d!?&6K?*dKoG Ee~960SO5S3 literal 2547 zcmZ{k2UJt(62~vH8pU-jh`6AD(w=025Dg$GQF<2wEK&pmL>6Kwf`n#*0-}q+Mu<}6 z&}%3vWK~*LT}2TLH4&sJ6s?$fsZ0R979$`iq&?N2#QO9ARchVO+nCQ1O&~ThaiJY z2$BuC*Wh>pY;5weu`q>%Nonm2U~LPrbPEHkrRXw$jW+@}uy89oGuR|dX7|r}zrfK% z;M(PjwT}SeY6z0_0k##mu50neC=j&O!^-rubL7D6(1TQEmwhkicP5xVlt|Qlw@XGM zM>fsDFDLr&rox$wcFh}c$$hj)5%J(%nu4s^^vZ(d(?_+Y?~a8 zsq_>o?&-`f;C=mc^igbT&Yv3x{qA?`xyA72AI&SWiY+GGr&EgE;kdRXcVc`?#AjOT zv4plH2T3jCQg=-AYI(15%ER5BhRc1va>f%T<=A?od9CxO6o^I|OXW1jZ#I^-&gW;> zy)QRfHRO3VVs!G`)-2tk9$V~ZT;Kj&qPw)qx|YcgUd(4}(dBF>){Co7^eWprK2Q7z z^|PnC5RxMYnwd+D5fy!~GxyguIO>E6tO7P$PU}b5t0bw3=;{f3T8DE4$(mGT>yn1I zDGBWGAQ3B`)O1jpF8B+TO;f9(nQe^*8D4o7zg1(Ooh>B&Rw3WlfT^(#Rw9OfGDG^1N}s)(dyBkm8rVbjs4E(IG#1-E zLZELuF+?wuoxw|T?_W7yJIH_E(@Y;2qy|!RYcNxP;s$~k%ggL#PE6K580XG8cpjl9 z24CLf(7c#5-`k4SxLb_Dlwz{#SJw*Q;bd0l3ciQFmoZ8v3hcVOMU#ym*QDAj{KOX)nub+DY2H^#b8>f>G1{28TE2Z^kP5YdlCczR zSH@JfJtwJF_BAHGldx-wGnYQsu1{n-TecZAU)#I9cB+aR47*;I@^|c_>%^$7c}qXzmV=@F^Hm^8ZD^MLy~?Y_s3 zzqV~&QTXn8{npentBYDIv`!%&NjDX=Y@rA<$>j@|q&jSgq4B+Yr^wQ!Xz$hq{}egJ_26gR$xo3$ z>02aH=Cct1E$pqc2L5C6@S>$@&09LLN}EtM`*t#WL=C-l=XpLunV~`DU~HOhM$8~r}AO5(wt2hw64Cmv9)Itbpn$t zBPut5OHsNgpnV|DY9B@mB78f@*D?hWB_yeXy2ANd$JSio_^n7@vdZip?Qp@EcTuXL z@a9K)u(!^FqEHTX5VV@za^=PAJdXM7?*cs^-lB{;xka817tf%I-^wtzix(YNMtcM3 zlrNg)3()SFBjR>H28}wID2_1%?QRvfdlWn zY{I;mpyl?s;^O4h$gxSv*TZD*@txm(BR8z3IoZ*R8Tekp zfdxV$kViEUdYVWbXB|BQ9esmi$KVKr0RmxBBklQ(fDr5#a3Sjd3v?H0AAsOr|H}{_ zK){8CV+kSOX^msmUvZbh0)m4e2UBxR9W9iTuKQE44OyAlo7NbgzxE&arSYo( From d8a82735dbe698264c14505181370951a2759a3d Mon Sep 17 00:00:00 2001 From: hschreiber Date: Tue, 24 Sep 2024 15:56:22 +0200 Subject: [PATCH 50/51] name fix --- .../include/gudhi/Oscillating_rips_persistence.h | 4 ++-- .../gudhi/Zigzag_persistence/oscillating_rips_iterators.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h b/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h index 37da3d69c1..01dadce6c4 100644 --- a/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h @@ -134,7 +134,7 @@ void compute_oscillating_rips_persistence( using VectorEdgeRange = std::vector >; using EdgeRangeIterator = typename std::conditional::type; + typename VectorEdgeRange::const_iterator>::type; using OscillatingRipsSimplexRange = Oscillating_rips_simplex_range; StableFilteredComplex st; @@ -213,7 +213,7 @@ compute_oscillating_rips_persistence( using VectorEdgeRange = std::vector >; using EdgeRangeIterator = typename std::conditional::type; + typename VectorEdgeRange::const_iterator>::type; using OscillatingRipsSimplexRange = Oscillating_rips_simplex_range; StableFilteredComplex st; diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h index 2192030d21..60e0852503 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h @@ -894,7 +894,7 @@ class Oscillating_rips_edge_range for (size_t k = i + 1; k < n; ++k) { // set eps_range[k] <- d(p_k, P_i) == // min{ d(p_k, P_{i-1}), d(p_k, p_i) } for k >= i. - double dist_i_k = distance(points[i], points[k]); + double dist_i_k = distance(sortedPoints[i], sortedPoints[k]); if (dist_i_k < eps_range[k]) { eps_range[k] = dist_i_k; } From da86ef5fa75cb998b91e881c5a2455cd2a06c4f9 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Wed, 25 Sep 2024 11:03:04 +0200 Subject: [PATCH 51/51] add std:: to size_t --- .../example_zzfiltration_from_file.cpp | 4 +- .../oscillating_rips_iterators.h | 56 +++++++++---------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp index 333194be6f..4e04f2b00b 100644 --- a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp +++ b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp @@ -27,7 +27,7 @@ lineType read_operation(std::string& line, std::vector& faces, double faces.clear(); ID_handle num; - size_t current = line.find_first_not_of(' ', 0); + std::size_t current = line.find_first_not_of(' ', 0); if (current == std::string::npos) return COMMENT; if (line[current] == 'i') @@ -46,7 +46,7 @@ lineType read_operation(std::string& line, std::vector& faces, double std::clog << "(2) Syntaxe error in file." << std::endl; exit(0); } - size_t next = line.find_first_of(' ', current); + std::size_t next = line.find_first_of(' ', current); timestamp = std::stod(line.substr(current, next - current)); current = line.find_first_not_of(' ', next); diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h index 60e0852503..d37bab5f36 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h @@ -346,7 +346,7 @@ class Oscillating_rips_edge_range Filtration_value nu_; /**< Lower multiplier. */ Filtration_value mu_; /**< Upper multiplier. */ Zigzag_edge currentEdge_; /**< Stores the current edge in the range. */ - size_t epsilonIndex_, rowIndex_, columnIndex_; /**< Indices indicating the next position in the range. */ + std::size_t epsilonIndex_, rowIndex_, columnIndex_; /**< Indices indicating the next position in the range. */ bool inPositiveDirection_, insertVertex_; /**< Next direction and indicates if next ''edge'' is a vertex. */ /** @@ -521,7 +521,7 @@ class Oscillating_rips_edge_range * @param i Epsilon value index. * @param direction Direction. */ - void _update_edge(size_t i, bool direction) + void _update_edge(std::size_t i, bool direction) { if constexpr (EdgeModifier::isActive_) currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, @@ -589,7 +589,7 @@ class Oscillating_rips_edge_range // only at the very last step of the oscillating Rips filtration. std::vector > > edgesAdded, edgesRemoved; - size_t number_of_arrows = _compute_edges(nu, mu, epsilonValues, distanceMatrix, edgesAdded, edgesRemoved); + std::size_t number_of_arrows = _compute_edges(nu, mu, epsilonValues, distanceMatrix, edgesAdded, edgesRemoved); // Now, sort edges according to lengths, and put everything in edgeFiltration edgeFiltration.clear(); @@ -602,7 +602,7 @@ class Oscillating_rips_edge_range true); // epsilonValues[0], true); - for (size_t i = 0; i < n - 1; ++i) { // all ascending arrows eps_i + for (std::size_t i = 0; i < n - 1; ++i) { // all ascending arrows eps_i if constexpr (EdgeModifier::isActive_) { edgeFiltration.emplace_back(i + 1, i + 1, @@ -815,7 +815,7 @@ class Oscillating_rips_edge_range { GUDHI_CHECK((nu <= mu) && (nu >= 0), "Invalid parameters mu and nu"); - size_t n = points.size(); // number of points + std::size_t n = points.size(); // number of points PointRange sortedPoints; sortedPoints.reserve(n); @@ -873,16 +873,16 @@ class Oscillating_rips_edge_range static std::vector _compute_epsilon_values(const PointRange& sortedPoints, DistanceFunction&& distance) { - size_t n = sortedPoints.size(); + std::size_t n = sortedPoints.size(); std::vector eps_range(n, std::numeric_limits::infinity()); // compute all \f$\varepsilon_i\f$ values, such that eps_range[i] == // eps_i==d_H(P_i,P), for i=0 ... n-1: - for (size_t i = 0; i < n; ++i) { + for (std::size_t i = 0; i < n; ++i) { // entering step i, maintain eps_range[j] = eps_j for j= i. #ifdef GUDHI_USE_TBB - tbb::parallel_for(size_t(i + 1), n, [&](size_t k) { + tbb::parallel_for(std::size_t(i + 1), n, [&](std::size_t k) { // set eps_range[k] <- d(p_k, P_i) == // min{ d(p_k, P_{i-1}), d(p_k, p_i) } for k >= i. double dist_i_k = distance(sortedPoints[i], sortedPoints[k]); @@ -891,7 +891,7 @@ class Oscillating_rips_edge_range } }); #else - for (size_t k = i + 1; k < n; ++k) { + for (std::size_t k = i + 1; k < n; ++k) { // set eps_range[k] <- d(p_k, P_i) == // min{ d(p_k, P_{i-1}), d(p_k, p_i) } for k >= i. double dist_i_k = distance(sortedPoints[i], sortedPoints[k]); @@ -904,7 +904,7 @@ class Oscillating_rips_edge_range // to do: implement parallel version by dividing the vector // set eps_range[i] <- eps_i = d_H(P_i,P) = max_{k>i} d(p_k, P_i) double eps_i = 0.; - for (size_t k = i + 1; k < n; ++k) { + for (std::size_t k = i + 1; k < n; ++k) { if (eps_range[k] > eps_i) { eps_i = eps_range[k]; } @@ -936,10 +936,10 @@ class Oscillating_rips_edge_range { std::vector > > distanceMatrix(sortedPoints.size()); #ifdef GUDHI_USE_TBB - tbb::parallel_for(size_t(0), sortedPoints.size(), [&](size_t i) { + tbb::parallel_for(std::size_t(0), sortedPoints.size(), [&](std::size_t i) { // distanceMatrix[i] = std::vector< std::pair >(); distanceMatrix[i].resize(i); - for (size_t j = 0; j < i; ++j) { + for (std::size_t j = 0; j < i; ++j) { distanceMatrix[i][j] = std::make_pair(j, distance(sortedPoints[i], sortedPoints[j])); } // distanceMatrix[i] is sorted by (j, d(p_i,p_j)) < (k, d(p_i,p_k)) iff @@ -947,10 +947,10 @@ class Oscillating_rips_edge_range std::stable_sort(distanceMatrix[i].begin(), distanceMatrix[i].end(), Point_distance_comp()); }); #else - for (size_t i = 0; i < sortedPoints.size(); ++i) { // for all vertices + for (std::size_t i = 0; i < sortedPoints.size(); ++i) { // for all vertices // distanceMatrix[i] = std::vector< std::pair >(); distanceMatrix[i].resize(i); - for (size_t j = 0; j < i; ++j) { + for (std::size_t j = 0; j < i; ++j) { distanceMatrix[i][j] = std::make_pair(j, distance(sortedPoints[i], sortedPoints[j])); } std::stable_sort(distanceMatrix[i].begin(), distanceMatrix[i].end(), Point_distance_comp()); @@ -973,14 +973,14 @@ class Oscillating_rips_edge_range * * @return Total number of edges. */ - static size_t _compute_edges(Filtration_value nu, - Filtration_value mu, - const std::vector& epsilonValues, - std::vector > >& distanceMatrix, - std::vector > >& edgesAdded, - std::vector > >& edgesRemoved) + static std::size_t _compute_edges(Filtration_value nu, + Filtration_value mu, + const std::vector& epsilonValues, + std::vector > >& distanceMatrix, + std::vector > >& edgesAdded, + std::vector > >& edgesRemoved) { - size_t number_of_arrows = 0; + std::size_t number_of_arrows = 0; auto n = epsilonValues.size(); edgesAdded.resize(n); edgesRemoved.resize(n); @@ -994,7 +994,7 @@ class Oscillating_rips_edge_range // R({p_0, ... , p_{i+1}}, mu * eps_i) <- R({p_0, ... , p_{i+1}}, nu * eps_{i+1}) #ifdef GUDHI_USE_TBB // no need to consider the case i=n-1 in an oscillating Rips filtration - tbb::parallel_for(size_t(0), n - 1, [&](size_t i) { + tbb::parallel_for(std::size_t(0), n - 1, [&](std::size_t i) { typename std::vector >::iterator it; //----edgesAdded[i]: // consider first all edges added in inclusion: @@ -1002,7 +1002,7 @@ class Oscillating_rips_edge_range // i.e., all (p_j,p_k) with 0 <= k < j <= i with // nu eps_i < d(p_j,p_k) <= mu eps_i // these new edges get filtration value epsilonValues[i] - for (size_t j = 1; j <= i; ++j) { + for (std::size_t j = 1; j <= i; ++j) { // get very first edge (k,j), over all k >::iterator it; - for (size_t i = 0; i < n - 1; ++i) { + for (std::size_t i = 0; i < n - 1; ++i) { //----edgesAdded[i]: // consider first all edges added in inclusion: // R({p_0, ... , p_i}, nu * eps_i) -> R({p_0, ... , p_i}, mu * eps_i), // i.e., all (p_j,p_k) with 0 <= k < j <= i with // nu eps_i < d(p_j,p_k) <= mu eps_i // these new edges get filtration value epsilonValues[i] - for (size_t j = 1; j <= i; ++j) { + for (std::size_t j = 1; j <= i; ++j) { // get very first edge (k,j), over all k currentSimplices_; /**< Stores current simplex handles. */ StableFilteredComplex* complex_; /**< Pointer to the complex. */ - size_t currentSimplexIndex_; /**< Index to current position in currentSimplices_. */ + std::size_t currentSimplexIndex_; /**< Index to current position in currentSimplices_. */ EdgeRangeIterator currentEdgeIt_; /**< Iterator pointing to the next edge. */ EdgeRangeIterator endEdgeIt_; /**< End edge iterator. */ bool currentDirection_; /**< Current direction. */