Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ext/gudhi-devel
Submodule gudhi-devel updated 35 files
+67 −0 src/Multi_filtration/include/gudhi/Multi_filtration/multi_filtration_conversions.h
+3 −0 src/Multi_filtration/test/CMakeLists.txt
+94 −0 src/Multi_filtration/test/multifiltration_comp_unit_test.cpp
+1 −2 src/Multi_persistence/concept/MultiFiltrationValue.h
+1 −2 src/Multi_persistence/concept/PersistenceAlgorithm.h
+125 −39 src/Multi_persistence/include/gudhi/Multi_parameter_filtered_complex.h
+13 −19 src/Multi_persistence/include/gudhi/Multi_persistence/Box.h
+21 −34 src/Multi_persistence/include/gudhi/Multi_persistence/Line.h
+2 −2 src/Multi_persistence/include/gudhi/Multi_persistence/Module.h
+17 −32 src/Multi_persistence/include/gudhi/Multi_persistence/Multi_parameter_filtered_complex_pcoh_interface.h
+13 −25 src/Multi_persistence/include/gudhi/Multi_persistence/Persistence_interface_cohomology.h
+14 −26 src/Multi_persistence/include/gudhi/Multi_persistence/Persistence_interface_homology.h
+12 −20 src/Multi_persistence/include/gudhi/Multi_persistence/Persistence_interface_vineyard.h
+31 −62 src/Multi_persistence/include/gudhi/Multi_persistence/Point.h
+3 −6 src/Multi_persistence/include/gudhi/Multi_persistence/Summand.h
+2 −2 src/Multi_persistence/include/gudhi/Multi_persistence/summand_helpers.h
+23 −55 src/Multi_persistence/include/gudhi/Projective_cover_kernel.h
+53 −101 src/Multi_persistence/include/gudhi/Slicer.h
+16 −34 src/Multi_persistence/include/gudhi/Thread_safe_slicer.h
+3 −6 src/Multi_persistence/include/gudhi/multi_simplex_tree_helpers.h
+5 −7 src/Multi_persistence/include/gudhi/multiparameter_module_approximation.h
+51 −104 src/Multi_persistence/include/gudhi/slicer_helpers.h
+6 −22 src/Multi_persistence/test/multipersistence_batch_slice_helpers_unit_test.cpp
+6 −9 src/Multi_persistence/test/multipersistence_box_unit_test.cpp
+22 −29 src/Multi_persistence/test/multipersistence_build_slicer_helpers_unit_test.cpp
+81 −15 src/Multi_persistence/test/multipersistence_complex_unit_test.cpp
+3 −6 src/Multi_persistence/test/multipersistence_line_unit_test.cpp
+2 −19 src/Multi_persistence/test/multipersistence_mma_unit_test.cpp
+1 −1 src/Multi_persistence/test/multipersistence_module_unit_test.cpp
+10 −19 src/Multi_persistence/test/multipersistence_persistence_interface_unit_test.cpp
+4 −8 src/Multi_persistence/test/multipersistence_simplex_tree_helpers_unit_test.cpp
+29 −54 src/Multi_persistence/test/multipersistence_slicer_unit_test.cpp
+4 −4 src/Multi_persistence/test/multipersistence_summand_unit_test.cpp
+184 −0 src/python/include/python_interfaces/construction_utils.h
+3 −0 src/python/include/python_interfaces/numpy_utils.h
17 changes: 5 additions & 12 deletions multipers/_mma_nanobind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,18 +199,11 @@ void bind_module_class(nb::module_& m) {
.def_prop_ro("num_parameters", &Module::get_number_of_parameters)
.def_prop_rw("box",
&Module::get_box_view,
[](Module& self, nb::object box) {
try {
if (nb::ndarray_check(box.ptr())) {
self.set_box(nb::cast<NDArray2>(box));
return;
}
self.set_box(nb::cast<std::vector<std::vector<T>>>(box));
return;
} catch (const nb::cast_error&) {
throw nb::type_error(
"Box has to be set with an array or a nested sequence of shape (2, p).");
}
[](Module& self, nb::iterable box) {
auto boxCpp =
Gudhi::python::_convert_iterable_to_cpp_type<NDArray2, std::vector<std::vector<T>>>(
box, "Box has to be set with an array or a nested sequence of shape (2, p).");
std::visit([&](auto&& val) { self.set_box(val); }, boxCpp);
})
.def("set_box",
[](Module& self, NDArray2 box) {
Expand Down
65 changes: 34 additions & 31 deletions multipers/_slicer_nanobind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@
#include <nanobind/stl/vector.h>

#include <algorithm>
#include <cctype>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <limits>
#include <memory>
#include <optional>
#include <stdexcept>
#include <string>
Expand All @@ -28,7 +26,6 @@
#include "Persistence_slices_interface.h"
#include "ext_interface/backend_log_policy.hpp"
#include "ext_interface/nanobind_generator_basis.hpp"
#include "ext_interface/packed_multi_critical_bridge.hpp"
#include "ext_interface/nanobind_registry_helpers.hpp"
#include "ext_interface/nanobind_registry_runtime.hpp"
#include "gudhi/Multi_parameter_filtered_complex.h"
Expand All @@ -39,10 +36,9 @@
#include <python_interfaces/numpy_utils.h>
#include "multi_parameter_rank_invariant/hilbert_function.h"
#include "multi_parameter_rank_invariant/rank_invariant.h"
#include "multiparameter_module_approximation/approximation.h"
#include <gudhi/multiparameter_module_approximation.h>
#include "nanobind_array_utils.hpp"
#include "nanobind_dense_array_utils.hpp"
#include "nanobind_mma_registry_helpers.hpp"
#include "nanobind_object_utils.hpp"
#include "nanobind_slicer_serialization.hpp"

Expand Down Expand Up @@ -208,9 +204,9 @@ Wrapper& make_filtration_non_decreasing_inplace(Wrapper& self, bool safe) {
bool modified = true;
while (modified) {
modified = false;
for (size_t i = boundaries.size(); i-- > 0;) {
for (size_t i = 1; i < boundaries.size(); ++i) {
for (auto b : boundaries[i]) {
modified |= intersect_lifetimes(filtrations[b], filtrations[i]);
modified |= intersect_lifetimes(filtrations[i], filtrations[b]);
}
}
if (ordered) {
Expand Down Expand Up @@ -1676,7 +1672,7 @@ void bind_slicer_class(nb::module_& m, nb::list& available_slicers) {
nb::make_tuple(serialized_state<Wrapper, Value, Desc::is_kcritical, Desc::is_degree_rips>(self),
self.filtration_grid,
self.generator_basis,
self.minpres_degree));
self.minpres_degree));
})
.def("__reduce_ex__",
[](Wrapper& self, int) -> nb::tuple {
Expand All @@ -1686,7 +1682,7 @@ void bind_slicer_class(nb::module_& m, nb::list& available_slicers) {
nb::make_tuple(serialized_state<Wrapper, Value, Desc::is_kcritical, Desc::is_degree_rips>(self),
self.filtration_grid,
self.generator_basis,
self.minpres_degree));
self.minpres_degree));
})
.def("_serialize_state",
[](Wrapper& self) -> nb::ndarray<nb::numpy, uint8_t> {
Expand Down Expand Up @@ -1842,17 +1838,17 @@ void bind_slicer_class(nb::module_& m, nb::list& available_slicers) {
"basepoint"_a,
"direction"_a = nb::none(),
nb::rv_policy::reference_internal)
.def(
"set_slice",
[](Wrapper& self, nb::object values) -> Wrapper& {
auto c_values = cast_vector<Value>(values);
{
nb::gil_scoped_release release;
self.truc.set_slice(c_values);
}
return self;
},
nb::rv_policy::reference_internal)
// .def(
// "set_slice",
// [](Wrapper& self, nb::object values) -> Wrapper& {
// auto c_values = cast_vector<Value>(values);
// {
// nb::gil_scoped_release release;
// self.truc.set_slice(c_values);
// }
// return self;
// },
// nb::rv_policy::reference_internal)
.def(
"initialize_persistence_computation",
[](Wrapper& self, bool ignore_infinite_filtration_values) -> Wrapper& {
Expand Down Expand Up @@ -1904,15 +1900,15 @@ void bind_slicer_class(nb::module_& m, nb::list& available_slicers) {
}
return dim_barcode_to_tuple<Barcode, int>(barcode);
})
.def("get_current_filtration",
[](Wrapper& self) -> nb::ndarray<nb::numpy, Value> {
std::vector<Value> current;
{
nb::gil_scoped_release release;
current = self.truc.get_slice();
}
return owned_array<Value>(std::move(current), {current.size()});
})
// .def("get_current_filtration",
// [](Wrapper& self) -> nb::ndarray<nb::numpy, Value> {
// std::vector<Value> current;
// {
// nb::gil_scoped_release release;
// current = self.truc.get_slice();
// }
// return owned_array<Value>(std::move(current), {current.size()});
// })
.def(
"prune_above_dimension",
[](Wrapper& self, int max_dimension) -> Wrapper& {
Expand Down Expand Up @@ -2093,8 +2089,15 @@ Gudhi::multi_persistence::Module_interface<double> module_approximation_from_des
Gudhi::multi_persistence::Module<double> mod;
{
nb::gil_scoped_release release;
mod = Gudhi::multiparameter::mma::multiparameter_module_approximation(
wrapper.truc, direction, max_error, box, threshold, complete, verbose, n_jobs);
mod = Gudhi::multi_persistence::multiparameter_module_approximation(wrapper.truc,
max_error,
box.get_lower_corner(),
box.get_upper_corner(),
direction,
threshold,
complete,
verbose,
n_jobs);
}
return {std::move(mod), box};
}
Expand Down
98 changes: 45 additions & 53 deletions multipers/gudhi/Module_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <utility>
#include <vector>

#include <boost/type_traits/add_const.hpp> // missing in boost/range/any_range.hpp
#include <boost/range/any_range.hpp>
#include <boost/range/adaptor/type_erased.hpp>

Expand All @@ -43,6 +44,7 @@
#include <gudhi/Multi_persistence/Box.h>
#include <gudhi/Multi_persistence/utils.h>
#include <python_interfaces/numpy_utils.h>
#include <python_interfaces/construction_utils.h>

namespace Gudhi {
namespace multi_persistence {
Expand All @@ -61,7 +63,7 @@ class Module_interface {
using Summand_of_dimension_range =
boost::any_range<Summand_t, boost::forward_traversal_tag, const Summand_t &, std::ptrdiff_t>;
using Tensor1D = nanobind::ndarray<const value_type, nanobind::ndim<1>, nanobind::any_contig>;
using Tensor2D = nanobind::ndarray<const value_type, nanobind::ndim<2>>;
using Tensor2D = nanobind::ndarray<const value_type, nanobind::ndim<2> >;
template <typename IntegerType>
using IntTensor1D = nanobind::ndarray<const IntegerType, nanobind::ndim<1>, nanobind::any_contig>;
using Box_t = std::vector<T>;
Expand Down Expand Up @@ -160,7 +162,7 @@ class Module_interface {
return *this;
}

[[nodiscard]] nanobind::list get_flat_filtration_values(bool unique) const {
[[nodiscard]] nanobind::tuple get_flat_filtration_values(bool unique) const {
int numParam = module_.get_number_of_parameters();
if (numParam == get_null_value<int>()) return {}; // empty module

Expand Down Expand Up @@ -193,15 +195,14 @@ class Module_interface {
}
}

nanobind::list out;
for (auto &vals : values) {
return Gudhi::python::_build_tuple(numParam, [&](std::size_t p) {
auto &vals = values[p];
if (unique) {
std::sort(vals.begin(), vals.end());
vals.erase(std::unique(vals.begin(), vals.end()), vals.end());
}
out.append(_wrap_as_numpy_array(std::move(vals), vals.size()));
}
return out;
return _wrap_as_numpy_array(std::move(vals), vals.size());
});
}

auto get_all_dimension() const {
Expand Down Expand Up @@ -260,9 +261,8 @@ class Module_interface {
return _wrap_as_numpy_array(std::move(corners), 2, dim);
}

nanobind::list get_barcode_from_line(Tensor1D basepoint, std::optional<Tensor1D> direction, int degree) const {
nanobind::tuple get_barcode_from_line(Tensor1D basepoint, std::optional<Tensor1D> direction, int degree) const {
std::vector<std::vector<std::array<double, 2>>> barcode;
nanobind::list out;
{
nanobind::gil_scoped_release release;
Dimension dim = degree < 0 ? get_null_value<Dimension>() : static_cast<Dimension>(degree);
Expand All @@ -276,16 +276,15 @@ class Module_interface {
}
barcode = module_.get_barcode_from_line(line, dim);
}
for (auto &d : barcode) {
out.append(_wrap_as_numpy_array(std::move(d)));
}
return out;

return Gudhi::python::_build_tuple(barcode.size(),
[&](std::size_t d) { return _wrap_as_numpy_array(std::move(barcode[d])); });
}

nanobind::list get_barcode_from_lines(Tensor2D basepoints,
std::optional<Tensor2D> directions,
int degree,
bool keep_inf) const {
nanobind::tuple get_barcode_from_lines(Tensor2D basepoints,
std::optional<Tensor2D> directions,
int degree,
bool keep_inf) const {
std::vector<std::vector<std::array<double, 2>>> barcode;
std::size_t numberOfLines;
{
Expand Down Expand Up @@ -633,71 +632,64 @@ class Module_interface {
return fb;
}

static nanobind::list get_numpy_barcode_from_lines_with_inf(std::vector<std::vector<std::array<double, 2>>> &barcode,
std::size_t numberOfLines) {
nanobind::list out;
static nanobind::tuple get_numpy_barcode_from_lines_with_inf(std::vector<std::vector<std::array<double, 2>>> &barcode,
std::size_t numberOfLines) {
std::vector<std::uint64_t> splits;

if (numberOfLines == 0) {
for (auto &d : barcode) {
if (d.size() != 0) throw std::logic_error("No lines but the barcode is not empty... ?");
out.append(nanobind::make_tuple(_wrap_as_numpy_array(std::move(d), 0, 0, 2),
_wrap_as_numpy_array(std::move(splits), 0)));
}
return out;
return Gudhi::python::_build_tuple(barcode.size(), [&](std::size_t d) {
if (barcode[d].size() != 0) throw std::logic_error("No lines but the barcode is not empty... ?");
return nanobind::make_tuple(_wrap_as_numpy_array(std::move(barcode[d]), 0, 0, 2),
_wrap_as_numpy_array(std::move(splits), 0));
});
}

for (auto &d : barcode) {
if (d.size() % numberOfLines != 0)
return Gudhi::python::_build_tuple(barcode.size(), [&](std::size_t d) {
if (barcode[d].size() % numberOfLines != 0)
throw std::logic_error("Barcodes do not have consistent sizes from a line to another.");
std::size_t numberOfBars = d.size() / numberOfLines;
out.append(nanobind::make_tuple(_wrap_as_numpy_array(std::move(d), numberOfLines, numberOfBars, 2),
_wrap_as_numpy_array(std::move(splits), 0)));
}
return out;
std::size_t numberOfBars = barcode[d].size() / numberOfLines;
return nanobind::make_tuple(_wrap_as_numpy_array(std::move(barcode[d]), numberOfLines, numberOfBars, 2),
_wrap_as_numpy_array(std::move(splits), 0));
});
}

static nanobind::list get_numpy_barcode_from_lines_without_inf(
static nanobind::tuple get_numpy_barcode_from_lines_without_inf(
std::vector<std::vector<std::array<double, 2>>> &barcode,
std::size_t numberOfLines) {
nanobind::list out;

if (numberOfLines == 0) {
for (auto &d : barcode) {
if (d.size() != 0) throw std::logic_error("No lines but the barcode is not empty... ?");
return Gudhi::python::_build_tuple(barcode.size(), [&](std::size_t d) {
if (barcode[d].size() != 0) throw std::logic_error("No lines but the barcode is not empty... ?");
std::vector<std::uint64_t> splits;
out.append(
nanobind::make_tuple(_wrap_as_numpy_array(std::move(d), 0, 2), _wrap_as_numpy_array(std::move(splits), 0)));
}
return out;
return nanobind::make_tuple(_wrap_as_numpy_array(std::move(barcode[d]), 0, 2),
_wrap_as_numpy_array(std::move(splits), 0));
});
}

for (auto &d : barcode) {
if (d.size() % numberOfLines != 0)
return Gudhi::python::_build_tuple(barcode.size(), [&](std::size_t d) {
if (barcode[d].size() % numberOfLines != 0)
throw std::logic_error("Barcodes do not have consistent sizes from a line to another.");
std::vector<std::uint64_t> splits(numberOfLines - 1);
std::size_t barCount = 0;
std::size_t numberOfBars = d.size() / numberOfLines;
std::size_t numberOfBars = barcode[d].size() / numberOfLines;
std::vector<std::array<double, 2>> bars;
for (std::size_t l = 0; l < numberOfLines; ++l) {
for (std::size_t i = 0; i < numberOfBars; ++i) {
auto &b = d[i + (l * numberOfBars)];
auto &b = barcode[d][i + (l * numberOfBars)];
if (b[0] != Module<double>::T_inf) {
bars.push_back(b);
++barCount;
}
}
if (l + 1 < numberOfLines) splits[l] = barCount;
}
out.append(nanobind::make_tuple(_wrap_as_numpy_array(std::move(bars), barCount, 2),
_wrap_as_numpy_array(std::move(splits), numberOfLines - 1)));
}
return out;
return nanobind::make_tuple(_wrap_as_numpy_array(std::move(bars), barCount, 2),
_wrap_as_numpy_array(std::move(splits), numberOfLines - 1));
});
}

static nanobind::list get_numpy_barcode_from_lines(std::vector<std::vector<std::array<double, 2>>> &barcode,
std::size_t numberOfLines,
bool keepInf) {
static nanobind::tuple get_numpy_barcode_from_lines(std::vector<std::vector<std::array<double, 2>>> &barcode,
std::size_t numberOfLines,
bool keepInf) {
if (keepInf) return get_numpy_barcode_from_lines_with_inf(barcode, numberOfLines);
return get_numpy_barcode_from_lines_without_inf(barcode, numberOfLines);
}
Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ include = [
"ext/gudhi-devel/src/Multi_persistence/include/gudhi/Thread_safe_slicer.h",
"ext/gudhi-devel/src/Multi_persistence/include/gudhi/multi_simplex_tree_helpers.h",
"ext/gudhi-devel/src/Multi_persistence/include/gudhi/slicer_helpers.h",
"ext/gudhi-devel/src/Multi_persistence/include/gudhi/multiparameter_module_approximation.h",
"ext/gudhi-devel/src/Persistence_matrix/include/gudhi/Fields/Z2_field_operators.h",
"ext/gudhi-devel/src/Persistence_matrix/include/gudhi/Fields/Zp_field_operators.h",
"ext/gudhi-devel/src/Persistence_matrix/include/gudhi/Matrix.h",
Expand Down Expand Up @@ -370,6 +371,7 @@ include = [
"ext/gudhi-devel/src/common/include/gudhi/reader_utils.h",
"ext/gudhi-devel/src/common/include/gudhi/simple_mdspan.h",
"ext/gudhi-devel/src/python/include/python_interfaces/numpy_utils.h",
"ext/gudhi-devel/src/python/include/python_interfaces/construction_utils.h",
"ext/hera/extern/phat/algorithms/standard_reduction.h",
"ext/hera/extern/phat/algorithms/twist_reduction.h",
"ext/hera/extern/phat/boundary_matrix.h",
Expand Down
16 changes: 12 additions & 4 deletions tests/test_slicer.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,16 +96,24 @@ def test_3():
def test_make_filtration_non_decreasing_propagates_transitively():
slicer_type = mp.Slicer(return_type_only=True, dtype=np.float64)
slicer = slicer_type(
[[], [], [0, 1], [2]],
np.asarray([0, 0, 1, 2], dtype=np.int32),
np.asarray([[0, 0], [0, 0], [1, 1], [5, 5]], dtype=np.float64),
[[], [0, 2], [], [], [5], [1, 6], [2, 3]],
np.asarray([0, 1, 0, 0, 3, 2, 2], dtype=np.int32),
np.asarray(
[[0, 2, 1], [0, 2, 0], [0, 1, 2], [3, 4, 5], [6, 4, 2], [3, 4, 3], [1, 2, 4]],
dtype=np.float64,
),
)

slicer.make_filtration_non_decreasing()

got = np.asarray(slicer.get_filtrations(), dtype=np.float64)
expected = np.asarray([[5, 5], [5, 5], [5, 5], [5, 5]], dtype=np.float64)
expected = np.asarray(
[[0, 2, 1], [0, 2, 2], [0, 1, 2], [3, 4, 5], [6, 4, 5], [3, 4, 5], [3, 4, 5]],
dtype=np.float64,
)
assert np.array_equal(got, expected)


def test_rank_custom():
B = [[], [0], [0], [0], [0]]
F = [[0, 0], [2, 1], [1, 2], [3, 0], [0, 3]]
Expand Down
Loading