multipers 2.4.0b1__cp312-cp312-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. multipers/.dylibs/libboost_timer.dylib +0 -0
  2. multipers/.dylibs/libc++.1.0.dylib +0 -0
  3. multipers/.dylibs/libtbb.12.17.dylib +0 -0
  4. multipers/__init__.py +33 -0
  5. multipers/_signed_measure_meta.py +426 -0
  6. multipers/_slicer_meta.py +231 -0
  7. multipers/array_api/__init__.py +62 -0
  8. multipers/array_api/numpy.py +124 -0
  9. multipers/array_api/torch.py +133 -0
  10. multipers/data/MOL2.py +458 -0
  11. multipers/data/UCR.py +18 -0
  12. multipers/data/__init__.py +1 -0
  13. multipers/data/graphs.py +466 -0
  14. multipers/data/immuno_regions.py +27 -0
  15. multipers/data/minimal_presentation_to_st_bf.py +0 -0
  16. multipers/data/pytorch2simplextree.py +91 -0
  17. multipers/data/shape3d.py +101 -0
  18. multipers/data/synthetic.py +113 -0
  19. multipers/distances.py +202 -0
  20. multipers/filtration_conversions.pxd +736 -0
  21. multipers/filtration_conversions.pxd.tp +226 -0
  22. multipers/filtrations/__init__.py +21 -0
  23. multipers/filtrations/density.py +529 -0
  24. multipers/filtrations/filtrations.py +480 -0
  25. multipers/filtrations.pxd +534 -0
  26. multipers/filtrations.pxd.tp +332 -0
  27. multipers/function_rips.cpython-312-darwin.so +0 -0
  28. multipers/function_rips.pyx +104 -0
  29. multipers/grids.cpython-312-darwin.so +0 -0
  30. multipers/grids.pyx +538 -0
  31. multipers/gudhi/Persistence_slices_interface.h +213 -0
  32. multipers/gudhi/Simplex_tree_interface.h +274 -0
  33. multipers/gudhi/Simplex_tree_multi_interface.h +648 -0
  34. multipers/gudhi/gudhi/Bitmap_cubical_complex.h +450 -0
  35. multipers/gudhi/gudhi/Bitmap_cubical_complex_base.h +1070 -0
  36. multipers/gudhi/gudhi/Bitmap_cubical_complex_periodic_boundary_conditions_base.h +579 -0
  37. multipers/gudhi/gudhi/Debug_utils.h +52 -0
  38. multipers/gudhi/gudhi/Degree_rips_bifiltration.h +2307 -0
  39. multipers/gudhi/gudhi/Dynamic_multi_parameter_filtration.h +2524 -0
  40. multipers/gudhi/gudhi/Fields/Multi_field.h +453 -0
  41. multipers/gudhi/gudhi/Fields/Multi_field_operators.h +460 -0
  42. multipers/gudhi/gudhi/Fields/Multi_field_shared.h +444 -0
  43. multipers/gudhi/gudhi/Fields/Multi_field_small.h +584 -0
  44. multipers/gudhi/gudhi/Fields/Multi_field_small_operators.h +490 -0
  45. multipers/gudhi/gudhi/Fields/Multi_field_small_shared.h +580 -0
  46. multipers/gudhi/gudhi/Fields/Z2_field.h +391 -0
  47. multipers/gudhi/gudhi/Fields/Z2_field_operators.h +389 -0
  48. multipers/gudhi/gudhi/Fields/Zp_field.h +493 -0
  49. multipers/gudhi/gudhi/Fields/Zp_field_operators.h +384 -0
  50. multipers/gudhi/gudhi/Fields/Zp_field_shared.h +492 -0
  51. multipers/gudhi/gudhi/Flag_complex_edge_collapser.h +337 -0
  52. multipers/gudhi/gudhi/Matrix.h +2200 -0
  53. multipers/gudhi/gudhi/Multi_filtration/Multi_parameter_generator.h +1712 -0
  54. multipers/gudhi/gudhi/Multi_filtration/multi_filtration_conversions.h +237 -0
  55. multipers/gudhi/gudhi/Multi_filtration/multi_filtration_utils.h +225 -0
  56. multipers/gudhi/gudhi/Multi_parameter_filtered_complex.h +485 -0
  57. multipers/gudhi/gudhi/Multi_parameter_filtration.h +2643 -0
  58. multipers/gudhi/gudhi/Multi_persistence/Box.h +233 -0
  59. multipers/gudhi/gudhi/Multi_persistence/Line.h +309 -0
  60. multipers/gudhi/gudhi/Multi_persistence/Multi_parameter_filtered_complex_pcoh_interface.h +268 -0
  61. multipers/gudhi/gudhi/Multi_persistence/Persistence_interface_cohomology.h +159 -0
  62. multipers/gudhi/gudhi/Multi_persistence/Persistence_interface_matrix.h +463 -0
  63. multipers/gudhi/gudhi/Multi_persistence/Point.h +853 -0
  64. multipers/gudhi/gudhi/Off_reader.h +173 -0
  65. multipers/gudhi/gudhi/Persistence_matrix/Base_matrix.h +834 -0
  66. multipers/gudhi/gudhi/Persistence_matrix/Base_matrix_with_column_compression.h +838 -0
  67. multipers/gudhi/gudhi/Persistence_matrix/Boundary_matrix.h +833 -0
  68. multipers/gudhi/gudhi/Persistence_matrix/Chain_matrix.h +1367 -0
  69. multipers/gudhi/gudhi/Persistence_matrix/Id_to_index_overlay.h +1157 -0
  70. multipers/gudhi/gudhi/Persistence_matrix/Position_to_index_overlay.h +869 -0
  71. multipers/gudhi/gudhi/Persistence_matrix/RU_matrix.h +905 -0
  72. multipers/gudhi/gudhi/Persistence_matrix/allocators/entry_constructors.h +122 -0
  73. multipers/gudhi/gudhi/Persistence_matrix/base_pairing.h +260 -0
  74. multipers/gudhi/gudhi/Persistence_matrix/base_swap.h +288 -0
  75. multipers/gudhi/gudhi/Persistence_matrix/chain_pairing.h +170 -0
  76. multipers/gudhi/gudhi/Persistence_matrix/chain_rep_cycles.h +247 -0
  77. multipers/gudhi/gudhi/Persistence_matrix/chain_vine_swap.h +571 -0
  78. multipers/gudhi/gudhi/Persistence_matrix/columns/chain_column_extra_properties.h +182 -0
  79. multipers/gudhi/gudhi/Persistence_matrix/columns/column_dimension_holder.h +130 -0
  80. multipers/gudhi/gudhi/Persistence_matrix/columns/column_utilities.h +235 -0
  81. multipers/gudhi/gudhi/Persistence_matrix/columns/entry_types.h +312 -0
  82. multipers/gudhi/gudhi/Persistence_matrix/columns/heap_column.h +1092 -0
  83. multipers/gudhi/gudhi/Persistence_matrix/columns/intrusive_list_column.h +923 -0
  84. multipers/gudhi/gudhi/Persistence_matrix/columns/intrusive_set_column.h +914 -0
  85. multipers/gudhi/gudhi/Persistence_matrix/columns/list_column.h +930 -0
  86. multipers/gudhi/gudhi/Persistence_matrix/columns/naive_vector_column.h +1071 -0
  87. multipers/gudhi/gudhi/Persistence_matrix/columns/row_access.h +203 -0
  88. multipers/gudhi/gudhi/Persistence_matrix/columns/set_column.h +886 -0
  89. multipers/gudhi/gudhi/Persistence_matrix/columns/unordered_set_column.h +984 -0
  90. multipers/gudhi/gudhi/Persistence_matrix/columns/vector_column.h +1213 -0
  91. multipers/gudhi/gudhi/Persistence_matrix/index_mapper.h +58 -0
  92. multipers/gudhi/gudhi/Persistence_matrix/matrix_dimension_holders.h +227 -0
  93. multipers/gudhi/gudhi/Persistence_matrix/matrix_row_access.h +200 -0
  94. multipers/gudhi/gudhi/Persistence_matrix/ru_pairing.h +166 -0
  95. multipers/gudhi/gudhi/Persistence_matrix/ru_rep_cycles.h +319 -0
  96. multipers/gudhi/gudhi/Persistence_matrix/ru_vine_swap.h +562 -0
  97. multipers/gudhi/gudhi/Persistence_on_a_line.h +152 -0
  98. multipers/gudhi/gudhi/Persistence_on_rectangle.h +617 -0
  99. multipers/gudhi/gudhi/Persistent_cohomology/Field_Zp.h +118 -0
  100. multipers/gudhi/gudhi/Persistent_cohomology/Multi_field.h +173 -0
  101. multipers/gudhi/gudhi/Persistent_cohomology/Persistent_cohomology_column.h +128 -0
  102. multipers/gudhi/gudhi/Persistent_cohomology.h +769 -0
  103. multipers/gudhi/gudhi/Points_off_io.h +171 -0
  104. multipers/gudhi/gudhi/Projective_cover_kernel.h +379 -0
  105. multipers/gudhi/gudhi/Simple_object_pool.h +69 -0
  106. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_iterators.h +559 -0
  107. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h +83 -0
  108. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_siblings.h +121 -0
  109. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_star_simplex_iterators.h +277 -0
  110. multipers/gudhi/gudhi/Simplex_tree/filtration_value_utils.h +155 -0
  111. multipers/gudhi/gudhi/Simplex_tree/hooks_simplex_base.h +62 -0
  112. multipers/gudhi/gudhi/Simplex_tree/indexing_tag.h +27 -0
  113. multipers/gudhi/gudhi/Simplex_tree/serialization_utils.h +60 -0
  114. multipers/gudhi/gudhi/Simplex_tree/simplex_tree_options.h +105 -0
  115. multipers/gudhi/gudhi/Simplex_tree.h +3170 -0
  116. multipers/gudhi/gudhi/Slicer.h +848 -0
  117. multipers/gudhi/gudhi/Thread_safe_slicer.h +393 -0
  118. multipers/gudhi/gudhi/distance_functions.h +62 -0
  119. multipers/gudhi/gudhi/graph_simplicial_complex.h +104 -0
  120. multipers/gudhi/gudhi/multi_simplex_tree_helpers.h +147 -0
  121. multipers/gudhi/gudhi/persistence_interval.h +263 -0
  122. multipers/gudhi/gudhi/persistence_matrix_options.h +188 -0
  123. multipers/gudhi/gudhi/reader_utils.h +367 -0
  124. multipers/gudhi/gudhi/simple_mdspan.h +484 -0
  125. multipers/gudhi/gudhi/slicer_helpers.h +779 -0
  126. multipers/gudhi/tmp_h0_pers/mma_interface_h0.h +223 -0
  127. multipers/gudhi/tmp_h0_pers/naive_merge_tree.h +536 -0
  128. multipers/io.cpython-312-darwin.so +0 -0
  129. multipers/io.pyx +472 -0
  130. multipers/ml/__init__.py +0 -0
  131. multipers/ml/accuracies.py +90 -0
  132. multipers/ml/invariants_with_persistable.py +79 -0
  133. multipers/ml/kernels.py +176 -0
  134. multipers/ml/mma.py +713 -0
  135. multipers/ml/one.py +472 -0
  136. multipers/ml/point_clouds.py +352 -0
  137. multipers/ml/signed_measures.py +1667 -0
  138. multipers/ml/sliced_wasserstein.py +461 -0
  139. multipers/ml/tools.py +113 -0
  140. multipers/mma_structures.cpython-312-darwin.so +0 -0
  141. multipers/mma_structures.pxd +134 -0
  142. multipers/mma_structures.pyx +1483 -0
  143. multipers/mma_structures.pyx.tp +1126 -0
  144. multipers/multi_parameter_rank_invariant/diff_helpers.h +85 -0
  145. multipers/multi_parameter_rank_invariant/euler_characteristic.h +95 -0
  146. multipers/multi_parameter_rank_invariant/function_rips.h +317 -0
  147. multipers/multi_parameter_rank_invariant/hilbert_function.h +761 -0
  148. multipers/multi_parameter_rank_invariant/persistence_slices.h +149 -0
  149. multipers/multi_parameter_rank_invariant/rank_invariant.h +350 -0
  150. multipers/multiparameter_edge_collapse.py +41 -0
  151. multipers/multiparameter_module_approximation/approximation.h +2541 -0
  152. multipers/multiparameter_module_approximation/debug.h +107 -0
  153. multipers/multiparameter_module_approximation/format_python-cpp.h +292 -0
  154. multipers/multiparameter_module_approximation/utilities.h +428 -0
  155. multipers/multiparameter_module_approximation.cpython-312-darwin.so +0 -0
  156. multipers/multiparameter_module_approximation.pyx +286 -0
  157. multipers/ops.cpython-312-darwin.so +0 -0
  158. multipers/ops.pyx +231 -0
  159. multipers/pickle.py +89 -0
  160. multipers/plots.py +550 -0
  161. multipers/point_measure.cpython-312-darwin.so +0 -0
  162. multipers/point_measure.pyx +409 -0
  163. multipers/simplex_tree_multi.cpython-312-darwin.so +0 -0
  164. multipers/simplex_tree_multi.pxd +136 -0
  165. multipers/simplex_tree_multi.pyx +11719 -0
  166. multipers/simplex_tree_multi.pyx.tp +2102 -0
  167. multipers/slicer.cpython-312-darwin.so +0 -0
  168. multipers/slicer.pxd +2097 -0
  169. multipers/slicer.pxd.tp +263 -0
  170. multipers/slicer.pyx +13042 -0
  171. multipers/slicer.pyx.tp +1259 -0
  172. multipers/tensor/tensor.h +672 -0
  173. multipers/tensor.pxd +13 -0
  174. multipers/test.pyx +44 -0
  175. multipers/tests/__init__.py +70 -0
  176. multipers/torch/__init__.py +1 -0
  177. multipers/torch/diff_grids.py +240 -0
  178. multipers/torch/rips_density.py +310 -0
  179. multipers/vector_interface.pxd +46 -0
  180. multipers-2.4.0b1.dist-info/METADATA +131 -0
  181. multipers-2.4.0b1.dist-info/RECORD +184 -0
  182. multipers-2.4.0b1.dist-info/WHEEL +6 -0
  183. multipers-2.4.0b1.dist-info/licenses/LICENSE +21 -0
  184. multipers-2.4.0b1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,286 @@
1
+ """!
2
+ @package mma
3
+ @brief Files containing the C++ cythonized functions.
4
+ @author David Loiseaux
5
+ @copyright Copyright (c) 2022 Inria.
6
+ """
7
+
8
+ # distutils: language = c++
9
+
10
+ ###########################################################################
11
+ ## PYTHON LIBRARIES
12
+ import gudhi as gd
13
+ import numpy as np
14
+ from typing import List
15
+ from joblib import Parallel, delayed
16
+ import sys
17
+ from warnings import warn
18
+
19
+ ###########################################################################
20
+ ## CPP CLASSES
21
+ from libc.stdint cimport intptr_t
22
+ from libc.stdint cimport uintptr_t
23
+
24
+ ###########################################################################
25
+ ## CYTHON TYPES
26
+ from libcpp.vector cimport vector
27
+ from libcpp.utility cimport pair
28
+ #from libcpp.list cimport list as clist
29
+ from libcpp cimport bool
30
+ from libcpp cimport int
31
+ from typing import Iterable,Optional, Literal
32
+ from cython.operator import dereference
33
+ #########################################################################
34
+ ## Multipersistence Module Approximation Classes
35
+ from multipers.mma_structures cimport *
36
+ from multipers.filtrations cimport *
37
+ from multipers.filtration_conversions cimport *
38
+ cimport numpy as cnp
39
+
40
+
41
+ #########################################################################
42
+ ## Small hack for typing
43
+ from multipers.simplex_tree_multi import is_simplextree_multi, SimplexTreeMulti_type
44
+ from multipers.slicer import Slicer_type, is_slicer
45
+ from multipers.mma_structures import *
46
+ from multipers.mma_structures import PyModule_type
47
+ from typing import Union
48
+ from multipers.slicer cimport _multiparameter_module_approximation_f32, _multiparameter_module_approximation_f64
49
+
50
+
51
+
52
+ def module_approximation_from_slicer(
53
+ slicer:Slicer_type,
54
+ box:Optional[np.ndarray]=None,
55
+ max_error=-1,
56
+ bool complete=True,
57
+ bool threshold=False,
58
+ bool verbose=False,
59
+ list[float] direction = [],
60
+ bool warnings = True,
61
+ unsqueeze_grid = None,
62
+ )->PyModule_type:
63
+
64
+ cdef Module[float] mod_f32
65
+ cdef Module[double] mod_f64
66
+ cdef intptr_t ptr
67
+ if not slicer.is_vine:
68
+ if warnings:
69
+ warn(r"(copy warning) Got a non-vine slicer as an input. Use `vineyard=True` to remove this copy.")
70
+ from multipers._slicer_meta import Slicer
71
+ slicer = Slicer(slicer, vineyard=True, backend="matrix")
72
+ # if slicer.is_squeezed and unsqueeze_grid not None:
73
+ # raise ValueError("Got a squeezed slicer. Should have been unsqueezed before !")
74
+
75
+ direction_ = np.asarray(direction, dtype=slicer.dtype)
76
+ if slicer.dtype == np.float32:
77
+ approx_mod = PyModule_f32()
78
+ if box is None:
79
+ box = slicer.filtration_bounds()
80
+ mod_f32 = _multiparameter_module_approximation_f32(slicer,_py2p_f32(direction_), max_error,Box[float](box),threshold, complete, verbose)
81
+ ptr = <intptr_t>(&mod_f32)
82
+ elif slicer.dtype == np.float64:
83
+ approx_mod = PyModule_f64()
84
+ if box is None:
85
+ box = slicer.filtration_bounds()
86
+ mod_f64 = _multiparameter_module_approximation_f64(slicer,_py2p_f64(direction_), max_error,Box[double](box),threshold, complete, verbose)
87
+ ptr = <intptr_t>(&mod_f64)
88
+ else:
89
+ raise ValueError(f"Slicer must be float-like. Got {slicer.dtype}.")
90
+
91
+ approx_mod._set_from_ptr(ptr)
92
+
93
+ if unsqueeze_grid is not None:
94
+ if verbose:
95
+ print("Reevaluating module in filtration grid...",end="", flush=True)
96
+ approx_mod.evaluate_in_grid(unsqueeze_grid)
97
+ from multipers.grids import compute_bounding_box
98
+ if len(approx_mod):
99
+ approx_mod.set_box(compute_bounding_box(approx_mod))
100
+ if verbose:
101
+ print("Done.",flush=True)
102
+
103
+ return approx_mod
104
+
105
+ def module_approximation(
106
+ input:Union[SimplexTreeMulti_type,Slicer_type, tuple],
107
+ box:Optional[np.ndarray]=None,
108
+ double max_error=-1,
109
+ int nlines=557,
110
+ bool from_coordinates = False,
111
+ bool complete=True,
112
+ bool threshold=False,
113
+ bool verbose=False,
114
+ bool ignore_warnings=False,
115
+ vector[double] direction = [],
116
+ vector[int] swap_box_coords = [],
117
+ *,
118
+ int n_jobs = -1,
119
+ )->PyModule_type:
120
+ """Computes an interval module approximation of a multiparameter filtration.
121
+
122
+ Parameters
123
+ ----------
124
+ input: SimplexTreeMulti or Slicer-like.
125
+ Holds the multifiltered complex.
126
+ max_error: positive float
127
+ Trade-off between approximation and computational complexity.
128
+ Upper bound of the module approximation, in bottleneck distance,
129
+ for interval-decomposable modules.
130
+ nlines: int = 557
131
+ Alternative to max_error;
132
+ specifies the number of persistence computation used for the approximation.
133
+ box : (Optional) pair of list of floats
134
+ Defines a rectangle on which to compute the approximation.
135
+ Format : [x,y], This defines a rectangle on which we draw the lines,
136
+ uniformly drawn (with a max_error step).
137
+ The first line is `x`.
138
+ **Warning**: For custom boxes, and directions, you **must** ensure
139
+ that the first line captures a generic barcode.
140
+ direction: float[:] = []
141
+ If given, the line are drawn with this angle.
142
+ **Warning**: You must ensure that the first line, defined by box,
143
+ captures a generic barcode.
144
+ threshold: bool
145
+ When true, intersects the module support with the box,
146
+ i.e. no more infinite summands.
147
+ verbose: bool
148
+ Prints C++ infos.
149
+ ignore_warning : bool
150
+ Unless set to true, prevents computing on more than 10k lines.
151
+ Useful to prevent a segmentation fault due to "infinite" recursion.
152
+ Returns
153
+ -------
154
+ PyModule
155
+ An interval decomposable module approximation of the module defined by the
156
+ homology of this multi-filtration.
157
+ """
158
+ if isinstance(input, tuple) or isinstance(input, list):
159
+
160
+ assert all(is_slicer(s) and (s.is_minpres or len(s)==0) for s in input), "Modules cannot be merged unless they are minimal presentations."
161
+
162
+ assert (np.unique([s.minpres_degree for s in input if len(s)], return_counts=True)[1] <=1).all(), "Multiple modules are at the same degree, cannot merge modules"
163
+ if len(input) == 0:
164
+ return PyModule_f64()
165
+ modules = tuple(Parallel(n_jobs=n_jobs, prefer="threads")(
166
+ delayed(module_approximation)(
167
+ input=slicer,
168
+ box=box,
169
+ max_error=max_error,
170
+ nlines=nlines,
171
+ from_coordinates=from_coordinates,
172
+ complete=complete,
173
+ threshold=threshold,
174
+ verbose=verbose,
175
+ ignore_warnings=ignore_warnings,
176
+ direction=direction,
177
+ swap_box_coords=swap_box_coords,
178
+ )
179
+ for slicer in input
180
+ ))
181
+ box = np.array([
182
+ np.min([m.get_box()[0] for m in modules if len(m)], axis=0),
183
+ np.max([m.get_box()[1] for m in modules if len(m)], axis=0),
184
+ ])
185
+ mod = PyModule_f64().set_box(box)
186
+ for i,m in enumerate(modules):
187
+ mod.merge(m, input[i].minpres_degree)
188
+ return mod
189
+ if len(input) == 0:
190
+ if verbose:
191
+ print("Empty input, returning the trivial module.")
192
+ return PyModule_f64()
193
+
194
+
195
+ cdef bool is_degenerate=False
196
+ for i in range(direction.size()):
197
+ if direction[i]<0:
198
+ raise ValueError(f"Got an invalid negative direction. {direction=}")
199
+ if direction[i] == 0:
200
+ is_degenerate=True
201
+ if is_degenerate and not ignore_warnings:
202
+ warn("Got a degenerate direction. This function may fail if the first line is not generic.")
203
+
204
+ if from_coordinates and not input.is_squeezed:
205
+ input = input.grid_squeeze()
206
+ unsqueeze_grid = None
207
+ if input.is_squeezed:
208
+ if not ignore_warnings:
209
+ warn("(copy warning) Got a squeezed input. ")
210
+ if verbose:
211
+ print("Preparing filtration (unsqueeze)... ",end="", flush=True)
212
+ if from_coordinates:
213
+ from multipers.grids import sanitize_grid
214
+ unsqueeze_grid = sanitize_grid(input.filtration_grid, numpyfy=True, add_inf=True)
215
+ input = input.astype(dtype=np.float64)
216
+ if direction.size() == 0:
217
+ _direction = np.asarray([1/g.size for g in unsqueeze_grid], dtype=np.float64)
218
+ _direction /= np.sqrt((_direction**2).sum())
219
+ direction = _direction
220
+ if verbose:
221
+ print(f"Updated `{direction=}`, and `{max_error=}` ",end="")
222
+
223
+ else:
224
+ input = input.unsqueeze()
225
+ if verbose:
226
+ print("Done.", flush=True)
227
+
228
+
229
+
230
+ if box is None:
231
+ if verbose:
232
+ print("No box given. Using filtration bounds to infer it.")
233
+ box = input.filtration_bounds()
234
+ if verbose:
235
+ print(f"Using {box=}.",flush=True)
236
+
237
+ box = np.asarray(box)
238
+ if box.ndim !=2:
239
+ raise ValueError(f"Invalid box dimension. Got {box.ndim=} != 2")
240
+ # empty coords
241
+ zero_idx = box[1] == box[0]
242
+ if np.any(zero_idx):
243
+ if not ignore_warnings:
244
+ warn(f"Got {(box[1] == box[0])=} trivial box coordinates.")
245
+ box[1] += zero_idx
246
+
247
+ for i in swap_box_coords:
248
+ box[[0,1],i] = box[[1,0],i]
249
+ num_parameters = box.shape[1]
250
+ if num_parameters <=0:
251
+ num_parameters = box.shape[1]
252
+ assert direction.size() == 0 or direction.size() == box[0].size, f"Invalid line direction size, has to be 0 or {num_parameters=}"
253
+
254
+ prod = sum(np.abs(box[1] - box[0])[:i].prod() * np.abs(box[1] - box[0])[i+1:].prod() for i in range(0,num_parameters) if (direction.size() ==0 or direction[i]!=0))
255
+
256
+ if max_error <= 0:
257
+ max_error = (prod/nlines)**(1/(num_parameters-1))
258
+
259
+ estimated_nlines = prod/(max_error**(num_parameters -1))
260
+ if not ignore_warnings and estimated_nlines >= 10_000:
261
+ raise ValueError(f"""
262
+ Warning : the number of lines (around {np.round(estimated_nlines)}) may be too high.
263
+ This may be due to extreme box or filtration bounds :
264
+
265
+ {box=}
266
+
267
+ Try to increase the precision parameter, or set `ignore_warnings=True` to compute this module.
268
+ Returning the trivial module.
269
+ """
270
+ )
271
+ if is_simplextree_multi(input):
272
+ from multipers._slicer_meta import Slicer
273
+ input = Slicer(input,backend="matrix", vineyard=True)
274
+ if not is_slicer(input):
275
+ raise ValueError("First argument must be a simplextree or a slicer !")
276
+ return module_approximation_from_slicer(
277
+ slicer=input,
278
+ box=box,
279
+ max_error=max_error,
280
+ complete=complete,
281
+ threshold=threshold,
282
+ verbose=verbose,
283
+ direction=direction,
284
+ unsqueeze_grid=unsqueeze_grid,
285
+ )
286
+
Binary file
multipers/ops.pyx ADDED
@@ -0,0 +1,231 @@
1
+ import cython
2
+ from multipers.vector_interface cimport *
3
+ import numpy as np
4
+ import multipers as mp
5
+
6
+ @cython.boundscheck(False)
7
+ @cython.wraparound(False)
8
+ cdef vector[pair[double, double]] array_view_to_vect_pair(double[:, :] arr_view) noexcept nogil:
9
+ cdef int n = arr_view.shape[0]
10
+ cdef vector[pair[double, double]] result_vector
11
+ result_vector.resize(n)
12
+ for i in range(n):
13
+ result_vector[i] = pair[double, double](arr_view[i, 0], arr_view[i, 1])
14
+ return result_vector
15
+
16
+ def _aida(col_degrees, row_degrees, matrix):
17
+ cdef multipers_interface_input stuff = multipers_interface_input(col_degrees, row_degrees, matrix)
18
+ cdef AIDA_functor truc
19
+ truc.config.show_info = True
20
+ truc.config.sort_output = True
21
+ truc.config.sort = True
22
+ cdef multipers_interface_output stuff2 = truc.multipers_interface(stuff)
23
+ out = []
24
+ for i in range(stuff2.summands.size()):
25
+ out.append((stuff2.summands[i].col_degrees, stuff2.summands[i].row_degrees, stuff2.summands[i].matrix))
26
+ return out
27
+
28
+ def aida(s, bool sort=True, bool verbose=False, bool progress = False):
29
+ """
30
+ Decomposes (a minimal presentation of a) 2-parameter persistence module as
31
+ a direct sum of indecomposables.
32
+
33
+ From [Decomposing Multiparameter Persistence Modules](https://doi.org/10.4230/LIPIcs.SoCG.2025.41).
34
+
35
+ Parameters:
36
+ - s : The slicer to reduce. Has to be a minimal presentation.
37
+ - verbose : shows log.
38
+ - progress : shows a progress bar
39
+ - sort : sorts the input first in a colexical order (debug)
40
+ """
41
+
42
+ from multipers.slicer import is_slicer
43
+ if not is_slicer(s):
44
+ raise ValueError(f"Input has to be a slicer. Got {type(s)=}.")
45
+ if not s.is_minpres:
46
+ raise ValueError(f"AIDA takes a minimal presentation as an input. Got {s.minpres_degree=}.")
47
+ if s.num_parameters != 2 or not s.is_minpres:
48
+ raise ValueError(f"AIDA is only compatible with 2-parameter minimal presentations. Got {s.num_parameters=} and {s.is_minpres=}.")
49
+ cdef bool is_squeezed = s.is_squeezed
50
+
51
+ cdef int degree = s.minpres_degree
52
+ if sort:
53
+ s = s.to_colexical()
54
+ F = np.asarray(s.get_filtrations())
55
+ D = s.get_dimensions()
56
+ cdef double[:,:] row_degree_ = np.asarray(F[D==degree], dtype = np.float64)
57
+ cdef double[:,:] col_degree_ = np.asarray(F[D==degree+1], dtype = np.float64)
58
+ cdef vector[pair[double,double]] row_degree = array_view_to_vect_pair(row_degree_)
59
+ cdef vector[pair[double,double]] col_degree = array_view_to_vect_pair(col_degree_)
60
+ i,j = np.searchsorted(D, [degree+1,degree+2])
61
+ cdef vector[vector[int]] matrix = s.get_boundaries()[i:j]
62
+
63
+ cdef AIDA_functor truc
64
+ cdef multipers_interface_input stuff
65
+ cdef multipers_interface_output stuff2
66
+ with nogil:
67
+ truc.config.show_info = verbose
68
+ truc.config.sort_output = False
69
+ truc.config.sort = sort
70
+ truc.config.progress = progress
71
+ stuff = multipers_interface_input(col_degree, row_degree, matrix)
72
+ stuff2 = truc.multipers_interface(stuff)
73
+ out = []
74
+ _Slicer = mp.Slicer(return_type_only=True, dtype=np.float64)
75
+ out = [_Slicer() for _ in range(stuff2.summands.size())]
76
+ dim_container_ = s.get_dimensions().copy()
77
+ cdef int32_t[:] dim_container = np.asarray(dim_container_, dtype=np.int32)
78
+ cdef list boundary_container
79
+ cdef vector[pair[double,double]] FR
80
+ cdef vector[pair[double,double]] FG
81
+ cdef vector[vector[int]] B
82
+ for i in range(stuff2.summands.size()):
83
+ FR = stuff2.summands[i].col_degrees
84
+ FG = stuff2.summands[i].row_degrees
85
+ B = stuff2.summands[i].matrix
86
+
87
+ for j in range(FG.size()):
88
+ dim_container[j] = degree
89
+ for j in range(FG.size(),FG.size()+FR.size()):
90
+ dim_container[j] = degree +1
91
+
92
+ boundary_container = [[] for _ in range(FG.size())]
93
+ boundary_container.extend(B)
94
+
95
+ if FR.size() == 0:
96
+ filtration_values = np.asarray(FG)
97
+ else:
98
+ filtration_values = np.concatenate([FG,FR], dtype=np.float64)
99
+
100
+ s_summand = _Slicer(
101
+ boundary_container,
102
+ dim_container[:FG.size()+FR.size()],
103
+ filtration_values
104
+ )
105
+ if s.is_squeezed:
106
+ s_summand.filtration_grid = s.filtration_grid
107
+ s_summand._clean_filtration_grid()
108
+ out[i] = s_summand
109
+
110
+ return out
111
+
112
+ def one_criticalify(
113
+ slicer,
114
+ bool reduce=False,
115
+ degree:Optional[int]=None,
116
+ bool clear = True,
117
+ swedish:Optional[bool] = None,
118
+ bool verbose = False,
119
+ bool kcritical=False,
120
+ str algo:Literal["path","tree"]="path",
121
+ ):
122
+ """
123
+ Computes a free implicit representation of a given multi-critical
124
+ multifiltration of a given homological degree (i.e., for a given
125
+ homological degree, a quasi-isomorphic 1-critical filtration), or free
126
+ resolution of the multifiltration (i.e., quasi-isomorphic 1-critical chain
127
+ complex).
128
+
129
+ From [Fast free resolutions of bifiltered chain complexes](https://doi.org/10.48550/arXiv.2512.08652),
130
+ whose code is available here: https://bitbucket.org/mkerber/multi_critical
131
+
132
+ Parameters:
133
+ - slicer : multicritical filtration to represent
134
+ - reduce : returns a (or multiple, see degree) minimal presentation(s) instead of the chain complex.
135
+ - degree : If an int is given, and `reduce` is true, only returns the minimal presentation of this degree.
136
+ If None is given and `reduce` is true, returns a minimal presentation of all possible degrees.
137
+ If reduce is false : has no effect.
138
+ - clear : Clears the temporary files.
139
+ - swedish : if True, `reduce=True` and `degree=None` skips the computation of the 1critical chain complex,
140
+ and directly (sequentially) computes the individual minimal presentations.
141
+ - verbose : shows log
142
+ - kcritical : do not use
143
+ - algo : see ref.
144
+ """
145
+ from multipers.io import _multi_critical_from_slicer
146
+ from multipers.slicer import is_slicer
147
+ if not is_slicer(slicer):
148
+ raise ValueError(f"Invalid input. Expected `SlicerType` got {type(slicer)=}.")
149
+ if not slicer.is_kcritical:
150
+ return slicer
151
+ if slicer.is_squeezed:
152
+ F = slicer.filtration_grid
153
+ else:
154
+ F = None
155
+ out = _multi_critical_from_slicer(
156
+ slicer, reduce=reduce, algo=algo,
157
+ degree=degree, clear=clear,
158
+ swedish=swedish, verbose=verbose,
159
+ kcritical=kcritical
160
+ )
161
+ if is_slicer(out, allow_minpres=False):
162
+ out.filtration_grid = F
163
+ else:
164
+ for stuff in out:
165
+ stuff.filtration_grid = F
166
+ return out
167
+
168
+ def minimal_presentation(
169
+ slicer,
170
+ int degree = -1,
171
+ degrees:Iterable[int]=[],
172
+ str backend:Literal["mpfree", "2pac", ""]="mpfree",
173
+ int n_jobs = -1,
174
+ bool force=False,
175
+ bool auto_clean = True,
176
+ ):
177
+ """
178
+ Computes a minimal presentation a (1-critical) multifiltered complex.
179
+
180
+ From [Fast minimal presentations of bi-graded persistence modules](https://doi.org/10.1137/1.9781611976472.16),
181
+ whose code is available here: https://bitbucket.org/mkerber/mpfree
182
+
183
+ Backends differents than `mpfree` are unstable.
184
+
185
+ Parameters:
186
+ - slicer : the filtration/free implicit representation to reduce
187
+ - degree : the homological degree to reduce
188
+ - degrees : a list of homological degrees to reduce. Output will be a list.
189
+ - backend : a callable `scc`-compatible backend
190
+ - n_jobs : process minpres in parallel if degrees is given
191
+ - force : if input is already reduced, force the re-computation of the minimal presentation.
192
+ - auto_clean : if input is squeezed, some filtraton values may disappear.
193
+ This is a postprocessing to remove unnecessary coordinates.
194
+ """
195
+ from multipers.io import _init_external_softwares, scc_reduce_from_str_to_slicer
196
+ from joblib import Parallel, delayed
197
+ from multipers.slicer import is_slicer
198
+ from multipers import Slicer
199
+ import os
200
+ import tempfile
201
+
202
+ if is_slicer(slicer) and slicer.is_minpres and not force:
203
+ from warnings import warn
204
+ warn(f"(unnecessary computation) The slicer seems to be already reduced, from homology of degree {slicer.minpres_degree}.")
205
+ return slicer
206
+ _init_external_softwares(requires=[backend])
207
+ if len(degrees)>0:
208
+ def todo(int degree):
209
+ return minimal_presentation(slicer, degree=degree, backend=backend, force=force, auto_clean=auto_clean)
210
+ return tuple(
211
+ Parallel(n_jobs=n_jobs, backend="threading")(delayed(todo)(d) for d in degrees)
212
+ )
213
+ assert degree>=0, f"Degree not provided."
214
+ if not np.any(slicer.get_dimensions() == degree):
215
+ return type(slicer)()
216
+ dimension = slicer.dimension - degree # latest = L-1, which is empty, -1 for degree 0, -2 for degree 1 etc.
217
+ with tempfile.TemporaryDirectory(prefix="multipers") as tmpdir:
218
+ tmp_path = os.path.join(tmpdir, "multipers.scc")
219
+ slicer.to_scc(path=tmp_path, strip_comments=True, degree=degree-1, unsqueeze = False)
220
+ new_slicer = type(slicer)()
221
+ if backend=="mpfree":
222
+ shift_dimension=degree-1
223
+ else:
224
+ shift_dimension=degree
225
+ scc_reduce_from_str_to_slicer(path=tmp_path, slicer=new_slicer, dimension=dimension, backend=backend, shift_dimension=shift_dimension)
226
+
227
+ new_slicer.minpres_degree = degree
228
+ new_slicer.filtration_grid = slicer.filtration_grid if slicer.is_squeezed else None
229
+ if new_slicer.is_squeezed and auto_clean:
230
+ new_slicer = new_slicer._clean_filtration_grid()
231
+ return new_slicer
multipers/pickle.py ADDED
@@ -0,0 +1,89 @@
1
+ import numpy as np
2
+
3
+
4
+ def save_with_axis(path: str, signed_measures):
5
+ np.savez(
6
+ path,
7
+ **{
8
+ f"{i}_{axis}_{degree}": np.c_[
9
+ sm_of_degree[0], sm_of_degree[1][:, np.newaxis]
10
+ ]
11
+ for i, sm in enumerate(signed_measures)
12
+ for axis, sm_of_axis in enumerate(sm)
13
+ for degree, sm_of_degree in enumerate(sm_of_axis)
14
+ },
15
+ )
16
+
17
+
18
+ def save_without_axis(path: str, signed_measures):
19
+ np.savez(
20
+ path,
21
+ **{
22
+ f"{i}_{degree}": np.c_[sm_of_degree[0], sm_of_degree[1][:, np.newaxis]]
23
+ for i, sm in enumerate(signed_measures)
24
+ for degree, sm_of_degree in enumerate(sm)
25
+ },
26
+ )
27
+
28
+
29
+ def get_sm_with_axis(sms, idx, axis, degree):
30
+ sm = sms[f"{idx}_{axis}_{degree}"]
31
+ return (sm[:, :-1], sm[:, -1])
32
+
33
+
34
+ def get_sm_without_axis(sms, idx, degree):
35
+ sm = sms[f"{idx}_{degree}"]
36
+ return (sm[:, :-1], sm[:, -1])
37
+
38
+
39
+ def load_without_axis(sms):
40
+ indices = np.array(
41
+ [[int(i) for i in key.split("_")] for key in sms.keys()], dtype=int
42
+ )
43
+ num_data, num_degrees = indices.max(axis=0) + 1
44
+ signed_measures_reconstructed = [
45
+ [get_sm_without_axis(sms, idx, degree) for degree in range(num_degrees)]
46
+ for idx in range(num_data)
47
+ ]
48
+ return signed_measures_reconstructed
49
+
50
+
51
+ # test : np.all([np.array_equal(a[0],b[0]) and np.array_equal(a[1],b[1]) and len(a) == len(b) == 2 for x,y in zip(signed_measures_reconstructed,signed_measures_reconstructed) for a,b in zip(x,y)])
52
+
53
+
54
+ def load_with_axis(sms):
55
+ indices = np.array(
56
+ [[int(i) for i in key.split("_")] for key in sms.keys()], dtype=int
57
+ )
58
+ num_data, num_axis, num_degrees = indices.max(axis=0) + 1
59
+ signed_measures_reconstructed = [
60
+ [
61
+ [get_sm_with_axis(sms, idx, axis, degree) for degree in range(num_degrees)]
62
+ for axis in range(num_axis)
63
+ ]
64
+ for idx in range(num_data)
65
+ ]
66
+ return signed_measures_reconstructed
67
+
68
+
69
+ def save(path: str, signed_measures):
70
+ if isinstance(signed_measures[0][0], tuple):
71
+ save_without_axis(path=path, signed_measures=signed_measures)
72
+ else:
73
+ save_with_axis(path=path, signed_measures=signed_measures)
74
+
75
+
76
+ def load(path: str):
77
+ sms = np.load(path)
78
+ item = None
79
+ for i in sms.keys():
80
+ item = i
81
+ break
82
+ n = len(item.split("_"))
83
+ match n:
84
+ case 2:
85
+ return load_without_axis(sms)
86
+ case 3:
87
+ return load_with_axis(sms)
88
+ case _:
89
+ raise Exception("Invalid Signed Measure !")