multipers 2.3.3b6__cp310-cp310-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.

Potentially problematic release.


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

Files changed (183) hide show
  1. multipers/.dylibs/libc++.1.0.dylib +0 -0
  2. multipers/.dylibs/libtbb.12.16.dylib +0 -0
  3. multipers/__init__.py +33 -0
  4. multipers/_signed_measure_meta.py +453 -0
  5. multipers/_slicer_meta.py +211 -0
  6. multipers/array_api/__init__.py +45 -0
  7. multipers/array_api/numpy.py +41 -0
  8. multipers/array_api/torch.py +58 -0
  9. multipers/data/MOL2.py +458 -0
  10. multipers/data/UCR.py +18 -0
  11. multipers/data/__init__.py +1 -0
  12. multipers/data/graphs.py +466 -0
  13. multipers/data/immuno_regions.py +27 -0
  14. multipers/data/minimal_presentation_to_st_bf.py +0 -0
  15. multipers/data/pytorch2simplextree.py +91 -0
  16. multipers/data/shape3d.py +101 -0
  17. multipers/data/synthetic.py +113 -0
  18. multipers/distances.py +202 -0
  19. multipers/filtration_conversions.pxd +229 -0
  20. multipers/filtration_conversions.pxd.tp +84 -0
  21. multipers/filtrations/__init__.py +18 -0
  22. multipers/filtrations/density.py +574 -0
  23. multipers/filtrations/filtrations.py +361 -0
  24. multipers/filtrations.pxd +224 -0
  25. multipers/function_rips.cpython-310-darwin.so +0 -0
  26. multipers/function_rips.pyx +105 -0
  27. multipers/grids.cpython-310-darwin.so +0 -0
  28. multipers/grids.pyx +433 -0
  29. multipers/gudhi/Persistence_slices_interface.h +132 -0
  30. multipers/gudhi/Simplex_tree_interface.h +239 -0
  31. multipers/gudhi/Simplex_tree_multi_interface.h +551 -0
  32. multipers/gudhi/cubical_to_boundary.h +59 -0
  33. multipers/gudhi/gudhi/Bitmap_cubical_complex.h +450 -0
  34. multipers/gudhi/gudhi/Bitmap_cubical_complex_base.h +1070 -0
  35. multipers/gudhi/gudhi/Bitmap_cubical_complex_periodic_boundary_conditions_base.h +579 -0
  36. multipers/gudhi/gudhi/Debug_utils.h +45 -0
  37. multipers/gudhi/gudhi/Fields/Multi_field.h +484 -0
  38. multipers/gudhi/gudhi/Fields/Multi_field_operators.h +455 -0
  39. multipers/gudhi/gudhi/Fields/Multi_field_shared.h +450 -0
  40. multipers/gudhi/gudhi/Fields/Multi_field_small.h +531 -0
  41. multipers/gudhi/gudhi/Fields/Multi_field_small_operators.h +507 -0
  42. multipers/gudhi/gudhi/Fields/Multi_field_small_shared.h +531 -0
  43. multipers/gudhi/gudhi/Fields/Z2_field.h +355 -0
  44. multipers/gudhi/gudhi/Fields/Z2_field_operators.h +376 -0
  45. multipers/gudhi/gudhi/Fields/Zp_field.h +420 -0
  46. multipers/gudhi/gudhi/Fields/Zp_field_operators.h +400 -0
  47. multipers/gudhi/gudhi/Fields/Zp_field_shared.h +418 -0
  48. multipers/gudhi/gudhi/Flag_complex_edge_collapser.h +337 -0
  49. multipers/gudhi/gudhi/Matrix.h +2107 -0
  50. multipers/gudhi/gudhi/Multi_critical_filtration.h +1038 -0
  51. multipers/gudhi/gudhi/Multi_persistence/Box.h +174 -0
  52. multipers/gudhi/gudhi/Multi_persistence/Line.h +282 -0
  53. multipers/gudhi/gudhi/Off_reader.h +173 -0
  54. multipers/gudhi/gudhi/One_critical_filtration.h +1441 -0
  55. multipers/gudhi/gudhi/Persistence_matrix/Base_matrix.h +769 -0
  56. multipers/gudhi/gudhi/Persistence_matrix/Base_matrix_with_column_compression.h +686 -0
  57. multipers/gudhi/gudhi/Persistence_matrix/Boundary_matrix.h +842 -0
  58. multipers/gudhi/gudhi/Persistence_matrix/Chain_matrix.h +1350 -0
  59. multipers/gudhi/gudhi/Persistence_matrix/Id_to_index_overlay.h +1105 -0
  60. multipers/gudhi/gudhi/Persistence_matrix/Position_to_index_overlay.h +859 -0
  61. multipers/gudhi/gudhi/Persistence_matrix/RU_matrix.h +910 -0
  62. multipers/gudhi/gudhi/Persistence_matrix/allocators/entry_constructors.h +139 -0
  63. multipers/gudhi/gudhi/Persistence_matrix/base_pairing.h +230 -0
  64. multipers/gudhi/gudhi/Persistence_matrix/base_swap.h +211 -0
  65. multipers/gudhi/gudhi/Persistence_matrix/boundary_cell_position_to_id_mapper.h +60 -0
  66. multipers/gudhi/gudhi/Persistence_matrix/boundary_face_position_to_id_mapper.h +60 -0
  67. multipers/gudhi/gudhi/Persistence_matrix/chain_pairing.h +136 -0
  68. multipers/gudhi/gudhi/Persistence_matrix/chain_rep_cycles.h +190 -0
  69. multipers/gudhi/gudhi/Persistence_matrix/chain_vine_swap.h +616 -0
  70. multipers/gudhi/gudhi/Persistence_matrix/columns/chain_column_extra_properties.h +150 -0
  71. multipers/gudhi/gudhi/Persistence_matrix/columns/column_dimension_holder.h +106 -0
  72. multipers/gudhi/gudhi/Persistence_matrix/columns/column_utilities.h +219 -0
  73. multipers/gudhi/gudhi/Persistence_matrix/columns/entry_types.h +327 -0
  74. multipers/gudhi/gudhi/Persistence_matrix/columns/heap_column.h +1140 -0
  75. multipers/gudhi/gudhi/Persistence_matrix/columns/intrusive_list_column.h +934 -0
  76. multipers/gudhi/gudhi/Persistence_matrix/columns/intrusive_set_column.h +934 -0
  77. multipers/gudhi/gudhi/Persistence_matrix/columns/list_column.h +980 -0
  78. multipers/gudhi/gudhi/Persistence_matrix/columns/naive_vector_column.h +1092 -0
  79. multipers/gudhi/gudhi/Persistence_matrix/columns/row_access.h +192 -0
  80. multipers/gudhi/gudhi/Persistence_matrix/columns/set_column.h +921 -0
  81. multipers/gudhi/gudhi/Persistence_matrix/columns/small_vector_column.h +1093 -0
  82. multipers/gudhi/gudhi/Persistence_matrix/columns/unordered_set_column.h +1012 -0
  83. multipers/gudhi/gudhi/Persistence_matrix/columns/vector_column.h +1244 -0
  84. multipers/gudhi/gudhi/Persistence_matrix/matrix_dimension_holders.h +186 -0
  85. multipers/gudhi/gudhi/Persistence_matrix/matrix_row_access.h +164 -0
  86. multipers/gudhi/gudhi/Persistence_matrix/ru_pairing.h +156 -0
  87. multipers/gudhi/gudhi/Persistence_matrix/ru_rep_cycles.h +376 -0
  88. multipers/gudhi/gudhi/Persistence_matrix/ru_vine_swap.h +540 -0
  89. multipers/gudhi/gudhi/Persistent_cohomology/Field_Zp.h +118 -0
  90. multipers/gudhi/gudhi/Persistent_cohomology/Multi_field.h +173 -0
  91. multipers/gudhi/gudhi/Persistent_cohomology/Persistent_cohomology_column.h +128 -0
  92. multipers/gudhi/gudhi/Persistent_cohomology.h +745 -0
  93. multipers/gudhi/gudhi/Points_off_io.h +171 -0
  94. multipers/gudhi/gudhi/Simple_object_pool.h +69 -0
  95. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_iterators.h +463 -0
  96. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h +83 -0
  97. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_siblings.h +106 -0
  98. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_star_simplex_iterators.h +277 -0
  99. multipers/gudhi/gudhi/Simplex_tree/hooks_simplex_base.h +62 -0
  100. multipers/gudhi/gudhi/Simplex_tree/indexing_tag.h +27 -0
  101. multipers/gudhi/gudhi/Simplex_tree/serialization_utils.h +62 -0
  102. multipers/gudhi/gudhi/Simplex_tree/simplex_tree_options.h +157 -0
  103. multipers/gudhi/gudhi/Simplex_tree.h +2794 -0
  104. multipers/gudhi/gudhi/Simplex_tree_multi.h +152 -0
  105. multipers/gudhi/gudhi/distance_functions.h +62 -0
  106. multipers/gudhi/gudhi/graph_simplicial_complex.h +104 -0
  107. multipers/gudhi/gudhi/persistence_interval.h +253 -0
  108. multipers/gudhi/gudhi/persistence_matrix_options.h +170 -0
  109. multipers/gudhi/gudhi/reader_utils.h +367 -0
  110. multipers/gudhi/mma_interface_coh.h +256 -0
  111. multipers/gudhi/mma_interface_h0.h +223 -0
  112. multipers/gudhi/mma_interface_matrix.h +293 -0
  113. multipers/gudhi/naive_merge_tree.h +536 -0
  114. multipers/gudhi/scc_io.h +310 -0
  115. multipers/gudhi/truc.h +1403 -0
  116. multipers/io.cpython-310-darwin.so +0 -0
  117. multipers/io.pyx +644 -0
  118. multipers/ml/__init__.py +0 -0
  119. multipers/ml/accuracies.py +90 -0
  120. multipers/ml/invariants_with_persistable.py +79 -0
  121. multipers/ml/kernels.py +176 -0
  122. multipers/ml/mma.py +713 -0
  123. multipers/ml/one.py +472 -0
  124. multipers/ml/point_clouds.py +352 -0
  125. multipers/ml/signed_measures.py +1589 -0
  126. multipers/ml/sliced_wasserstein.py +461 -0
  127. multipers/ml/tools.py +113 -0
  128. multipers/mma_structures.cpython-310-darwin.so +0 -0
  129. multipers/mma_structures.pxd +128 -0
  130. multipers/mma_structures.pyx +2786 -0
  131. multipers/mma_structures.pyx.tp +1094 -0
  132. multipers/multi_parameter_rank_invariant/diff_helpers.h +84 -0
  133. multipers/multi_parameter_rank_invariant/euler_characteristic.h +97 -0
  134. multipers/multi_parameter_rank_invariant/function_rips.h +322 -0
  135. multipers/multi_parameter_rank_invariant/hilbert_function.h +769 -0
  136. multipers/multi_parameter_rank_invariant/persistence_slices.h +148 -0
  137. multipers/multi_parameter_rank_invariant/rank_invariant.h +369 -0
  138. multipers/multiparameter_edge_collapse.py +41 -0
  139. multipers/multiparameter_module_approximation/approximation.h +2330 -0
  140. multipers/multiparameter_module_approximation/combinatory.h +129 -0
  141. multipers/multiparameter_module_approximation/debug.h +107 -0
  142. multipers/multiparameter_module_approximation/euler_curves.h +0 -0
  143. multipers/multiparameter_module_approximation/format_python-cpp.h +286 -0
  144. multipers/multiparameter_module_approximation/heap_column.h +238 -0
  145. multipers/multiparameter_module_approximation/images.h +79 -0
  146. multipers/multiparameter_module_approximation/list_column.h +174 -0
  147. multipers/multiparameter_module_approximation/list_column_2.h +232 -0
  148. multipers/multiparameter_module_approximation/ru_matrix.h +347 -0
  149. multipers/multiparameter_module_approximation/set_column.h +135 -0
  150. multipers/multiparameter_module_approximation/structure_higher_dim_barcode.h +36 -0
  151. multipers/multiparameter_module_approximation/unordered_set_column.h +166 -0
  152. multipers/multiparameter_module_approximation/utilities.h +403 -0
  153. multipers/multiparameter_module_approximation/vector_column.h +223 -0
  154. multipers/multiparameter_module_approximation/vector_matrix.h +331 -0
  155. multipers/multiparameter_module_approximation/vineyards.h +464 -0
  156. multipers/multiparameter_module_approximation/vineyards_trajectories.h +649 -0
  157. multipers/multiparameter_module_approximation.cpython-310-darwin.so +0 -0
  158. multipers/multiparameter_module_approximation.pyx +235 -0
  159. multipers/pickle.py +90 -0
  160. multipers/plots.py +456 -0
  161. multipers/point_measure.cpython-310-darwin.so +0 -0
  162. multipers/point_measure.pyx +395 -0
  163. multipers/simplex_tree_multi.cpython-310-darwin.so +0 -0
  164. multipers/simplex_tree_multi.pxd +134 -0
  165. multipers/simplex_tree_multi.pyx +10840 -0
  166. multipers/simplex_tree_multi.pyx.tp +2009 -0
  167. multipers/slicer.cpython-310-darwin.so +0 -0
  168. multipers/slicer.pxd +3034 -0
  169. multipers/slicer.pxd.tp +234 -0
  170. multipers/slicer.pyx +20481 -0
  171. multipers/slicer.pyx.tp +1088 -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 +62 -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-2.3.3b6.dist-info/METADATA +128 -0
  180. multipers-2.3.3b6.dist-info/RECORD +183 -0
  181. multipers-2.3.3b6.dist-info/WHEEL +6 -0
  182. multipers-2.3.3b6.dist-info/licenses/LICENSE +21 -0
  183. multipers-2.3.3b6.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1094 @@
1
+ {{py:
2
+
3
+ """
4
+ MMA core structures.
5
+ """
6
+
7
+ ## Value types : CTYPE, PYTHON_TYPE, short
8
+ # value_types = [
9
+ # ("int32_t", "np.int32", "i32"),
10
+ # ("int64_t", "np.int64", "i64"),
11
+ # ("float", "np.float32", "f32"),
12
+ # ("double", "np.float64", "f64"),
13
+ # ]
14
+
15
+ import pickle as pkl
16
+ import numpy as np
17
+
18
+ D = pkl.load(open("build/tmp/_slicer_names.pkl", "rb"))
19
+ value_types = np.unique([tuple((x['C_VALUE_TYPE'],x['PY_VALUE_TYPE'], x['SHORT_VALUE_TYPE'])) for x in D], axis=0)
20
+
21
+ }}
22
+
23
+
24
+
25
+
26
+
27
+ """!
28
+ @package mma
29
+ @brief Files containing the C++ cythonized functions.
30
+ @author David Loiseaux
31
+ @copyright Copyright (c) 2022 Inria.
32
+ """
33
+
34
+ # distutils: language = c++
35
+
36
+ ###########################################################################
37
+ ## PYTHON LIBRARIES
38
+ import gudhi as gd
39
+ import numpy as np
40
+ from typing import Union, Literal
41
+ from collections.abc import Callable, Iterable, Sequence
42
+ import pickle
43
+ import multipers.grids as mpg
44
+
45
+ ###########################################################################
46
+ ## CPP CLASSES
47
+ from libc.stdint cimport intptr_t
48
+ from libc.stdint cimport uintptr_t
49
+
50
+ ###########################################################################
51
+ ## CYTHON TYPES
52
+ from libcpp.vector cimport vector
53
+ from libcpp.utility cimport pair
54
+ #from libcpp.list cimport list as clist
55
+ from libcpp cimport bool
56
+ from libcpp cimport int
57
+ from cython.operator cimport dereference
58
+ from libcpp.utility cimport move
59
+ cimport cython
60
+ #########################################################################
61
+ ## Multipersistence Module Approximation Classes
62
+ from multipers.mma_structures cimport *
63
+ from multipers.filtration_conversions cimport *
64
+ cimport numpy as cnp
65
+
66
+
67
+ #########################################################################
68
+ ## Small hack for typing
69
+ from gudhi import SimplexTree
70
+ from multipers.simplex_tree_multi import SimplexTreeMulti
71
+ from joblib import Parallel, delayed
72
+
73
+ available_pymodules = [
74
+ {{for CTYPE, PYTYPE, SHORT in value_types}}
75
+ PyModule_{{SHORT}},
76
+ {{endfor}}
77
+ ]
78
+
79
+ PyModule_type = Union[
80
+ {{for CTYPE, PYTYPE, SHORT in value_types}}
81
+ PyModule_{{SHORT}},
82
+ {{endfor}}
83
+ ]
84
+
85
+ {{for CTYPE, PYTYPE, SHORT in value_types}}
86
+ cdef class PySummand_{{SHORT}}:
87
+ r"""
88
+ Stores a Summand of a PyModule
89
+ """
90
+ cdef Summand[{{CTYPE}}] sum
91
+
92
+ def get_birth_list(self):
93
+ cdef vector[One_critical_filtration[{{CTYPE}}]] v = self.sum.get_birth_list()
94
+ return _vff21cview_{{SHORT}}(v, copy = True, duplicate = self.num_parameters())
95
+
96
+ def get_death_list(self):
97
+ cdef vector[One_critical_filtration[{{CTYPE}}]] v = self.sum.get_death_list()
98
+ return _vff21cview_{{SHORT}}(v, copy = True, duplicate = self.num_parameters())
99
+ @property
100
+ def degree(self)->int:
101
+ return self.sum.get_dimension()
102
+
103
+ cdef set(self, Summand[{{CTYPE}}]& summand):
104
+ self.sum = summand
105
+ return self
106
+ def get_bounds(self):
107
+ cdef pair[One_critical_filtration[{{CTYPE}}],One_critical_filtration[{{CTYPE}}]] cbounds
108
+ with nogil:
109
+ cbounds = self.sum.get_bounds().get_bounding_corners()
110
+ return _ff21cview_{{SHORT}}(&cbounds.first).copy(), _ff21cview_{{SHORT}}(&cbounds.second).copy()
111
+ @property
112
+ def dtype(self):
113
+ return {{PYTYPE}}
114
+
115
+ def num_parameters(self):
116
+ cdef vector[One_critical_filtration[{{CTYPE}}]] v = self.sum.get_birth_list()
117
+ if v[0].is_finite():
118
+ return v[0].num_parameters()
119
+ v = self.sum.get_death_list()
120
+ return v[0].num_parameters()
121
+ def __eq__(self, PySummand_{{SHORT}} other):
122
+ return self.sum == other.sum
123
+
124
+
125
+
126
+
127
+ cdef inline get_summand_filtration_values_{{SHORT}}(Summand[{{CTYPE}}] summand):
128
+ r"""
129
+ Returns a list (over parameter) of the filtrations values of this parameter.
130
+ """
131
+ cdef vector[One_critical_filtration[{{CTYPE}}]] vb = summand.get_birth_list()
132
+ cdef vector[One_critical_filtration[{{CTYPE}}]] vd = summand.get_death_list()
133
+
134
+ if vb[0].is_finite():
135
+ if vd[0].is_finite():
136
+ pts = np.concatenate([_vff21cview_{{SHORT}}(vb, copy=True),
137
+ _vff21cview_{{SHORT}}(vd, copy=True)],axis=0)
138
+ else:
139
+ pts = np.array(_vff21cview_{{SHORT}}(vb, copy=True))
140
+ else:
141
+ if vd[0].is_finite():
142
+ pts = np.array(_vff21cview_{{SHORT}}(vd, copy=True))
143
+ else:
144
+ return []
145
+
146
+ num_parameters = pts.shape[1]
147
+ out = [np.unique(pts[:,parameter]) for parameter in range(num_parameters)]
148
+ out = [f[:-1] if f[-1] == np.inf else f for f in out]
149
+ out = [f[1:] if f[0] == -np.inf else f for f in out]
150
+ return out
151
+
152
+ cdef class PyBox_{{SHORT}}:
153
+ cdef Box[{{CTYPE}}] box
154
+ def __cinit__(self, vector[{{CTYPE}}]& bottomCorner, vector[{{CTYPE}}]& topCorner):
155
+ self.box = Box[{{CTYPE}}](bottomCorner, topCorner)
156
+ @property
157
+ def num_parameters(self):
158
+ cdef size_t dim = self.box.get_lower_corner().num_parameters()
159
+ if dim == self.box.get_upper_corner().num_parameters(): return dim
160
+ else: print("Bad box definition.")
161
+ def contains(self, x):
162
+ return self.box.contains(x)
163
+ cdef set(self, Box[{{CTYPE}}]& b):
164
+ self.box = b
165
+ return self
166
+
167
+ def get(self):
168
+ return [<vector[{{CTYPE}}]>self.box.get_lower_corner(), <vector[{{CTYPE}}]>self.box.get_upper_corner()]
169
+ def to_multipers(self):
170
+ #assert (self.get_dimension() == 2) "Multipers only works in dimension 2 !"
171
+ return np.array(self.get()).flatten(order = 'F')
172
+ @property
173
+ def dtype(self):
174
+ return {{PYTYPE}}
175
+
176
+
177
+
178
+ cdef class PyModule_{{SHORT}}:
179
+ r"""
180
+ Stores a representation of a n-persistence module.
181
+ """
182
+ cdef Module[{{CTYPE}}] cmod
183
+
184
+ @property
185
+ def dtype(self):
186
+ return {{PYTYPE}}
187
+
188
+ cdef set(self, Module[{{CTYPE}}] m):
189
+ self.cmod = m
190
+
191
+ def __eq__(self, PyModule_{{SHORT}} other):
192
+ return self.cmod == other.cmod
193
+
194
+ def merge(self, PyModule_{{SHORT}} other, int dim=-1):
195
+ r"""
196
+ Merges two modules into one
197
+ """
198
+ cdef Module[{{CTYPE}}] c_other = other.cmod
199
+ with nogil:
200
+ for summand in c_other:
201
+ self.cmod.add_summand(summand, dim)
202
+ return self
203
+
204
+ def _set_from_ptr(self, intptr_t module_ptr):
205
+ r"""
206
+ Copy module from a memory pointer. Unsafe.
207
+ """
208
+ self.cmod = move(dereference(<Module[{{CTYPE}}]*>(module_ptr)))
209
+ def set_box(self, box):
210
+ assert len(box) == 2, "Box format is [low, hight]"
211
+ pybox = PyBox_{{SHORT}}(box[0], box[1])
212
+ cdef Box[{{CTYPE}}] cbox = pybox.box
213
+ with nogil:
214
+ self.cmod.set_box(cbox)
215
+ return self
216
+ def get_module_of_degree(self, int degree)->PyModule_{{SHORT}}: # TODO : in c++ ?
217
+ r"""
218
+ Returns a copy of a module of fixed degree.
219
+ """
220
+ pmodule = PyModule_{{SHORT}}()
221
+ cdef Box[{{CTYPE}}] c_box = self.cmod.get_box()
222
+ pmodule.cmod.set_box(c_box)
223
+ with nogil:
224
+ for summand in self.cmod:
225
+ if summand.get_dimension() == degree:
226
+ pmodule.cmod.add_summand(summand)
227
+ return pmodule
228
+ def get_module_of_degrees(self, degrees:Iterable[int])->PyModule_{{SHORT}}: # TODO : in c++ ?
229
+ r"""
230
+ Returns a copy of the summands of degrees in `degrees`
231
+ """
232
+ pmodule = PyModule_{{SHORT}}()
233
+ cdef Box[{{CTYPE}}] c_box = self.cmod.get_box()
234
+ pmodule.cmod.set_box(c_box)
235
+ cdef vector[int] cdegrees = degrees
236
+ with nogil:
237
+ for summand in self.cmod:
238
+ for d in cdegrees:
239
+ if d == summand.get_dimension():
240
+ pmodule.cmod.add_summand(summand)
241
+ return pmodule
242
+ def __len__(self)->int:
243
+ return self.cmod.size()
244
+ def get_bottom(self)->np.ndarray:
245
+ r"""
246
+ Bottom of the box of the module
247
+ """
248
+ return np.asarray(<vector[{{CTYPE}}]>(self.cmod.get_box().get_lower_corner()))
249
+ def get_top(self)->np.ndarray:
250
+ r"""
251
+ Top of the box of the module
252
+ """
253
+ return np.asarray(<vector[{{CTYPE}}]>(self.cmod.get_box().get_upper_corner()))
254
+ def get_box(self)->np.ndarray:
255
+ r"""
256
+ Returns the current bounding box of the module.
257
+ """
258
+ return np.asarray([self.get_bottom(), self.get_top()])
259
+ @property
260
+ def max_degree(self)->int:
261
+ r"""
262
+ Returns the maximum degree of the module.
263
+ """
264
+ return self.cmod.get_dimension()
265
+ @property
266
+ def num_parameters(self)->int:
267
+ cdef size_t dim = self.cmod.get_box().get_lower_corner().num_parameters()
268
+ assert dim == self.cmod.get_box().get_upper_corner().num_parameters(), "Bad box definition, cannot infer num_parameters."
269
+ return dim
270
+ def dump(self, path:str|None=None):
271
+ r"""
272
+ Dumps the module into a pickle-able format.
273
+
274
+ Parameters
275
+ ----------
276
+
277
+ path:str=None (optional) saves the pickled module in specified path
278
+
279
+ Returns
280
+ -------
281
+
282
+ list of list, encoding the module, which can be retrieved with the function `from_dump`.
283
+ """
284
+ ## TODO : optimize, but not really used.
285
+ return dump_cmod_{{SHORT}}(self.cmod)
286
+ def __getstate__(self):
287
+ return self.dump()
288
+ def __setstate__(self,dump):
289
+ cdef Module[{{CTYPE}}] cmod = cmod_from_dump_{{SHORT}}(dump)
290
+ self.cmod = cmod
291
+ return
292
+ def __getitem__(self, int i) -> PySummand_{{SHORT}}:
293
+ if i == slice(None):
294
+ return self
295
+ summand = PySummand_{{SHORT}}()
296
+ summand.set(self.cmod.at(i % self.cmod.size()))
297
+ return summand
298
+ def __iter__(self):
299
+ cdef int num_summands = self.cmod.size()
300
+ for i in range(num_summands):
301
+ summand = PySummand_{{SHORT}}()
302
+ summand.set(self.cmod.at(i)) ## hmm copy. maybe do summand from ptr
303
+ yield summand
304
+
305
+ def get_bounds(self):
306
+ r"""
307
+ Computes bounds from the summands' bounds.
308
+ Useful to change this' box.
309
+ """
310
+ cdef pair[One_critical_filtration[{{CTYPE}}],One_critical_filtration[{{CTYPE}}]] cbounds
311
+ with nogil:
312
+ cbounds = self.cmod.get_bounds().get_bounding_corners()
313
+ return _ff21cview_{{SHORT}}(&cbounds.first).copy(), _ff21cview_{{SHORT}}(&cbounds.second).copy()
314
+ def rescale(self,rescale_factors, int degree=-1):
315
+ r"""
316
+ Rescales the fitlration values of the summands by this rescaling vector.
317
+ """
318
+ cdef vector[{{CTYPE}}] crescale_factors = rescale_factors
319
+ with nogil:
320
+ self.cmod.rescale(crescale_factors,degree)
321
+ def translate(self,translation, int degree=-1):
322
+ r"""
323
+ Translates the module in the filtration space by this vector.
324
+ """
325
+ cdef vector[{{CTYPE}}] ctranslation = translation
326
+ with nogil:
327
+ self.cmod.translate(ctranslation,degree)
328
+
329
+ def get_filtration_values(self, bool unique=True):
330
+ r"""
331
+ Retrieves all filtration values of the summands of the module.
332
+
333
+ Output format
334
+ -------------
335
+
336
+ list of filtration values for parameter.
337
+ """
338
+ if len(self) ==0:
339
+ return np.empty((self.num_parameters,0))
340
+ values = tuple(tuple(stuff) if len(stuff:=get_summand_filtration_values_{{SHORT}}(summand)) == self.num_parameters else list(stuff) + [[]]*(self.num_parameters - len(stuff)) for summand in self.cmod)
341
+ try:
342
+ values = tuple(np.concatenate([
343
+ f[parameter]
344
+ for f in values
345
+ ], axis=0) for parameter in range(self.num_parameters)
346
+ )
347
+ except:
348
+ return values
349
+ if unique:
350
+ return [np.unique(f) for f in values]
351
+ return values
352
+
353
+ def plot(self, int degree=-1,**kwargs)->None:
354
+ r"""Shows the module on a plot. Each color corresponds to an apprimation summand of the module, and its shape corresponds to its support.
355
+ Only works with 2-parameter modules.
356
+
357
+ Parameters
358
+ ----------
359
+ degree = -1 : integer
360
+ If positive returns only the image of dimension `dimension`.
361
+ box=None : of the form [[b_x,b_y], [d_x,d_y]] where b,d are the bottom and top corner of the rectangle.
362
+ If non-None, will plot the module on this specific rectangle.
363
+ min_persistence =0 : float
364
+ Only plots the summand with a persistence above this threshold.
365
+ separated=False : bool
366
+ If true, plot each summand in a different plot.
367
+ alpha=1 : float
368
+ Transparancy parameter
369
+ save = False : string
370
+ if nontrivial, will save the figure at this path
371
+
372
+
373
+ Returns
374
+ -------
375
+ The figure of the plot.
376
+ """
377
+ from multipers.plots import plot2d_PyModule
378
+ import matplotlib.pyplot as plt
379
+ box = kwargs.pop('box', self.get_box())
380
+ if (len(box[0]) != 2):
381
+ print("Filtration size :", len(box[0]), " != 2")
382
+ return
383
+ if(degree < 0):
384
+ dims = np.unique(self.get_dimensions())
385
+ separated = kwargs.pop("separated", False)
386
+ ndim = len(dims)
387
+ scale = kwargs.pop("scale", 4)
388
+ if separated:
389
+ fig = None
390
+ axes = None
391
+ elif ndim > 1:
392
+ fig, axes = plt.subplots(1,ndim, figsize=(ndim*scale,scale))
393
+ else:
394
+ fig = plt.gcf()
395
+ axes = [plt.gca()]
396
+ for dim_idx in range(ndim):
397
+ if not separated:
398
+ plt.sca(axes[dim_idx])
399
+ self.plot(dims[dim_idx],box=box, separated = separated, **kwargs)
400
+ return
401
+ corners = self.cmod.get_corners_of_dimension(degree)
402
+ plot2d_PyModule(corners, box=box, dimension=degree, **kwargs)
403
+ return
404
+ def degree_splits(self):
405
+ return np.asarray(self.cmod.get_degree_splits(), dtype=np.int64)
406
+ {{if SHORT[0] == 'f'}}
407
+ def _compute_pixels(self,coordinates:np.ndarray,
408
+ degrees=None, box=None, {{CTYPE}} delta=.1,
409
+ {{CTYPE}} p=1., bool normalize=False, int n_jobs=0):
410
+ r"""
411
+ Computes the image of the module at the given coordinates
412
+ """
413
+ if degrees is not None: assert np.all(degrees[:-1] <= degrees[1:]), "Degrees have to be sorted"
414
+ cdef vector[int] cdegrees = np.arange(self.max_degree +1) if degrees is None else degrees
415
+ pybox = PyBox_{{SHORT}}(*self.get_box()) if box is None else PyBox_{{SHORT}}(*box)
416
+ cdef Box[{{CTYPE}}] cbox = pybox.box
417
+ cdef vector[vector[{{CTYPE}}]] ccoords = coordinates
418
+ cdef vector[vector[{{CTYPE}}]] out
419
+ with nogil:
420
+ out = self.cmod.compute_pixels(ccoords, cdegrees, cbox, delta, p, normalize, n_jobs)
421
+ return np.asarray(out)
422
+ def barcode(self, basepoint, int degree = -1,*, bool threshold = False): # TODO direction vector interface
423
+ r"""Computes the barcode of module along a lines.
424
+
425
+ Parameters
426
+ ----------
427
+
428
+ basepoint : vector
429
+ basepoint of the lines on which to compute the barcodes, i.e. a point on the line
430
+ degree = -1 : integer
431
+ Homology degree on which to compute the bars. If negative, every dimension is computed
432
+ box (default) :
433
+ box on which to compute the barcodes if basepoints is not specified. Default is a linspace of lines crossing that box.
434
+ threshold = False :
435
+ Thre
436
+
437
+ Warning
438
+ -------
439
+
440
+ If the barcodes are not thresholded, essential barcodes will not be plot-able.
441
+
442
+ Returns
443
+ -------
444
+
445
+ PyMultiDiagrams
446
+ Structure that holds the barcodes. Barcodes can be retrieved with a .get_points() or a .to_multipers() or a .plot().
447
+ """
448
+ out = PyMultiDiagram_{{SHORT}}()
449
+ out.set(self.cmod.get_barcode(Line[{{CTYPE}}](_py21c_{{SHORT}}(np.asarray(basepoint, dtype={{PYTYPE}}))), degree, threshold))
450
+ return out
451
+ @staticmethod
452
+ cdef _threshold_bc(bc):
453
+ return tuple(np.fromiter((a for a in stuff if a[0] < np.inf), dtype=np.dtype(({{PYTYPE}},2)) ) for stuff in bc)
454
+ @staticmethod
455
+ def _bc_to_full(bcs, basepoint, direction=None):
456
+ # i, (b sv d), coords
457
+ basepoint = np.asarray(basepoint)[None,None,:]
458
+ direction = 1 if direction is None else np.asarray(direction)[None,None,:]
459
+ return tuple(bc[:,:,None]*direction + basepoint for bc in bcs)
460
+ def barcode2(self, basepoint, direction=None, int degree = -1,*, bool threshold = False, bool keep_inf = True, bool full = False): # TODO direction vector interface
461
+ r"""
462
+ Compute the 1d-barcode a diagonal line based on basepoint, with some direction.
463
+
464
+ Parameters
465
+ ----------
466
+
467
+ - basepoint: 1d array
468
+ - directiont: 1d array or None, if None: diagonal
469
+ - degree: int the degree to compute (-1 means all)
470
+ - threshold: bool if True, threshold the barcode to the modules box
471
+ - keep_inf: bool if False, removes trivial bars
472
+ Note that this removes the order w.r.t. the summands in that case
473
+ - full:bool if True, returns the coordinates of the barcode instead of the coordinate in the line.
474
+
475
+ The output is of the form
476
+
477
+ tuple[np.ndarray of shape (num_bars,2)] or tuple[array of shape (num_bar, 2, num_parameters)]
478
+ """
479
+ basepoint = np.asarray(basepoint, dtype={{PYTYPE}})
480
+ if direction is None:
481
+ bc = tuple(np.asarray(x).reshape(-1,2) for x in self.cmod.get_barcode2(Line[{{CTYPE}}](_py21c_{{SHORT}}(basepoint)), degree))
482
+ else:
483
+ direction = np.asarray(direction, dtype = {{PYTYPE}})
484
+ bc = tuple(np.asarray(x).reshape(-1,2) for x in self.cmod.get_barcode2(Line[{{CTYPE}}](_py21c_{{SHORT}}(basepoint), _py21c_{{SHORT}}(direction)), degree))
485
+ if not keep_inf:
486
+ bc = PyModule_{{SHORT}}._threshold_bc(bc)
487
+ if full:
488
+ bc = PyModule_{{SHORT}}._bc_to_full(bc, basepoint, direction)
489
+
490
+ return bc
491
+
492
+ def get_dimensions(self):
493
+ cdef int num_summands = len(self)
494
+ out = np.empty(shape=num_summands, dtype=np.int32)
495
+ cdef int32_t[:] c_out = out
496
+ for i in range(num_summands):
497
+ c_out[i] = self.cmod.at(i).get_dimension()
498
+ return out
499
+
500
+ def barcodes(self, int degree, basepoints = None, num=100, box = None,threshold = False):
501
+ r"""Computes barcodes of module along a set of lines.
502
+
503
+ Parameters
504
+ ----------
505
+
506
+ basepoints = None : list of vectors
507
+ basepoints of the lines on which to compute the barcodes.
508
+ degree = -1 : integer
509
+ Homology degree on which to compute the bars. If negative, every dimension is computed
510
+ box (default) :
511
+ box on which to compute the barcodes if basepoints is not specified. Default is a linspace of lines crossing that box.
512
+ num:int=100
513
+ if basepoints is not specified, defines the number of lines to consider.
514
+ threshold = False : threshold t
515
+ Resolution of the image(s).
516
+
517
+ Warning
518
+ -------
519
+
520
+ If the barcodes are not thresholded, essential barcodes will not be plot-able.
521
+
522
+ Returns
523
+ -------
524
+
525
+ PyMultiDiagrams
526
+ Structure that holds the barcodes. Barcodes can be retrieved with a .get_points() or a .to_multipers() or a .plot().
527
+ """
528
+ out = PyMultiDiagrams_{{SHORT}}()
529
+ if box is None:
530
+ box = [self.get_bottom(), self.get_top()]
531
+ if (len(box[0]) != 2) and (basepoints is None):
532
+ raise ValueError("Basepoints has to be specified for filtration dimension >= 3 !")
533
+ elif basepoints is None:
534
+ h = box[1][1] - box[0][1]
535
+ basepoints = np.linspace([box[0][0] - h,box[0][1]], [box[1][0],box[0][1]], num=num)
536
+ else :
537
+ num=len(basepoints)
538
+
539
+ cdef {{CTYPE}}[:,:] basepoints_view = np.asarray(basepoints, dtype = {{PYTYPE}})
540
+ cdef vector[One_critical_filtration[{{CTYPE}}]] cbasepoints = _py2v1c_{{SHORT}}(basepoints_view)
541
+
542
+ out.set(self.cmod.get_barcodes(cbasepoints, degree, threshold))
543
+ return out
544
+
545
+ def barcodes2(self, int degree = -1, basepoints = None, int num=100, box = None, bool threshold = False):
546
+ r"""Computes barcodes of module along a set of lines.
547
+
548
+ Parameters
549
+ ----------
550
+
551
+ basepoints = None : list of vectors
552
+ basepoints of the lines on which to compute the barcodes.
553
+ degree = -1 : integer
554
+ Homology degree on which to compute the bars. If negative, every dimension is computed
555
+ box (default) :
556
+ box on which to compute the barcodes if basepoints is not specified. Default is a linspace of lines crossing that box.
557
+ num:int=100
558
+ if basepoints is not specified, defines the number of lines to consider.
559
+ threshold = False : threshold t
560
+ Resolution of the image(s).
561
+
562
+ Warning
563
+ -------
564
+
565
+ If the barcodes are not thresholded, essential barcodes will not be plot-able.
566
+
567
+ Returns
568
+ -------
569
+
570
+ tuple of 1d barcodes, based on basepoint, with direction (1,1)
571
+ """
572
+ if box is None:
573
+ box = [self.get_bottom(), self.get_top()]
574
+ if (len(box[0]) != 2) and (basepoints is None):
575
+ raise ValueError("Basepoints has to be specified for filtration dimension >= 3 !")
576
+ elif basepoints is None:
577
+ h = box[1][1] - box[0][1]
578
+ basepoints = np.linspace([box[0][0] - h,box[0][1]], [box[1][0],box[0][1]], num=num)
579
+ else :
580
+ num=len(basepoints)
581
+
582
+ basepoints = np.asarray(basepoints, dtype={{PYTYPE}})
583
+ cdef vector[Line[{{CTYPE}}]] cbasepoints
584
+ for i in range(num):
585
+ cbasepoints.push_back(Line[{{CTYPE}}](_py21c_{{SHORT}}(basepoints[i])))
586
+ return tuple(np.asarray(bc) for bc in self.cmod.get_barcodes2(cbasepoints, degree))
587
+
588
+ def landscape(self, degree:int, k:int=0,box:Sequence|np.ndarray|None=None, resolution:Sequence=[100,100], bool plot=False):
589
+ r"""Computes the multiparameter landscape from a PyModule. Python interface only bifiltrations.
590
+
591
+ Parameters
592
+ ----------
593
+
594
+ degree : integer
595
+ The homology degree of the landscape.
596
+ k = 0 : int
597
+ the k-th landscape
598
+ resolution = [50,50] : pair of integers
599
+ Resolution of the image.
600
+ box = None : in the format [[a,b], [c,d]]
601
+ If nontrivial, compute the landscape of this box. Default is the PyModule box.
602
+ plot = True : Boolean
603
+ If true, plots the images;
604
+ Returns
605
+ -------
606
+
607
+ The landscape of the module.
608
+
609
+ """
610
+ import matplotlib.pyplot as plt
611
+ if box is None:
612
+ box = self.get_box()
613
+ cdef Box[{{CTYPE}}] c_box = Box[{{CTYPE}}](box)
614
+ out = np.array(self.cmod.get_landscape(degree, k, c_box, resolution))
615
+ if plot:
616
+ aspect = (box[1][0]-box[0][0]) / (box[1][1]-box[0][1])
617
+ extent = [box[0][0], box[1][0], box[0][1], box[1][1]]
618
+ plt.imshow(out.T, origin="lower", extent=extent, aspect=aspect)
619
+ return out
620
+
621
+ def landscapes(self, degree:int, ks:list|np.ndarray=[0],box=None, resolution:list|np.ndarray=[100,100], bool plot=False):
622
+ r"""Computes the multiparameter landscape from a PyModule. Python interface only bifiltrations.
623
+
624
+ Parameters
625
+ ----------
626
+
627
+ - degree : integer
628
+ The homology degree of the landscape.
629
+ - ks = 0 : list of int
630
+ the k-th landscape
631
+ - resolution = [50,50] : pair of integers
632
+ Resolution of the image.
633
+ - box = None : in the format [[a,b], [c,d]]
634
+ If nontrivial, compute the landscape of this box. Default is the PyModule box.
635
+ - plot = True : bool
636
+ If true, plots the images;
637
+ Returns
638
+ -------
639
+
640
+ The landscapes of the module with parameters ks.
641
+
642
+ """
643
+ import matplotlib.pyplot as plt
644
+ if box is None:
645
+ box = self.get_box()
646
+ out = np.array(self.cmod.get_landscapes(degree, ks, Box[{{CTYPE}}](box), resolution))
647
+ if plot:
648
+ to_plot = np.sum(out, axis=0)
649
+ aspect = (box[1][0]-box[0][0]) / (box[1][1]-box[0][1])
650
+ extent = [box[0][0], box[1][0], box[0][1], box[1][1]]
651
+ plt.imshow(to_plot.T, origin="lower", extent=extent, aspect=aspect)
652
+ return out
653
+
654
+
655
+ def representation(self, degrees=None, double bandwidth=0.1,
656
+ resolution:Sequence[int]|int=50,
657
+ kernel:Literal["gaussian","linear","linear2","exponential"]|Callable = "gaussian",
658
+ bool signed=False,
659
+ bool normalize=False, bool plot=False,
660
+ bool save=False, int dpi=200,double p=2., box=None,
661
+ bool flatten=False, int n_jobs=0,
662
+ grid = None)->np.ndarray:
663
+ r"""Computes a representation of the module, using
664
+
665
+ [A Framework for Fast and Stable Representations of Multiparameter Persistent Homology Decompositions, Neurips2023]
666
+
667
+ Parameters
668
+ ----------
669
+
670
+ - degrees = None : integer list
671
+ If given returns only the image(s) of homology degrees `degrees`.
672
+ - bandwidth = 0.1 : float
673
+ Image parameter.
674
+ - resolution = [100,100] : pair of integers
675
+ Resolution of the image(s).
676
+ - normalize = True : Boolean
677
+ Ensures that the image belongs to [0,1].
678
+ - plot = False : Boolean
679
+ If true, plots the images;
680
+ - flatten=False :
681
+ If True, reshapes the output to a flattened shape.
682
+ - kernel: Either linear, gaussian, or callable
683
+ The kernel to apply to the matrix $(d(x,I), x \in \mathrm{pixels}, I\in \mathrm{summands})$.
684
+ signature should be : (distance matrix, summand_weights, bandwidth, p) -> representation
685
+
686
+ Returns
687
+ -------
688
+
689
+ The list of images, or the image of fixed dimension.
690
+ """
691
+ import matplotlib.pyplot as plt
692
+ # box = kwargs.get("box",[self.get_bottom(),self.get_top()])
693
+ if box is None:
694
+ box = self.get_box()
695
+ num_parameters = self.num_parameters
696
+ if degrees is None:
697
+ degrees = np.arange(self.max_degree +1)
698
+ num_degrees = len(degrees)
699
+ try:
700
+ int(resolution)
701
+ resolution = [resolution]*num_parameters
702
+ except:
703
+ pass
704
+
705
+ if grid is None:
706
+ grid = [np.linspace(*np.asarray(box)[:,parameter], num=res) for parameter, res in zip(range(num_parameters), resolution)]
707
+ else:
708
+ resolution = tuple(len(g) for g in grid)
709
+ coordinates = mpg.todense(grid)
710
+
711
+ if kernel == "linear":
712
+ assert not signed, "This kernel is not compatible with signed."
713
+ concatenated_images = self._compute_pixels(coordinates, degrees=degrees, box=box, delta=bandwidth, p=p, normalize=normalize,n_jobs=n_jobs)
714
+ else:
715
+ if kernel == "linear2":
716
+ def todo(PyModule_{{SHORT}} mod_degree):
717
+ x = mod_degree.distance_to(coordinates,signed=signed)
718
+ w = mod_degree.get_interleavings()[None]**p
719
+ s = np.where(x>=0,1,-1) if signed else 1
720
+ x = np.abs(x)
721
+ return (s*np.where(x<bandwidth,(bandwidth-x)/bandwidth,0)*w).sum(1)
722
+ elif kernel == "gaussian":
723
+ def todo(PyModule_{{SHORT}} mod_degree):
724
+ x = mod_degree.distance_to(coordinates,signed=signed)
725
+ w = mod_degree.get_interleavings()[None]**p
726
+ s = np.where(x>=0,1,-1) if signed else 1
727
+ return (s*np.exp( -.5*((x/bandwidth)**2))*w).sum(1)
728
+ elif kernel == "exponential":
729
+ def todo(PyModule_{{SHORT}} mod_degree):
730
+ x = mod_degree.distance_to(coordinates,signed=signed)
731
+ w = mod_degree.get_interleavings()[None]**p
732
+ s = np.where(x>=0,1,-1) if signed else 1
733
+ x = np.abs(x)
734
+ return (s*np.exp( -((np.abs(x)/bandwidth)))*w).sum(1)
735
+ else:
736
+ assert callable(kernel), r"""
737
+ Kernel should be
738
+ gaussian, linear, linear2, exponential or callable,
739
+ with signature
740
+ (array[shape=(N, M)], array[shape=(M)])-> array(shape=(N)),
741
+ the first argument being a distance matrix (pts) vs (summands of the module)
742
+ and the second argument is a weight vector (weight(summand) for summand in module).
743
+ Note that the distance can be signed.
744
+ """
745
+ def todo(PyModule_{{SHORT}} mod_degree):
746
+ x = mod_degree.distance_to(coordinates,signed=signed, n_jobs=n_jobs)
747
+ w = mod_degree.get_interleavings()[None]**p
748
+ return kernel(x/bandwidth,w)
749
+ concatenated_images = np.stack(
750
+ Parallel(n_jobs = n_jobs if n_jobs else -1, backend= "threading")(
751
+ delayed(todo)(self.get_module_of_degree(degree))
752
+ for degree in degrees
753
+ )
754
+ )
755
+
756
+ if flatten:
757
+ image_vector = concatenated_images.reshape((len(degrees),-1))
758
+ if plot:
759
+ raise ValueError("Unflatten to plot.")
760
+ return image_vector
761
+ else:
762
+ image_vector = concatenated_images.reshape((len(degrees),*resolution))
763
+ if plot:
764
+ assert num_parameters == 2, "Plot only available for 2-parameter modules"
765
+ import multipers.plots
766
+ i=0
767
+ n_plots = len(image_vector)
768
+ scale = 4
769
+ if n_plots >1:
770
+ fig, axs = plt.subplots(1,n_plots, figsize=(n_plots*scale,scale))
771
+ else:
772
+ fig = plt.gcf()
773
+ axs = [plt.gca()]
774
+ for image, degree, i in zip(image_vector, degrees, range(num_degrees)):
775
+ ax = axs[i]
776
+ temp = multipers.plots.plot_surface(grid, image.T, ax=ax)
777
+ plt.colorbar(temp, ax = ax)
778
+ if degree < 0 :
779
+ ax.set_title(rf"$H_{i}$ $2$-persistence image")
780
+ if degree >= 0:
781
+ ax.set_title(rf"$H_{degree}$ $2$-persistence image")
782
+ return image_vector
783
+
784
+ def euler_char(self, points:list|np.ndarray) -> np.ndarray:
785
+ r""" Computes the Euler Characteristic of the filtered complex at given (multiparameter) time
786
+
787
+ Parameters
788
+ ----------
789
+
790
+ points: list[float] | list[list[float]] | np.ndarray
791
+ List of filtration values on which to compute the euler characteristic.
792
+ WARNING FIXME : the points have to have the same dimension as the simplextree.
793
+
794
+ Returns
795
+ -------
796
+
797
+ The list of euler characteristic values
798
+ """
799
+ if len(points) == 0:
800
+ return []
801
+ if type(points[0]) is float:
802
+ points = [points]
803
+ if type(points) is np.ndarray:
804
+ assert len(points.shape) in [1,2]
805
+ if len(points.shape) == 1:
806
+ points = [points]
807
+
808
+ cdef {{CTYPE}}[:,:] points_view = np.asarray(points, dtype = {{PYTYPE}})
809
+ cdef vector[One_critical_filtration[{{CTYPE}}]] c_points = _py2v1c_{{SHORT}}(points_view)
810
+ # cdef One_critical_filtration temp
811
+ # for point in points:
812
+ # temp.clear()
813
+ # for truc in point:
814
+ # temp.push_back(<{{CTYPE}}>(truc))
815
+ # c_points.push_back(temp)
816
+ cdef Module[{{CTYPE}}] c_mod = self.cmod
817
+ with nogil:
818
+ c_euler = c_mod.euler_curve(c_points)
819
+ euler = c_euler
820
+ return np.asarray(euler, dtype=int)
821
+ def to_idx(self,grid):
822
+ cdef vector[vector[{{CTYPE}}]] cgrid = grid
823
+ cdef vector[vector[pair[vector[vector[int]],vector[vector[int]]]]] out
824
+ with nogil:
825
+ out = self.cmod.to_idx(cgrid)
826
+ return tuple(tuple((np.asarray(I.first,dtype=np.int64), np.asarray(I.second, dtype=np.int64)) for I in Is_of_degree) for Is_of_degree in out)
827
+
828
+ @cython.wraparound(False)
829
+ @cython.boundscheck(False)
830
+ def to_flat_idx(self,grid):
831
+ if len(self) == 0:
832
+ return np.empty((2,0), dtype = np.int32), np.empty((0, self.num_parameters), dtype = np.int32), np.empty((0, self.num_parameters), dtype = np.int32)
833
+ cdef vector[vector[{{CTYPE}}]] cgrid = grid
834
+ cdef vector[vector[vector[int]]] out
835
+ cdef int num_summands, num_births, num_deaths
836
+ cdef int num_parameters = self.num_parameters
837
+ with nogil:
838
+ out = self.cmod.to_flat_idx(cgrid)
839
+ num_summands = out[0][0].size()
840
+ num_births = out[1].size()
841
+ num_deaths = out[2].size()
842
+ idx = np.empty((2, num_summands),dtype=np.int32 )
843
+ births = np.empty((num_births,num_parameters),dtype=np.int32)
844
+ deaths = np.empty((num_deaths,num_parameters),dtype=np.int32)
845
+
846
+ cdef int32_t[:,:] idx_view = idx
847
+ cdef int32_t[:,:] births_view = births
848
+ cdef int32_t[:,:] deaths_view = deaths
849
+
850
+ with nogil:
851
+ for i in range(num_summands):
852
+ idx_view[0,i] = out[0][0][i]
853
+ idx_view[1,i] = out[0][1][i]
854
+ for i in range(num_births):
855
+ for j in range(num_parameters):
856
+ births_view[i,j] = out[1][i][j]
857
+ for i in range(num_deaths):
858
+ for j in range(num_parameters):
859
+ deaths_view[i,j] = out[2][i][j]
860
+
861
+ return idx, births,deaths
862
+
863
+ def distances_idx_to(self, pts, bool full=False, int n_jobs=1):
864
+ pts = np.asarray(pts)
865
+ if pts.ndim == 1:
866
+ pts = pts[None]
867
+ cdef vector[vector[{{CTYPE}}]] cpts = pts
868
+ cdef vector[vector[vector[int]]] out
869
+ with nogil:
870
+ out = self.cmod.compute_distances_idx_to(cpts,full, n_jobs)
871
+ return np.asarray(out, dtype=np.int32)
872
+
873
+ def distance_to(self, pts, bool signed=False, int n_jobs = 0)->np.ndarray:
874
+ r"""
875
+ Distance from a point to each summand's support.
876
+ Signed distance is the distance to the boundary,
877
+ with negative values inside the summands.
878
+
879
+ pts of shape (num_pts, num_parameters)
880
+
881
+ output shape : (num_pts,num_summands)
882
+ """
883
+ pts = np.asarray(pts)
884
+ if pts.ndim == 1:
885
+ pts = pts[None]
886
+ assert pts.shape[-1] == self.num_parameters
887
+ cdef vector[vector[{{CTYPE}}]] cpts = pts
888
+ # cdef vector[vector[{{CTYPE}}]] out
889
+ to_fill = np.empty(shape=(cpts.size(),len(self)), dtype = {{PYTYPE}})
890
+ cdef {{CTYPE}}[:,:] c_to_fill = to_fill
891
+ cdef {{CTYPE}}* data_ptr = &c_to_fill[0,0]
892
+ with nogil:
893
+ self.cmod.compute_distances_to(data_ptr, cpts,signed, n_jobs)
894
+ return to_fill
895
+
896
+ def get_interleavings(self,box=None):
897
+ if box is None:
898
+ box = self.get_box()
899
+ cdef Box[{{CTYPE}}] cbox = Box[{{CTYPE}}](box)
900
+ return np.asarray(self.cmod.get_interleavings(cbox))
901
+
902
+ cdef class PyMultiDiagramPoint_{{SHORT}}:
903
+ cdef MultiDiagram_point[One_critical_filtration[{{CTYPE}}]] point
904
+ cdef set(self, MultiDiagram_point[One_critical_filtration[{{CTYPE}}]] pt):
905
+ self.point = pt
906
+ return self
907
+
908
+ def get_degree(self):
909
+ return self.point.get_dimension()
910
+ def get_birth(self):
911
+ cdef One_critical_filtration[{{CTYPE}}] v = self.point.get_birth()
912
+ return _ff21cview_{{SHORT}}(&v, copy=True)
913
+ def get_death(self):
914
+ cdef One_critical_filtration[{{CTYPE}}] v = self.point.get_death()
915
+ return _ff21cview_{{SHORT}}(&v, copy=True)
916
+
917
+
918
+ cdef class PyMultiDiagram_{{SHORT}}:
919
+ r"""
920
+ Stores the diagram of a PyModule on a line
921
+ """
922
+ cdef MultiDiagram[One_critical_filtration[{{CTYPE}}], {{CTYPE}}] multiDiagram
923
+ cdef set(self, MultiDiagram[One_critical_filtration[{{CTYPE}}], {{CTYPE}}] m):
924
+ self.multiDiagram = m
925
+ return self
926
+ def get_points(self, degree:int=-1) -> np.ndarray:
927
+ cdef vector[pair[vector[{{CTYPE}}],vector[{{CTYPE}}]]] out = self.multiDiagram.get_points(degree)
928
+ if len(out) == 0 and len(self) == 0:
929
+ return np.empty() # TODO Retrieve good number of parameters if there is no points in diagram
930
+ if len(out) == 0:
931
+ return np.empty((0,2,self.multiDiagram.at(0).get_dimension())) # gets the number of parameters
932
+ return np.array(out)
933
+ def to_multipers(self, dimension:int):
934
+ return self.multiDiagram.to_multipers(dimension)
935
+ def __len__(self) -> int:
936
+ return self.multiDiagram.size()
937
+ def __getitem__(self,i:int) -> PyMultiDiagramPoint_{{SHORT}}:
938
+ return PyMultiDiagramPoint_{{SHORT}}().set(self.multiDiagram.at(i % self.multiDiagram.size()))
939
+ cdef class PyMultiDiagrams_{{SHORT}}:
940
+ """
941
+ Stores the barcodes of a PyModule on multiple lines
942
+ """
943
+ cdef MultiDiagrams[One_critical_filtration[{{CTYPE}}], {{CTYPE}}] multiDiagrams
944
+ cdef set(self,MultiDiagrams[One_critical_filtration[{{CTYPE}}], {{CTYPE}}] m):
945
+ self.multiDiagrams = m
946
+ return self
947
+ def to_multipers(self):
948
+ out = self.multiDiagrams.to_multipers()
949
+ return [np.asarray(summand) for summand in out]
950
+ def __getitem__(self,i:int):
951
+ if i >=0 :
952
+ return PyMultiDiagram_{{SHORT}}().set(self.multiDiagrams.at(i))
953
+ else:
954
+ return PyMultiDiagram_{{SHORT}}().set(self.multiDiagrams.at( self.multiDiagrams.size() - i))
955
+ def __len__(self):
956
+ return self.multiDiagrams.size()
957
+ def get_points(self, degree:int=-1):
958
+ return self.multiDiagrams.get_points()
959
+ cdef _get_plot_bars(self, dimension:int=-1, min_persistence:float=0):
960
+ return self.multiDiagrams._for_python_plot(dimension, min_persistence);
961
+ def plot(self, degree:int=-1, min_persistence:float=0):
962
+ """
963
+ Plots the barcodes.
964
+
965
+ Parameters
966
+ ----------
967
+
968
+ - degree:int=-1
969
+ Only plots the bars of specified homology degree. Useful when the multidiagrams contains multiple dimenions
970
+ - min_persistence:float=0
971
+ Only plot bars of length greater than this value. Useful to reduce the time to plot.
972
+
973
+ Warning
974
+ -------
975
+
976
+ If the barcodes are not thresholded, essential barcodes will not be displayed !
977
+
978
+ """
979
+ from cycler import cycler
980
+ import matplotlib
981
+ import matplotlib.pyplot as plt
982
+ if len(self) == 0: return
983
+ _cmap = matplotlib.colormaps["Spectral"]
984
+ multibarcodes_, colors = self._get_plot_bars(degree, min_persistence)
985
+ n_summands = np.max(colors)+1 if len(colors)>0 else 1
986
+
987
+ plt.rc('axes', prop_cycle = cycler('color', [_cmap(i/n_summands) for i in colors]))
988
+ return plt.plot(*multibarcodes_)
989
+ {{endif}}
990
+
991
+
992
+ cdef dump_summand_{{SHORT}}(Summand[{{CTYPE}}]& summand):
993
+ cdef vector[One_critical_filtration[{{CTYPE}}]] births = summand.get_birth_list()
994
+ cdef vector[One_critical_filtration[{{CTYPE}}]] deaths = summand.get_death_list()
995
+ return (
996
+ np.array(_vff21cview_{{SHORT}}(births)),
997
+ np.array(_vff21cview_{{SHORT}}(deaths)),
998
+ summand.get_dimension(),
999
+ )
1000
+
1001
+ cdef inline Summand[{{CTYPE}}] summand_from_dump_{{SHORT}}(summand_dump):
1002
+ cdef vector[One_critical_filtration[{{CTYPE}}]] births = _py2v1c_{{SHORT}}(summand_dump[0])
1003
+ cdef vector[One_critical_filtration[{{CTYPE}}]] deaths = _py2v1c_{{SHORT}}(summand_dump[1])
1004
+ cdef int dim = summand_dump[2]
1005
+ return Summand[{{CTYPE}}](births,deaths,dim)
1006
+
1007
+ cdef dump_cmod_{{SHORT}}(Module[{{CTYPE}}]& mod):
1008
+ cdef Box[{{CTYPE}}] cbox = mod.get_box()
1009
+ cdef int dim = mod.get_dimension()
1010
+ cdef cnp.ndarray[{{CTYPE}}, ndim=1] bottom_corner = _ff21cview_{{SHORT}}(&cbox.get_lower_corner())
1011
+ cdef cnp.ndarray[{{CTYPE}}, ndim=1] top_corner = _ff21cview_{{SHORT}}(&cbox.get_upper_corner())
1012
+ box = np.asarray([bottom_corner, top_corner])
1013
+ summands = tuple(dump_summand_{{SHORT}}(summand) for summand in mod)
1014
+ return box, summands
1015
+
1016
+ cdef Module[{{CTYPE}}] cmod_from_dump_{{SHORT}}(module_dump):
1017
+ box = module_dump[0]
1018
+ summands = module_dump[1]
1019
+ cdef Module[{{CTYPE}}] out_module = Module[{{CTYPE}}]()
1020
+ out_module.set_box(Box[{{CTYPE}}](box))
1021
+ for i in range(len(summands)):
1022
+ out_module.add_summand(summand_from_dump_{{SHORT}}(summands[i]))
1023
+ return out_module
1024
+
1025
+
1026
+ def from_dump_{{SHORT}}(dump)->PyModule_{{SHORT}}:
1027
+ r"""Retrieves a PyModule from a previous dump.
1028
+
1029
+ Parameters
1030
+ ----------
1031
+
1032
+ dump: either the output of the dump function, or a file containing the output of a dump.
1033
+ The dumped module to retrieve
1034
+
1035
+ Returns
1036
+ -------
1037
+
1038
+ PyModule
1039
+ The retrieved module.
1040
+ """
1041
+ # TODO : optimize...
1042
+ mod = PyModule_{{SHORT}}()
1043
+ if type(dump) is str:
1044
+ dump = pickle.load(open(dump, "rb"))
1045
+ cdef Module[{{CTYPE}}] cmod = cmod_from_dump_{{SHORT}}(dump)
1046
+ mod.cmod = cmod
1047
+ return mod
1048
+
1049
+
1050
+ {{endfor}}
1051
+
1052
+
1053
+ global PyModule_type, PySummand_type
1054
+ PyModule_type = Union[
1055
+ {{for CTYPE, PYTYPE, SHORT in value_types}}
1056
+ PyModule_{{SHORT}},
1057
+ {{endfor}}
1058
+ ]
1059
+ PySummand_type = Union[
1060
+ {{for CTYPE, PYTYPE, SHORT in value_types}}
1061
+ PySummand_{{SHORT}},
1062
+ {{endfor}}
1063
+ ]
1064
+
1065
+ PyBox_type = Union[
1066
+ {{for CTYPE, PYTYPE, SHORT in value_types}}
1067
+ PyBox_{{SHORT}},
1068
+ {{endfor}}
1069
+ ]
1070
+
1071
+
1072
+ PyMultiDiagram_type = Union[
1073
+ {{for CTYPE, PYTYPE, SHORT in value_types}}
1074
+ {{if SHORT[0] == 'f'}}
1075
+ PyMultiDiagram_{{SHORT}},
1076
+ {{endif}}
1077
+ {{endfor}}
1078
+ ]
1079
+
1080
+
1081
+ PyMultiDiagrams_type = Union[
1082
+ {{for CTYPE, PYTYPE, SHORT in value_types}}
1083
+ {{if SHORT[0] == 'f'}}
1084
+ PyMultiDiagrams_{{SHORT}},
1085
+ {{endif}}
1086
+ {{endfor}}
1087
+ ]
1088
+
1089
+ def is_mma(stuff):
1090
+ return (False
1091
+ {{for CTYPE, PYTYPE, SHORT in value_types}}
1092
+ or isinstance(stuff,PyModule_{{SHORT}})
1093
+ {{endfor}}
1094
+ )