multipers 2.2.3__cp311-cp311-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of multipers might be problematic. Click here for more details.

Files changed (189) hide show
  1. multipers/__init__.py +31 -0
  2. multipers/_signed_measure_meta.py +430 -0
  3. multipers/_slicer_meta.py +212 -0
  4. multipers/data/MOL2.py +458 -0
  5. multipers/data/UCR.py +18 -0
  6. multipers/data/__init__.py +1 -0
  7. multipers/data/graphs.py +466 -0
  8. multipers/data/immuno_regions.py +27 -0
  9. multipers/data/minimal_presentation_to_st_bf.py +0 -0
  10. multipers/data/pytorch2simplextree.py +91 -0
  11. multipers/data/shape3d.py +101 -0
  12. multipers/data/synthetic.py +111 -0
  13. multipers/distances.py +198 -0
  14. multipers/filtration_conversions.pxd +229 -0
  15. multipers/filtration_conversions.pxd.tp +84 -0
  16. multipers/filtrations.pxd +224 -0
  17. multipers/function_rips.cp311-win_amd64.pyd +0 -0
  18. multipers/function_rips.pyx +105 -0
  19. multipers/grids.cp311-win_amd64.pyd +0 -0
  20. multipers/grids.pyx +350 -0
  21. multipers/gudhi/Persistence_slices_interface.h +132 -0
  22. multipers/gudhi/Simplex_tree_interface.h +245 -0
  23. multipers/gudhi/Simplex_tree_multi_interface.h +561 -0
  24. multipers/gudhi/cubical_to_boundary.h +59 -0
  25. multipers/gudhi/gudhi/Bitmap_cubical_complex.h +450 -0
  26. multipers/gudhi/gudhi/Bitmap_cubical_complex_base.h +1070 -0
  27. multipers/gudhi/gudhi/Bitmap_cubical_complex_periodic_boundary_conditions_base.h +579 -0
  28. multipers/gudhi/gudhi/Debug_utils.h +45 -0
  29. multipers/gudhi/gudhi/Fields/Multi_field.h +484 -0
  30. multipers/gudhi/gudhi/Fields/Multi_field_operators.h +455 -0
  31. multipers/gudhi/gudhi/Fields/Multi_field_shared.h +450 -0
  32. multipers/gudhi/gudhi/Fields/Multi_field_small.h +531 -0
  33. multipers/gudhi/gudhi/Fields/Multi_field_small_operators.h +507 -0
  34. multipers/gudhi/gudhi/Fields/Multi_field_small_shared.h +531 -0
  35. multipers/gudhi/gudhi/Fields/Z2_field.h +355 -0
  36. multipers/gudhi/gudhi/Fields/Z2_field_operators.h +376 -0
  37. multipers/gudhi/gudhi/Fields/Zp_field.h +420 -0
  38. multipers/gudhi/gudhi/Fields/Zp_field_operators.h +400 -0
  39. multipers/gudhi/gudhi/Fields/Zp_field_shared.h +418 -0
  40. multipers/gudhi/gudhi/Flag_complex_edge_collapser.h +337 -0
  41. multipers/gudhi/gudhi/Matrix.h +2107 -0
  42. multipers/gudhi/gudhi/Multi_critical_filtration.h +1038 -0
  43. multipers/gudhi/gudhi/Multi_persistence/Box.h +171 -0
  44. multipers/gudhi/gudhi/Multi_persistence/Line.h +282 -0
  45. multipers/gudhi/gudhi/Off_reader.h +173 -0
  46. multipers/gudhi/gudhi/One_critical_filtration.h +1431 -0
  47. multipers/gudhi/gudhi/Persistence_matrix/Base_matrix.h +769 -0
  48. multipers/gudhi/gudhi/Persistence_matrix/Base_matrix_with_column_compression.h +686 -0
  49. multipers/gudhi/gudhi/Persistence_matrix/Boundary_matrix.h +842 -0
  50. multipers/gudhi/gudhi/Persistence_matrix/Chain_matrix.h +1350 -0
  51. multipers/gudhi/gudhi/Persistence_matrix/Id_to_index_overlay.h +1105 -0
  52. multipers/gudhi/gudhi/Persistence_matrix/Position_to_index_overlay.h +859 -0
  53. multipers/gudhi/gudhi/Persistence_matrix/RU_matrix.h +910 -0
  54. multipers/gudhi/gudhi/Persistence_matrix/allocators/entry_constructors.h +139 -0
  55. multipers/gudhi/gudhi/Persistence_matrix/base_pairing.h +230 -0
  56. multipers/gudhi/gudhi/Persistence_matrix/base_swap.h +211 -0
  57. multipers/gudhi/gudhi/Persistence_matrix/boundary_cell_position_to_id_mapper.h +60 -0
  58. multipers/gudhi/gudhi/Persistence_matrix/boundary_face_position_to_id_mapper.h +60 -0
  59. multipers/gudhi/gudhi/Persistence_matrix/chain_pairing.h +136 -0
  60. multipers/gudhi/gudhi/Persistence_matrix/chain_rep_cycles.h +190 -0
  61. multipers/gudhi/gudhi/Persistence_matrix/chain_vine_swap.h +616 -0
  62. multipers/gudhi/gudhi/Persistence_matrix/columns/chain_column_extra_properties.h +150 -0
  63. multipers/gudhi/gudhi/Persistence_matrix/columns/column_dimension_holder.h +106 -0
  64. multipers/gudhi/gudhi/Persistence_matrix/columns/column_utilities.h +219 -0
  65. multipers/gudhi/gudhi/Persistence_matrix/columns/entry_types.h +327 -0
  66. multipers/gudhi/gudhi/Persistence_matrix/columns/heap_column.h +1140 -0
  67. multipers/gudhi/gudhi/Persistence_matrix/columns/intrusive_list_column.h +934 -0
  68. multipers/gudhi/gudhi/Persistence_matrix/columns/intrusive_set_column.h +934 -0
  69. multipers/gudhi/gudhi/Persistence_matrix/columns/list_column.h +980 -0
  70. multipers/gudhi/gudhi/Persistence_matrix/columns/naive_vector_column.h +1092 -0
  71. multipers/gudhi/gudhi/Persistence_matrix/columns/row_access.h +192 -0
  72. multipers/gudhi/gudhi/Persistence_matrix/columns/set_column.h +921 -0
  73. multipers/gudhi/gudhi/Persistence_matrix/columns/small_vector_column.h +1093 -0
  74. multipers/gudhi/gudhi/Persistence_matrix/columns/unordered_set_column.h +1012 -0
  75. multipers/gudhi/gudhi/Persistence_matrix/columns/vector_column.h +1244 -0
  76. multipers/gudhi/gudhi/Persistence_matrix/matrix_dimension_holders.h +186 -0
  77. multipers/gudhi/gudhi/Persistence_matrix/matrix_row_access.h +164 -0
  78. multipers/gudhi/gudhi/Persistence_matrix/ru_pairing.h +156 -0
  79. multipers/gudhi/gudhi/Persistence_matrix/ru_rep_cycles.h +376 -0
  80. multipers/gudhi/gudhi/Persistence_matrix/ru_vine_swap.h +540 -0
  81. multipers/gudhi/gudhi/Persistent_cohomology/Field_Zp.h +118 -0
  82. multipers/gudhi/gudhi/Persistent_cohomology/Multi_field.h +173 -0
  83. multipers/gudhi/gudhi/Persistent_cohomology/Persistent_cohomology_column.h +128 -0
  84. multipers/gudhi/gudhi/Persistent_cohomology.h +745 -0
  85. multipers/gudhi/gudhi/Points_off_io.h +171 -0
  86. multipers/gudhi/gudhi/Simple_object_pool.h +69 -0
  87. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_iterators.h +463 -0
  88. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h +83 -0
  89. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_siblings.h +106 -0
  90. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_star_simplex_iterators.h +277 -0
  91. multipers/gudhi/gudhi/Simplex_tree/hooks_simplex_base.h +62 -0
  92. multipers/gudhi/gudhi/Simplex_tree/indexing_tag.h +27 -0
  93. multipers/gudhi/gudhi/Simplex_tree/serialization_utils.h +62 -0
  94. multipers/gudhi/gudhi/Simplex_tree/simplex_tree_options.h +157 -0
  95. multipers/gudhi/gudhi/Simplex_tree.h +2794 -0
  96. multipers/gudhi/gudhi/Simplex_tree_multi.h +163 -0
  97. multipers/gudhi/gudhi/distance_functions.h +62 -0
  98. multipers/gudhi/gudhi/graph_simplicial_complex.h +104 -0
  99. multipers/gudhi/gudhi/persistence_interval.h +253 -0
  100. multipers/gudhi/gudhi/persistence_matrix_options.h +170 -0
  101. multipers/gudhi/gudhi/reader_utils.h +367 -0
  102. multipers/gudhi/mma_interface_coh.h +255 -0
  103. multipers/gudhi/mma_interface_h0.h +231 -0
  104. multipers/gudhi/mma_interface_matrix.h +282 -0
  105. multipers/gudhi/naive_merge_tree.h +575 -0
  106. multipers/gudhi/scc_io.h +289 -0
  107. multipers/gudhi/truc.h +888 -0
  108. multipers/io.cp311-win_amd64.pyd +0 -0
  109. multipers/io.pyx +711 -0
  110. multipers/ml/__init__.py +0 -0
  111. multipers/ml/accuracies.py +90 -0
  112. multipers/ml/convolutions.py +520 -0
  113. multipers/ml/invariants_with_persistable.py +79 -0
  114. multipers/ml/kernels.py +176 -0
  115. multipers/ml/mma.py +714 -0
  116. multipers/ml/one.py +472 -0
  117. multipers/ml/point_clouds.py +346 -0
  118. multipers/ml/signed_measures.py +1589 -0
  119. multipers/ml/sliced_wasserstein.py +461 -0
  120. multipers/ml/tools.py +113 -0
  121. multipers/mma_structures.cp311-win_amd64.pyd +0 -0
  122. multipers/mma_structures.pxd +127 -0
  123. multipers/mma_structures.pyx +2746 -0
  124. multipers/mma_structures.pyx.tp +1085 -0
  125. multipers/multi_parameter_rank_invariant/diff_helpers.h +93 -0
  126. multipers/multi_parameter_rank_invariant/euler_characteristic.h +97 -0
  127. multipers/multi_parameter_rank_invariant/function_rips.h +322 -0
  128. multipers/multi_parameter_rank_invariant/hilbert_function.h +769 -0
  129. multipers/multi_parameter_rank_invariant/persistence_slices.h +148 -0
  130. multipers/multi_parameter_rank_invariant/rank_invariant.h +369 -0
  131. multipers/multiparameter_edge_collapse.py +41 -0
  132. multipers/multiparameter_module_approximation/approximation.h +2295 -0
  133. multipers/multiparameter_module_approximation/combinatory.h +129 -0
  134. multipers/multiparameter_module_approximation/debug.h +107 -0
  135. multipers/multiparameter_module_approximation/euler_curves.h +0 -0
  136. multipers/multiparameter_module_approximation/format_python-cpp.h +286 -0
  137. multipers/multiparameter_module_approximation/heap_column.h +238 -0
  138. multipers/multiparameter_module_approximation/images.h +79 -0
  139. multipers/multiparameter_module_approximation/list_column.h +174 -0
  140. multipers/multiparameter_module_approximation/list_column_2.h +232 -0
  141. multipers/multiparameter_module_approximation/ru_matrix.h +347 -0
  142. multipers/multiparameter_module_approximation/set_column.h +135 -0
  143. multipers/multiparameter_module_approximation/structure_higher_dim_barcode.h +36 -0
  144. multipers/multiparameter_module_approximation/unordered_set_column.h +166 -0
  145. multipers/multiparameter_module_approximation/utilities.h +419 -0
  146. multipers/multiparameter_module_approximation/vector_column.h +223 -0
  147. multipers/multiparameter_module_approximation/vector_matrix.h +331 -0
  148. multipers/multiparameter_module_approximation/vineyards.h +464 -0
  149. multipers/multiparameter_module_approximation/vineyards_trajectories.h +649 -0
  150. multipers/multiparameter_module_approximation.cp311-win_amd64.pyd +0 -0
  151. multipers/multiparameter_module_approximation.pyx +217 -0
  152. multipers/pickle.py +53 -0
  153. multipers/plots.py +334 -0
  154. multipers/point_measure.cp311-win_amd64.pyd +0 -0
  155. multipers/point_measure.pyx +320 -0
  156. multipers/simplex_tree_multi.cp311-win_amd64.pyd +0 -0
  157. multipers/simplex_tree_multi.pxd +133 -0
  158. multipers/simplex_tree_multi.pyx +10335 -0
  159. multipers/simplex_tree_multi.pyx.tp +1935 -0
  160. multipers/slicer.cp311-win_amd64.pyd +0 -0
  161. multipers/slicer.pxd +2371 -0
  162. multipers/slicer.pxd.tp +214 -0
  163. multipers/slicer.pyx +15467 -0
  164. multipers/slicer.pyx.tp +914 -0
  165. multipers/tbb12.dll +0 -0
  166. multipers/tbbbind_2_5.dll +0 -0
  167. multipers/tbbmalloc.dll +0 -0
  168. multipers/tbbmalloc_proxy.dll +0 -0
  169. multipers/tensor/tensor.h +672 -0
  170. multipers/tensor.pxd +13 -0
  171. multipers/test.pyx +44 -0
  172. multipers/tests/__init__.py +57 -0
  173. multipers/tests/test_diff_helper.py +73 -0
  174. multipers/tests/test_hilbert_function.py +82 -0
  175. multipers/tests/test_mma.py +83 -0
  176. multipers/tests/test_point_clouds.py +49 -0
  177. multipers/tests/test_python-cpp_conversion.py +82 -0
  178. multipers/tests/test_signed_betti.py +181 -0
  179. multipers/tests/test_signed_measure.py +89 -0
  180. multipers/tests/test_simplextreemulti.py +221 -0
  181. multipers/tests/test_slicer.py +221 -0
  182. multipers/torch/__init__.py +1 -0
  183. multipers/torch/diff_grids.py +217 -0
  184. multipers/torch/rips_density.py +304 -0
  185. multipers-2.2.3.dist-info/LICENSE +21 -0
  186. multipers-2.2.3.dist-info/METADATA +134 -0
  187. multipers-2.2.3.dist-info/RECORD +189 -0
  188. multipers-2.2.3.dist-info/WHEEL +5 -0
  189. multipers-2.2.3.dist-info/top_level.txt +1 -0
multipers/test.pyx ADDED
@@ -0,0 +1,44 @@
1
+ # cimport multipers.tensor as mt
2
+ from libc.stdint cimport intptr_t, uint16_t
3
+ from libcpp.vector cimport vector
4
+ from libcpp cimport bool, int, float
5
+ from libcpp.utility cimport pair
6
+ from typing import Optional,Iterable,Callable
7
+
8
+
9
+ ctypedef float value_type
10
+ # ctypedef uint16_t index_type
11
+
12
+ import numpy as np
13
+ # cimport numpy as cnp
14
+ # cnp.import_array()
15
+
16
+ # cdef extern from "multi_parameter_rank_invariant/rank_invariant.h" namespace "Gudhi::rank_invariant":
17
+ # void get_hilbert_surface(const intptr_t, mt.static_tensor_view, const vector[index_type], const vector[index_type], index_type, index_type, const vector[index_type], bool, bool) except + nogil
18
+
19
+
20
+ from multipers.simplex_tree_multi import SimplexTreeMulti
21
+
22
+
23
+ def numpy_to_tensor(array:np.ndarray):
24
+ cdef vector[index_type] shape = array.shape
25
+ cdef dtype[::1] contigus_array_view = np.ascontiguousarray(array)
26
+ cdef dtype* dtype_ptr = &contigus_array_view[0]
27
+ cdef mt.static_tensor_view tensor
28
+ with nogil:
29
+ tensor = mt.static_tensor_view(dtype_ptr, shape)
30
+ return tensor.get_resolution()
31
+
32
+ # def hilbert2d(simplextree:SimplexTreeMulti, grid_shape:np.ndarray|list, vector[index_type] degrees, bool mobius_inversion):
33
+ # # assert simplextree.num_parameters == 2
34
+ # cdef intptr_t ptr = simplextree.thisptr
35
+ # cdef vector[index_type] c_grid_shape = grid_shape
36
+ # cdef dtype[::1] container = np.zeros(grid_shape, dtype=np.float32).flatten()
37
+ # cdef dtype* container_ptr = &container[0]
38
+ # cdef mt.static_tensor_view c_container = mt.static_tensor_view(container_ptr, c_grid_shape)
39
+ # cdef index_type i = 0
40
+ # cdef index_type j = 1
41
+ # cdef vector[index_type] fixed_values = [[],[]]
42
+ # # get_hilbert_surface(ptr, c_container, c_grid_shape, degrees,i,j,fixed_values, False, False)
43
+ # return container.reshape(grid_shape)
44
+
@@ -0,0 +1,57 @@
1
+ import numpy as np
2
+
3
+
4
+ def assert_st_simplices(st, dump):
5
+ """
6
+ Checks that the simplextree has the same
7
+ filtration as the dump.
8
+ """
9
+
10
+ assert np.all(
11
+ [
12
+ np.isclose(a, b).all()
13
+ for x, y in zip(st.get_simplices(), dump, strict=True)
14
+ for a, b in zip(x, y, strict=True)
15
+ ]
16
+ )
17
+
18
+
19
+ def sort_sm(sms):
20
+ idx = np.argsort([sm[0][:, 0] for sm in sms])
21
+ return tuple((sm[0][idx], sm[1][idx]) for sm in sms)
22
+
23
+
24
+ def assert_sm_pair(sm1, sm2, exact=True, max_error=1e-3, reg=0.1):
25
+ if not exact:
26
+ from multipers.distances import sm_distance
27
+
28
+ d = sm_distance(sm1, sm2, reg=0.1)
29
+ assert d < max_error, f"Failed comparison:\n{sm1}\n{sm2},\n with distance {d}."
30
+ return
31
+ assert np.all(
32
+ [
33
+ np.isclose(a, b).all()
34
+ for x, y in zip(sm1, sm2, strict=True)
35
+ for a, b in zip(x, y, strict=True)
36
+ ]
37
+ ), f"Failed comparison:\n-----------------\n{sm1}\n-----------------\n{sm2}"
38
+
39
+
40
+ def assert_sm(*args, exact=True, max_error=1e-5, reg=0.1):
41
+ sms = tuple(args)
42
+ for i in range(len(sms) - 1):
43
+ assert_sm_pair(sms[i], sms[i + 1], exact=exact, max_error=max_error, reg=reg)
44
+
45
+
46
+ def random_st(npts=100, num_parameters=2, max_dim=2):
47
+ import gudhi as gd
48
+
49
+ import multipers as mp
50
+ from multipers.data import noisy_annulus
51
+
52
+ x = noisy_annulus(npts // 2, npts - npts // 2, dim=max_dim)
53
+ st = gd.AlphaComplex(points=x).create_simplex_tree()
54
+ st = mp.SimplexTreeMulti(st, num_parameters=num_parameters)
55
+ for p in range(num_parameters):
56
+ st.fill_lowerstar(np.random.uniform(size=npts), p)
57
+ return st
@@ -0,0 +1,73 @@
1
+ import gudhi as gd
2
+ import numpy as np
3
+
4
+ import multipers as mp
5
+
6
+
7
+
8
+ def test_h1_rips_density():
9
+ num_pts = 100
10
+ dim = 2
11
+ pts = np.random.uniform(size=(num_pts, dim))
12
+ weights = np.random.uniform(size=num_pts)
13
+ st = gd.RipsComplex(points=pts).create_simplex_tree()
14
+ st = mp.SimplexTreeMulti(st, num_parameters=2)
15
+ st.fill_lowerstar(weights, parameter=1)
16
+ st.collapse_edges(num=100)
17
+ st.expansion(2)
18
+ # F=st.get_filtration_grid()
19
+ # st.grid_squeeze(F, coordinate_values=True)
20
+ (sm,) = mp.signed_measure(st, degree=1, plot=True, mass_default=None)
21
+ pts, weights = sm
22
+ sm_indices, unmappable_points = st.pts_to_indices(pts, simplices_dimensions=[1, 0])
23
+ assert len(unmappable_points) == 0, "Found unmappable points in Rips edges ?"
24
+ filtration_values = np.array([f for _, f in st.get_simplices()])
25
+ reconstructed_measure = np.asarray(
26
+ [
27
+ [filtration_values[i][parameter] for parameter, i in enumerate(indices)]
28
+ for indices in sm_indices
29
+ ]
30
+ )
31
+ assert np.array_equal(
32
+ reconstructed_measure, pts
33
+ ), "Reconstructed measure is not equal to original measure ?"
34
+
35
+
36
+ def test_h0_rips_density():
37
+ num_pts = 100
38
+ dim = 2
39
+ pts = np.random.uniform(size=(num_pts, dim))
40
+ weights = np.random.uniform(size=num_pts)
41
+ st = gd.RipsComplex(points=pts).create_simplex_tree()
42
+ st = mp.SimplexTreeMulti(st, num_parameters=2)
43
+ st.fill_lowerstar(weights, parameter=1)
44
+ st.collapse_edges(full=True)
45
+ # F=st.get_filtration_grid()
46
+ # st.grid_squeeze(F, coordinate_values=True)
47
+ (sm,) = mp.signed_measure(st, degree=0, plot=True, mass_default=None)
48
+ pts, weights = sm
49
+ _, unmappable_points = st.pts_to_indices(pts, simplices_dimensions=[1, 0])
50
+ assert (
51
+ pts[unmappable_points[:, 0]][:, 0] == 0
52
+ ).all(), "Unmapped points of H0 have to be the nodes of the rips."
53
+
54
+
55
+ # def test_h1_rips_density_rank():
56
+ # num_pts = 100
57
+ # dim=2
58
+ # pts = np.random.uniform(size=(num_pts,dim))
59
+ # weights = np.random.uniform(size=num_pts)
60
+ # st = gd.RipsComplex(points=pts).create_simplex_tree()
61
+ # st = mp.SimplexTreeMulti(st, num_parameters=2)
62
+ # st.fill_lowerstar(weights, parameter=1)
63
+ # st.collapse_edges(full=True)
64
+ # st.expansion(2)
65
+ # # F=st.get_filtration_grid()
66
+ # # st.grid_squeeze(F, coordinate_values=True)
67
+ # sm, = mp.signed_measure(st, degree=1, plot=True, mass_default=None, invariant="rank_invariant", resolution=20, grid_strategy="quantile")
68
+ # pts, weights = sm
69
+ # sm_indices, unmappable_points = signed_measure_indices(st,pts, simplices_dimensions = [1,0])
70
+ # assert len(unmappable_points) == 0, 'Found unmappable points in Rips edges ?'
71
+ # filtration_values = np.array([f for _,f in st.get_simplices()])
72
+ # reconstructed_measure = np.asarray([[filtration_values[i][parameter%2] for parameter,i in enumerate(indices)] for indices in sm_indices])
73
+ # assert np.array_equal(reconstructed_measure, pts), 'Reconstructed measure is not equal to original measure ?'
@@ -0,0 +1,82 @@
1
+ import numpy as np
2
+
3
+ import multipers as mp
4
+ from multipers import signed_measure
5
+
6
+
7
+
8
+
9
+ # def test_1(): # TODO: test integrate_measure instead
10
+ # st = mp.SimplexTreeMulti(num_parameters=3)
11
+ # st.insert([0], [1, 0, 0])
12
+ # st.insert([1], [0, 1, 0])
13
+ # st.insert([2], [0, 0, 1])
14
+ # st.insert([0, 1, 2], [2, 2, 2])
15
+ # st.make_filtration_non_decreasing()
16
+ # st = st.grid_squeeze(grid_strategy="exact")
17
+ # assert np.array_equal(
18
+ # hilbert_surface(st, degrees=[0])[1],
19
+ # np.array(
20
+ # [
21
+ # [
22
+ # [[0, 1, 1], [1, 2, 2], [1, 2, 2]],
23
+ # [[1, 2, 2], [2, 3, 3], [2, 3, 3]],
24
+ # [[1, 2, 2], [2, 3, 3], [2, 3, 1]],
25
+ # ]
26
+ # ]
27
+ # ),
28
+ # )
29
+ # assert np.array_equal(hilbert_surface(st, degrees=[0])[1][0], euler_surface(st)[1])
30
+
31
+
32
+ # def test_2():
33
+ # st = mp.SimplexTreeMulti(num_parameters=4)
34
+ # st.insert([0], [1, 0, 0, 0])
35
+ # st.insert([1], [0, 1, 0, 0])
36
+ # st.insert([2], [0, 0, 1, 0])
37
+ # st.insert([3], [0, 0, 0, 1])
38
+ # st.insert([0, 1, 2, 3], [2, 2, 2, 2])
39
+ # st.make_filtration_non_decreasing()
40
+ # # list(st.get_simplices())
41
+ # st.grid_squeeze(grid_strategy="exact")
42
+ # assert np.array_equal(
43
+ # hilbert_surface(st, degrees=[0])[1][0], (euler_surface(st)[1])
44
+ # )
45
+
46
+
47
+ def test_3():
48
+ st = mp.SimplexTreeMulti(num_parameters=2)
49
+ st.insert([0, 1, 2], [1] * st.num_parameters)
50
+ st.remove_maximal_simplex([0, 1, 2])
51
+ st = st.grid_squeeze(grid_strategy="exact")
52
+ ((a, b),) = mp.signed_measure(st, degrees=[1], mass_default=None)
53
+ assert np.array_equal(a, [[1, 1]]) and np.array_equal(b, [1])
54
+ assert mp.signed_measure(st, degrees=[1], mass_default="inf")[0][1].sum() == 0
55
+
56
+
57
+ def test_4():
58
+ st = mp.SimplexTreeMulti(num_parameters=3)
59
+ st.insert([0], [1, 0, 0])
60
+ st.insert([1], [0, 1, 0])
61
+ st.insert([2], [0, 0, 1])
62
+ st.insert([0, 1, 2], [2, 2, 2])
63
+ st.make_filtration_non_decreasing()
64
+ # list(st.get_simplices())
65
+ st.grid_squeeze(grid_strategy="exact")
66
+ assert signed_measure(st, degrees=[0], mass_default="inf")[0][1].sum() == 0
67
+
68
+
69
+ def test_5():
70
+ num_param = 7
71
+ st = mp.SimplexTreeMulti(num_parameters=num_param)
72
+ for i in range(num_param):
73
+ f = np.zeros(num_param)
74
+ f[i] = 1
75
+ st.insert([i], f)
76
+ st.insert(np.arange(num_param), [2] * num_param)
77
+ assert not st.make_filtration_non_decreasing()
78
+ st.grid_squeeze()
79
+ (a, b), (c, d) = signed_measure(st, degrees=[0, 1])
80
+ assert np.all(a[-1] == 2)
81
+ assert np.sum(b) == 1 and b[-1] == -(num_param - 1)
82
+ assert c.shape == (0, num_param)
@@ -0,0 +1,83 @@
1
+ import numpy as np
2
+ import pytest
3
+
4
+ import multipers as mp
5
+ import multipers.ml.mma as mma
6
+ from multipers.tests import random_st
7
+
8
+
9
+
10
+ def test_1():
11
+ st = mp.SimplexTreeMulti(num_parameters=2)
12
+ st.insert([0], [0, 1])
13
+ st.insert([1], [1, 0])
14
+ st.insert([0, 1], [1, 1])
15
+ mma_pymodule = st.persistence_approximation()
16
+ assert np.array_equal(mma_pymodule[0].get_birth_list(), [[0.0, 1.0], [1.0, 0.0]])
17
+ assert np.array_equal(mma_pymodule[0].get_death_list(), [[np.inf, np.inf]])
18
+
19
+
20
+ def test_img():
21
+ simplextree = mp.SimplexTreeMulti(num_parameters=4)
22
+ simplextree.insert([0], [1, 2, 3, 4])
23
+ mod = simplextree.persistence_approximation(
24
+ box=[[0, 0, 0, 0], [5, 5, 5, 5]], max_error=1.0
25
+ )
26
+ img = mod.representation(resolution=6, kernel="linear")
27
+ assert np.isclose(img[0, 2, 3, 4, 5], 0.5)
28
+ assert np.isclose(img[0, 1, 1, 1, 1], 0)
29
+ assert np.isclose(img[0, 3, 4, 5, 5], 1)
30
+
31
+
32
+ @pytest.mark.parametrize("n_jobs", [1, 2])
33
+ @pytest.mark.parametrize("prune_degrees_above", [0, 1, None])
34
+ def test_pipeline1(prune_degrees_above, n_jobs):
35
+ args = locals()
36
+ st = random_st(npts=50, max_dim=1).collapse_edges(-2)
37
+ st.expansion(2)
38
+ (truc1,) = mma.FilteredComplex2MMA(**args).fit_transform([[st]])[0]
39
+ (truc2,) = mma.FilteredComplex2MMA(**args).fit_transform([[mp.Slicer(st)]])[0]
40
+ box = st.filtration_bounds()
41
+ st_copy = st.copy()
42
+ if prune_degrees_above is not None:
43
+ st_copy.prune_above_dimension(prune_degrees_above)
44
+ output = mp.module_approximation(st_copy, box=box).representation(
45
+ bandwidth=0.1, kernel="linear"
46
+ )
47
+ assert np.sum(output) > 0, "Invalid mma rpz"
48
+ assert np.array_equal(
49
+ truc1.representation(bandwidth=0.1, kernel="linear"),
50
+ truc2.representation(bandwidth=0.1, kernel="linear"),
51
+ ), "Slicer == Simplextree not satisfied"
52
+ assert np.array_equal(truc1.representation(bandwidth=0.1, kernel="linear"), output)
53
+
54
+ st = [random_st(npts=50).collapse_edges(-2, ignore_warning=True) for _ in range(5)]
55
+ some_fited_pipeline = mma.FilteredComplex2MMA(**args).fit([st])
56
+ truc1 = some_fited_pipeline.transform([st])
57
+ truc2 = mma.FilteredComplex2MMA(**args).fit_transform(
58
+ [[mp.Slicer(truc) for truc in st]]
59
+ )
60
+ for a, b in zip(truc1, truc2):
61
+ assert np.array_equal(
62
+ a[0].representation(bandwidth=0.01, kernel="linear"),
63
+ b[0].representation(bandwidth=0.01, kernel="linear"),
64
+ ), "Slicer == Simplextree not satisfied"
65
+
66
+
67
+ @pytest.mark.parametrize("n_jobs", [1, 2])
68
+ @pytest.mark.parametrize("prune_degrees_above", [1, None])
69
+ @pytest.mark.parametrize("expand_dim", [None, 2, 3])
70
+ def test_pipeline2(prune_degrees_above, n_jobs, expand_dim):
71
+ args = locals()
72
+ st = random_st(max_dim=1)
73
+ st.collapse_edges(-2)
74
+ truc = mma.FilteredComplex2MMA(**args).fit_transform([[st]])[0]
75
+ box = st.filtration_bounds()
76
+ st_copy = st.copy()
77
+ # if prune_degrees_above is not None:
78
+ # st_copy.prune_above_dimension(prune_degrees_above)
79
+ if expand_dim is not None:
80
+ st_copy.expansion(expand_dim)
81
+
82
+ output = mp.module_approximation(st_copy, box=box).representation(bandwidth=0.01)
83
+ assert np.array_equal(truc[0].representation(bandwidth=-0.01), output)
@@ -0,0 +1,49 @@
1
+ import numpy as np
2
+
3
+ import multipers as mp
4
+ import pytest
5
+ import platform
6
+ np.random.seed(0)
7
+
8
+
9
+ @pytest.mark.skipif(
10
+ platform.system() == "Windows",
11
+ reason="Detected windows. Pykeops is not compatible with windows yet. Skipping this ftm.",
12
+ )
13
+ def test_throw_test():
14
+ import multipers.ml.point_clouds as mmp
15
+ pts = np.array([[1, 1], [2, 2]], dtype=np.float32)
16
+ st = mmp.PointCloud2FilteredComplex(masses=[0.1]).fit_transform([pts])[0][0]
17
+ assert isinstance(st, mp.simplex_tree_multi.SimplexTreeMulti_type)
18
+ st = mmp.PointCloud2FilteredComplex(bandwidths=[-0.1], complex="alpha").fit_transform(
19
+ [pts]
20
+ )[0][0]
21
+ assert isinstance(st, mp.simplex_tree_multi.SimplexTreeMulti_type)
22
+
23
+ st = mmp.PointCloud2FilteredComplex(masses=[0.1], complex="alpha", output_type="slicer_novine").fit_transform(
24
+ [pts]
25
+ )[0][0]
26
+ assert isinstance(st, mp.slicer.Slicer_type)
27
+ assert st.is_vine is False
28
+
29
+ st = mmp.PointCloud2FilteredComplex(masses=[0.1], complex="alpha", output_type="slicer").fit_transform(
30
+ [pts]
31
+ )[0][0]
32
+ assert isinstance(st, mp.slicer.Slicer_type)
33
+ assert st.is_vine
34
+
35
+ st1, st2 = mmp.PointCloud2FilteredComplex(bandwidths=[0.1], masses=[0.1]).fit_transform(
36
+ [pts]
37
+ )[0]
38
+ assert isinstance(st1, mp.simplex_tree_multi.SimplexTreeMulti_type)
39
+ assert isinstance(st2, mp.simplex_tree_multi.SimplexTreeMulti_type)
40
+ ## ensures it doesn't throw
41
+ assert isinstance(
42
+ mp.module_approximation(st),
43
+ mp.multiparameter_module_approximation.PyModule_type,
44
+ )
45
+ assert mp.signed_measure(st, degree=None, invariant="euler")[0][0].ndim == 2
46
+ assert mp.signed_measure(st, degree=0, invariant="hilbert")[0][0].ndim == 2
47
+ assert mp.signed_measure(st, degree=0, invariant="rank")[0][0].ndim == 2
48
+ assert mp.signed_measure(st, degree=0, invariant="rank")[0][0].shape[1] == 4
49
+
@@ -0,0 +1,82 @@
1
+ import numpy as np
2
+ import multipers as mp
3
+ import gudhi as gd
4
+
5
+
6
+ mp.simplex_tree_multi.SAFE_CONVERSION = False
7
+
8
+
9
+ def test_random_alpha_safe_conversion():
10
+ x = np.random.uniform(size=(200, 2))
11
+ num_parameter = 4
12
+ st_gudhi = gd.AlphaComplex(points=x).create_simplex_tree()
13
+ st_multi = mp.SimplexTreeMulti(
14
+ st_gudhi, num_parameters=num_parameter, safe_conversion=True
15
+ )
16
+ assert (
17
+ np.all([s in st_multi for s, f in st_gudhi.get_simplices()])
18
+ and st_gudhi.num_simplices() == st_multi.num_simplices
19
+ ), "Simplices conversion failed."
20
+ assert np.all(
21
+ [f.shape[0] == num_parameter for _, f in st_multi.get_simplices()]
22
+ ), "Number of parameters is inconcistent"
23
+ assert np.all(
24
+ [np.isclose(st_multi.filtration(s)[0], f) for s, f in st_gudhi.get_simplices()]
25
+ ), "Filtration values conversion failed."
26
+
27
+
28
+ def test_random_alpha_unsafe_conversion():
29
+ x = np.random.uniform(size=(200, 2))
30
+ num_parameter = 4
31
+ st_gudhi = gd.AlphaComplex(points=x).create_simplex_tree()
32
+ st_multi = mp.SimplexTreeMulti(
33
+ st_gudhi, num_parameters=num_parameter, safe_conversion=False
34
+ )
35
+ assert (
36
+ np.all([s in st_multi for s, f in st_gudhi.get_simplices()])
37
+ and st_gudhi.num_simplices() == st_multi.num_simplices
38
+ ), "Simplices conversion failed."
39
+ assert np.all(
40
+ [f.shape[0] == num_parameter for _, f in st_multi.get_simplices()]
41
+ ), "Number of parameters is inconcistent"
42
+ assert np.all(
43
+ [np.isclose(st_multi.filtration(s)[0], f) for s, f in st_gudhi.get_simplices()]
44
+ ), "Filtration values conversion failed."
45
+
46
+
47
+ def test_random_rips_safe_conversion():
48
+ x = np.random.uniform(size=(100, 2))
49
+ num_parameter = 4
50
+ st_gudhi = gd.RipsComplex(points=x).create_simplex_tree()
51
+ st_multi = mp.SimplexTreeMulti(
52
+ st_gudhi, num_parameters=num_parameter, safe_conversion=True
53
+ )
54
+ assert (
55
+ np.all([s in st_multi for s, f in st_gudhi.get_simplices()])
56
+ and st_gudhi.num_simplices() == st_multi.num_simplices
57
+ ), "Simplices conversion failed."
58
+ assert np.all(
59
+ [f.shape[0] == num_parameter for _, f in st_multi.get_simplices()]
60
+ ), "Number of parameters is inconcistent"
61
+ assert np.all(
62
+ [np.isclose(st_multi.filtration(s)[0], f) for s, f in st_gudhi.get_simplices()]
63
+ ), "Filtration values conversion failed."
64
+
65
+
66
+ def test_random_alpha_unsafe_conversion():
67
+ x = np.random.uniform(size=(100, 2))
68
+ num_parameter = 4
69
+ st_gudhi = gd.RipsComplex(points=x).create_simplex_tree()
70
+ st_multi = mp.SimplexTreeMulti(
71
+ st_gudhi, num_parameters=num_parameter, safe_conversion=False
72
+ )
73
+ assert (
74
+ np.all([s in st_multi for s, f in st_gudhi.get_simplices()])
75
+ and st_gudhi.num_simplices() == st_multi.num_simplices
76
+ ), "Simplices conversion failed."
77
+ assert np.all(
78
+ [f.shape[0] == num_parameter for _, f in st_multi.get_simplices()]
79
+ ), "Number of parameters is inconcistent"
80
+ assert np.all(
81
+ [np.isclose(st_multi.filtration(s)[0], f) for s, f in st_gudhi.get_simplices()]
82
+ ), "Filtration values conversion failed."
@@ -0,0 +1,181 @@
1
+ import numpy as np
2
+ from multipers.point_measure import signed_betti, rank_decomposition_by_rectangles
3
+
4
+
5
+ # only tests rank functions with 1 and 2 parameters
6
+ def test_rank_decomposition():
7
+ # rank of an interval module in 1D on a grid with 2 elements
8
+ ri = np.array(
9
+ [
10
+ [
11
+ 1, # 0,0
12
+ 1, # 0,1
13
+ ],
14
+ [0, 1], # 1,0 # 1,1
15
+ ]
16
+ )
17
+ expected_rd = np.array(
18
+ [
19
+ [
20
+ 0, # 0,0
21
+ 1, # 0,1
22
+ ],
23
+ [0, 0], # 1,0 # 1,1
24
+ ]
25
+ )
26
+ rd = rank_decomposition_by_rectangles(ri)
27
+ for i in range(2):
28
+ for i_ in range(i, 2):
29
+ assert rd[i, i_] == expected_rd[i, i_]
30
+
31
+ # rank of a sum of two rectangles in 2D on a grid of 2 elements
32
+ ri = np.array(
33
+ [
34
+ [
35
+ [
36
+ [1, 1], # (0,0), (0,0) # (0,0), (0,1)
37
+ [1, 1], # (0,0), (1,0) # (0,0), (1,1)
38
+ ],
39
+ [
40
+ [0, 1], # (0,1), (0,0) # (0,1), (0,1)
41
+ [0, 1], # (0,1), (1,0) # (0,1), (1,1)
42
+ ],
43
+ ],
44
+ [
45
+ [
46
+ [0, 0], # (1,0), (0,0) # (1,0), (0,1)
47
+ [2, 2], # (1,0), (1,0) # (1,0), (1,1)
48
+ ],
49
+ [
50
+ [0, 0], # (1,1), (0,0) # (1,1), (0,1)
51
+ [0, 2], # (1,1), (1,0) # (1,1), (1,1)
52
+ ],
53
+ ],
54
+ ]
55
+ )
56
+ expected_rd = np.array(
57
+ [
58
+ [
59
+ [
60
+ [0, 0], # (0,0), (0,0) # (0,0), (0,1)
61
+ [0, 1], # (0,0), (1,0) # (0,0), (1,1)
62
+ ],
63
+ [
64
+ [0, 0], # (0,1), (0,0) # (0,1), (0,1)
65
+ [0, 0], # (0,1), (1,0) # (0,1), (1,1)
66
+ ],
67
+ ],
68
+ [
69
+ [
70
+ [0, 0], # (1,0), (0,0) # (1,0), (0,1)
71
+ [0, 1], # (1,0), (1,0) # (1,0), (1,1)
72
+ ],
73
+ [
74
+ [0, 0], # (1,1), (0,0) # (1,1), (0,1)
75
+ [0, 0], # (1,1), (1,0) # (1,1), (1,1)
76
+ ],
77
+ ],
78
+ ]
79
+ )
80
+
81
+ rd = rank_decomposition_by_rectangles(ri)
82
+ for i in range(2):
83
+ for i_ in range(i, 2):
84
+ for j in range(2):
85
+ for j_ in range(j, 2):
86
+ assert rd[i, j, i_, j_] == expected_rd[i, j, i_, j_]
87
+
88
+
89
+ # only tests Hilbert functions with 1, 2, 3, and 4 parameters
90
+ def _test_signed_betti():
91
+ np.random.seed(0)
92
+ N = 4
93
+
94
+ # test 1D
95
+ for _ in range(N):
96
+ a = np.random.randint(10, 30)
97
+
98
+ f = np.random.randint(0, 40, size=(a))
99
+ sb = signed_betti(f)
100
+
101
+ check = np.zeros(f.shape)
102
+ for i in range(f.shape[0]):
103
+ for i_ in range(0, i + 1):
104
+ check[i] += sb[i_]
105
+
106
+ assert np.allclose(check, f)
107
+
108
+ # test 2D
109
+ for _ in range(N):
110
+ a = np.random.randint(10, 30)
111
+ b = np.random.randint(10, 30)
112
+
113
+ f = np.random.randint(0, 40, size=(a, b))
114
+ sb = signed_betti(f)
115
+
116
+ check = np.zeros(f.shape)
117
+ for i in range(f.shape[0]):
118
+ for j in range(f.shape[1]):
119
+ for i_ in range(0, i + 1):
120
+ for j_ in range(0, j + 1):
121
+ check[i, j] += sb[i_, j_]
122
+
123
+ assert np.allclose(check, f)
124
+
125
+ # test 3D
126
+ for _ in range(N):
127
+ a = np.random.randint(10, 20)
128
+ b = np.random.randint(10, 20)
129
+ c = np.random.randint(10, 20)
130
+
131
+ f = np.random.randint(0, 40, size=(a, b, c))
132
+ sb = signed_betti(f)
133
+
134
+ check = np.zeros(f.shape)
135
+ for i in range(f.shape[0]):
136
+ for j in range(f.shape[1]):
137
+ for k in range(f.shape[2]):
138
+ for i_ in range(0, i + 1):
139
+ for j_ in range(0, j + 1):
140
+ for k_ in range(0, k + 1):
141
+ check[i, j, k] += sb[i_, j_, k_]
142
+
143
+ assert np.allclose(check, f)
144
+
145
+ # test 4D
146
+ for _ in range(N):
147
+ a = np.random.randint(5, 10)
148
+ b = np.random.randint(5, 10)
149
+ c = np.random.randint(5, 10)
150
+ d = np.random.randint(5, 10)
151
+
152
+ f = np.random.randint(0, 40, size=(a, b, c, d))
153
+ sb = signed_betti(f)
154
+
155
+ check = np.zeros(f.shape)
156
+ for i in range(f.shape[0]):
157
+ for j in range(f.shape[1]):
158
+ for k in range(f.shape[2]):
159
+ for l in range(f.shape[3]):
160
+ for i_ in range(0, i + 1):
161
+ for j_ in range(0, j + 1):
162
+ for k_ in range(0, k + 1):
163
+ for l_ in range(0, l + 1):
164
+ check[i, j, k, l] += sb[i_, j_, k_, l_]
165
+
166
+ assert np.allclose(check, f)
167
+
168
+ for threshold in [True, False]:
169
+ for _ in range(N):
170
+ a = np.random.randint(5, 10)
171
+ b = np.random.randint(5, 10)
172
+ c = np.random.randint(5, 10)
173
+ d = np.random.randint(5, 10)
174
+ e = np.random.randint(5, 10)
175
+ f = np.random.randint(5, 10)
176
+
177
+ f = np.random.randint(0, 40, size=(a, b, c, d,e,f))
178
+ sb = signed_betti(f, threshold=threshold)
179
+ sb_ = signed_betti(f, threshold=threshold)
180
+
181
+ assert np.allclose(sb, sb_)