multipers 2.2.3__cp310-cp310-win_amd64.whl → 2.3.1__cp310-cp310-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 (182) hide show
  1. multipers/__init__.py +33 -31
  2. multipers/_signed_measure_meta.py +430 -430
  3. multipers/_slicer_meta.py +211 -212
  4. multipers/data/MOL2.py +458 -458
  5. multipers/data/UCR.py +18 -18
  6. multipers/data/graphs.py +466 -466
  7. multipers/data/immuno_regions.py +27 -27
  8. multipers/data/pytorch2simplextree.py +90 -90
  9. multipers/data/shape3d.py +101 -101
  10. multipers/data/synthetic.py +113 -111
  11. multipers/distances.py +198 -198
  12. multipers/filtration_conversions.pxd.tp +84 -84
  13. multipers/filtrations/__init__.py +18 -0
  14. multipers/{ml/convolutions.py → filtrations/density.py} +563 -520
  15. multipers/filtrations/filtrations.py +289 -0
  16. multipers/filtrations.pxd +224 -224
  17. multipers/function_rips.cp310-win_amd64.pyd +0 -0
  18. multipers/function_rips.pyx +105 -105
  19. multipers/grids.cp310-win_amd64.pyd +0 -0
  20. multipers/grids.pyx +350 -350
  21. multipers/gudhi/Persistence_slices_interface.h +132 -132
  22. multipers/gudhi/Simplex_tree_interface.h +239 -245
  23. multipers/gudhi/Simplex_tree_multi_interface.h +516 -561
  24. multipers/gudhi/cubical_to_boundary.h +59 -59
  25. multipers/gudhi/gudhi/Bitmap_cubical_complex.h +450 -450
  26. multipers/gudhi/gudhi/Bitmap_cubical_complex_base.h +1070 -1070
  27. multipers/gudhi/gudhi/Bitmap_cubical_complex_periodic_boundary_conditions_base.h +579 -579
  28. multipers/gudhi/gudhi/Debug_utils.h +45 -45
  29. multipers/gudhi/gudhi/Fields/Multi_field.h +484 -484
  30. multipers/gudhi/gudhi/Fields/Multi_field_operators.h +455 -455
  31. multipers/gudhi/gudhi/Fields/Multi_field_shared.h +450 -450
  32. multipers/gudhi/gudhi/Fields/Multi_field_small.h +531 -531
  33. multipers/gudhi/gudhi/Fields/Multi_field_small_operators.h +507 -507
  34. multipers/gudhi/gudhi/Fields/Multi_field_small_shared.h +531 -531
  35. multipers/gudhi/gudhi/Fields/Z2_field.h +355 -355
  36. multipers/gudhi/gudhi/Fields/Z2_field_operators.h +376 -376
  37. multipers/gudhi/gudhi/Fields/Zp_field.h +420 -420
  38. multipers/gudhi/gudhi/Fields/Zp_field_operators.h +400 -400
  39. multipers/gudhi/gudhi/Fields/Zp_field_shared.h +418 -418
  40. multipers/gudhi/gudhi/Flag_complex_edge_collapser.h +337 -337
  41. multipers/gudhi/gudhi/Matrix.h +2107 -2107
  42. multipers/gudhi/gudhi/Multi_critical_filtration.h +1038 -1038
  43. multipers/gudhi/gudhi/Multi_persistence/Box.h +171 -171
  44. multipers/gudhi/gudhi/Multi_persistence/Line.h +282 -282
  45. multipers/gudhi/gudhi/Off_reader.h +173 -173
  46. multipers/gudhi/gudhi/One_critical_filtration.h +1433 -1431
  47. multipers/gudhi/gudhi/Persistence_matrix/Base_matrix.h +769 -769
  48. multipers/gudhi/gudhi/Persistence_matrix/Base_matrix_with_column_compression.h +686 -686
  49. multipers/gudhi/gudhi/Persistence_matrix/Boundary_matrix.h +842 -842
  50. multipers/gudhi/gudhi/Persistence_matrix/Chain_matrix.h +1350 -1350
  51. multipers/gudhi/gudhi/Persistence_matrix/Id_to_index_overlay.h +1105 -1105
  52. multipers/gudhi/gudhi/Persistence_matrix/Position_to_index_overlay.h +859 -859
  53. multipers/gudhi/gudhi/Persistence_matrix/RU_matrix.h +910 -910
  54. multipers/gudhi/gudhi/Persistence_matrix/allocators/entry_constructors.h +139 -139
  55. multipers/gudhi/gudhi/Persistence_matrix/base_pairing.h +230 -230
  56. multipers/gudhi/gudhi/Persistence_matrix/base_swap.h +211 -211
  57. multipers/gudhi/gudhi/Persistence_matrix/boundary_cell_position_to_id_mapper.h +60 -60
  58. multipers/gudhi/gudhi/Persistence_matrix/boundary_face_position_to_id_mapper.h +60 -60
  59. multipers/gudhi/gudhi/Persistence_matrix/chain_pairing.h +136 -136
  60. multipers/gudhi/gudhi/Persistence_matrix/chain_rep_cycles.h +190 -190
  61. multipers/gudhi/gudhi/Persistence_matrix/chain_vine_swap.h +616 -616
  62. multipers/gudhi/gudhi/Persistence_matrix/columns/chain_column_extra_properties.h +150 -150
  63. multipers/gudhi/gudhi/Persistence_matrix/columns/column_dimension_holder.h +106 -106
  64. multipers/gudhi/gudhi/Persistence_matrix/columns/column_utilities.h +219 -219
  65. multipers/gudhi/gudhi/Persistence_matrix/columns/entry_types.h +327 -327
  66. multipers/gudhi/gudhi/Persistence_matrix/columns/heap_column.h +1140 -1140
  67. multipers/gudhi/gudhi/Persistence_matrix/columns/intrusive_list_column.h +934 -934
  68. multipers/gudhi/gudhi/Persistence_matrix/columns/intrusive_set_column.h +934 -934
  69. multipers/gudhi/gudhi/Persistence_matrix/columns/list_column.h +980 -980
  70. multipers/gudhi/gudhi/Persistence_matrix/columns/naive_vector_column.h +1092 -1092
  71. multipers/gudhi/gudhi/Persistence_matrix/columns/row_access.h +192 -192
  72. multipers/gudhi/gudhi/Persistence_matrix/columns/set_column.h +921 -921
  73. multipers/gudhi/gudhi/Persistence_matrix/columns/small_vector_column.h +1093 -1093
  74. multipers/gudhi/gudhi/Persistence_matrix/columns/unordered_set_column.h +1012 -1012
  75. multipers/gudhi/gudhi/Persistence_matrix/columns/vector_column.h +1244 -1244
  76. multipers/gudhi/gudhi/Persistence_matrix/matrix_dimension_holders.h +186 -186
  77. multipers/gudhi/gudhi/Persistence_matrix/matrix_row_access.h +164 -164
  78. multipers/gudhi/gudhi/Persistence_matrix/ru_pairing.h +156 -156
  79. multipers/gudhi/gudhi/Persistence_matrix/ru_rep_cycles.h +376 -376
  80. multipers/gudhi/gudhi/Persistence_matrix/ru_vine_swap.h +540 -540
  81. multipers/gudhi/gudhi/Persistent_cohomology/Field_Zp.h +118 -118
  82. multipers/gudhi/gudhi/Persistent_cohomology/Multi_field.h +173 -173
  83. multipers/gudhi/gudhi/Persistent_cohomology/Persistent_cohomology_column.h +128 -128
  84. multipers/gudhi/gudhi/Persistent_cohomology.h +745 -745
  85. multipers/gudhi/gudhi/Points_off_io.h +171 -171
  86. multipers/gudhi/gudhi/Simple_object_pool.h +69 -69
  87. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_iterators.h +463 -463
  88. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h +83 -83
  89. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_siblings.h +106 -106
  90. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_star_simplex_iterators.h +277 -277
  91. multipers/gudhi/gudhi/Simplex_tree/hooks_simplex_base.h +62 -62
  92. multipers/gudhi/gudhi/Simplex_tree/indexing_tag.h +27 -27
  93. multipers/gudhi/gudhi/Simplex_tree/serialization_utils.h +62 -62
  94. multipers/gudhi/gudhi/Simplex_tree/simplex_tree_options.h +157 -157
  95. multipers/gudhi/gudhi/Simplex_tree.h +2794 -2794
  96. multipers/gudhi/gudhi/Simplex_tree_multi.h +152 -163
  97. multipers/gudhi/gudhi/distance_functions.h +62 -62
  98. multipers/gudhi/gudhi/graph_simplicial_complex.h +104 -104
  99. multipers/gudhi/gudhi/persistence_interval.h +253 -253
  100. multipers/gudhi/gudhi/persistence_matrix_options.h +170 -170
  101. multipers/gudhi/gudhi/reader_utils.h +367 -367
  102. multipers/gudhi/mma_interface_coh.h +256 -255
  103. multipers/gudhi/mma_interface_h0.h +223 -231
  104. multipers/gudhi/mma_interface_matrix.h +291 -282
  105. multipers/gudhi/naive_merge_tree.h +536 -575
  106. multipers/gudhi/scc_io.h +310 -289
  107. multipers/gudhi/truc.h +957 -888
  108. multipers/io.cp310-win_amd64.pyd +0 -0
  109. multipers/io.pyx +714 -711
  110. multipers/ml/accuracies.py +90 -90
  111. multipers/ml/invariants_with_persistable.py +79 -79
  112. multipers/ml/kernels.py +176 -176
  113. multipers/ml/mma.py +713 -714
  114. multipers/ml/one.py +472 -472
  115. multipers/ml/point_clouds.py +352 -346
  116. multipers/ml/signed_measures.py +1589 -1589
  117. multipers/ml/sliced_wasserstein.py +461 -461
  118. multipers/ml/tools.py +113 -113
  119. multipers/mma_structures.cp310-win_amd64.pyd +0 -0
  120. multipers/mma_structures.pxd +127 -127
  121. multipers/mma_structures.pyx +4 -8
  122. multipers/mma_structures.pyx.tp +1083 -1085
  123. multipers/multi_parameter_rank_invariant/diff_helpers.h +84 -93
  124. multipers/multi_parameter_rank_invariant/euler_characteristic.h +97 -97
  125. multipers/multi_parameter_rank_invariant/function_rips.h +322 -322
  126. multipers/multi_parameter_rank_invariant/hilbert_function.h +769 -769
  127. multipers/multi_parameter_rank_invariant/persistence_slices.h +148 -148
  128. multipers/multi_parameter_rank_invariant/rank_invariant.h +369 -369
  129. multipers/multiparameter_edge_collapse.py +41 -41
  130. multipers/multiparameter_module_approximation/approximation.h +2298 -2295
  131. multipers/multiparameter_module_approximation/combinatory.h +129 -129
  132. multipers/multiparameter_module_approximation/debug.h +107 -107
  133. multipers/multiparameter_module_approximation/format_python-cpp.h +286 -286
  134. multipers/multiparameter_module_approximation/heap_column.h +238 -238
  135. multipers/multiparameter_module_approximation/images.h +79 -79
  136. multipers/multiparameter_module_approximation/list_column.h +174 -174
  137. multipers/multiparameter_module_approximation/list_column_2.h +232 -232
  138. multipers/multiparameter_module_approximation/ru_matrix.h +347 -347
  139. multipers/multiparameter_module_approximation/set_column.h +135 -135
  140. multipers/multiparameter_module_approximation/structure_higher_dim_barcode.h +36 -36
  141. multipers/multiparameter_module_approximation/unordered_set_column.h +166 -166
  142. multipers/multiparameter_module_approximation/utilities.h +403 -419
  143. multipers/multiparameter_module_approximation/vector_column.h +223 -223
  144. multipers/multiparameter_module_approximation/vector_matrix.h +331 -331
  145. multipers/multiparameter_module_approximation/vineyards.h +464 -464
  146. multipers/multiparameter_module_approximation/vineyards_trajectories.h +649 -649
  147. multipers/multiparameter_module_approximation.cp310-win_amd64.pyd +0 -0
  148. multipers/multiparameter_module_approximation.pyx +218 -217
  149. multipers/pickle.py +90 -53
  150. multipers/plots.py +342 -334
  151. multipers/point_measure.cp310-win_amd64.pyd +0 -0
  152. multipers/point_measure.pyx +322 -320
  153. multipers/simplex_tree_multi.cp310-win_amd64.pyd +0 -0
  154. multipers/simplex_tree_multi.pxd +133 -133
  155. multipers/simplex_tree_multi.pyx +115 -48
  156. multipers/simplex_tree_multi.pyx.tp +1947 -1935
  157. multipers/slicer.cp310-win_amd64.pyd +0 -0
  158. multipers/slicer.pxd +301 -120
  159. multipers/slicer.pxd.tp +218 -214
  160. multipers/slicer.pyx +1570 -507
  161. multipers/slicer.pyx.tp +931 -914
  162. multipers/tensor/tensor.h +672 -672
  163. multipers/tensor.pxd +13 -13
  164. multipers/test.pyx +44 -44
  165. multipers/tests/__init__.py +57 -57
  166. multipers/torch/diff_grids.py +217 -217
  167. multipers/torch/rips_density.py +310 -304
  168. {multipers-2.2.3.dist-info → multipers-2.3.1.dist-info}/LICENSE +21 -21
  169. {multipers-2.2.3.dist-info → multipers-2.3.1.dist-info}/METADATA +21 -11
  170. multipers-2.3.1.dist-info/RECORD +182 -0
  171. {multipers-2.2.3.dist-info → multipers-2.3.1.dist-info}/WHEEL +1 -1
  172. multipers/tests/test_diff_helper.py +0 -73
  173. multipers/tests/test_hilbert_function.py +0 -82
  174. multipers/tests/test_mma.py +0 -83
  175. multipers/tests/test_point_clouds.py +0 -49
  176. multipers/tests/test_python-cpp_conversion.py +0 -82
  177. multipers/tests/test_signed_betti.py +0 -181
  178. multipers/tests/test_signed_measure.py +0 -89
  179. multipers/tests/test_simplextreemulti.py +0 -221
  180. multipers/tests/test_slicer.py +0 -221
  181. multipers-2.2.3.dist-info/RECORD +0 -189
  182. {multipers-2.2.3.dist-info → multipers-2.3.1.dist-info}/top_level.txt +0 -0
@@ -1,320 +1,322 @@
1
- # cimport multipers.tensor as mt
2
- from libc.stdint cimport intptr_t, uint16_t, uint32_t, int32_t, int64_t
3
- from libcpp.vector cimport vector
4
- from libcpp cimport bool, int, float
5
- import numpy as np
6
- cimport numpy as cnp
7
- import itertools
8
- from typing import Optional,Iterable
9
-
10
- from collections import defaultdict
11
- cnp.import_array()
12
- from scipy import sparse
13
-
14
-
15
- import multipers.grids as mpg
16
-
17
- ctypedef fused some_int:
18
- int32_t
19
- int64_t
20
- int
21
-
22
- ctypedef fused some_float:
23
- int32_t
24
- int64_t
25
- int
26
- float
27
- double
28
-
29
-
30
- import cython
31
- cimport cython
32
-
33
-
34
-
35
- # from scipy.sparse import coo_array
36
- # from scipy.ndimage import convolve1d
37
-
38
- def signed_betti(hilbert_function:np.ndarray, bool threshold=False):
39
- cdef int n = hilbert_function.ndim
40
- # zero out the "end" of the Hilbert function
41
- if threshold:
42
- for dimension in range(n):
43
- slicer = tuple([slice(None) if i != dimension else -1 for i in range(n)])
44
- hilbert_function[slicer] = 0
45
- for i in range(n):
46
- minus = tuple([slice(None) if j!=i else slice(0,-1) for j in range(n)])
47
- plus = tuple([slice(None) if j!=i else slice(1,None) for j in range(n)])
48
- hilbert_function[plus] -= hilbert_function[minus]
49
- return hilbert_function
50
-
51
- def rank_decomposition_by_rectangles(rank_invariant:np.ndarray, bool threshold=False):
52
- # takes as input the rank invariant of an n-parameter persistence module
53
- # M : [0, ..., s_1 - 1] x ... x [0, ..., s_n - 1] ---> Vec
54
- # on a grid with dimensions of sizes s_1, ..., s_n. The input is assumed to be
55
- # given as a tensor of dimensions (s_1, ..., s_n, s_1, ..., s_n), so that,
56
- # at index [i_1, ..., i_n, j_1, ..., j_n] we have the rank of the structure
57
- # map M(i) -> M(j), where i = (i_1, ..., i_n) and j = (j_1, ..., j_n), and
58
- # i <= j, meaning that i_1 <= j_1, ..., i_n <= j_n.
59
- # NOTE :
60
- # - About the input, we assume that, if not( i <= j ), then at index
61
- # [i_1, ..., i_n, j_1, ..., j_n] we have a zero.
62
- # - Similarly, the output at index [i_1, ..., i_n, j_1, ..., j_n] only
63
- # makes sense when i <= j. For indices where not( i <= j ) the output
64
- # may take arbitrary values and they should be ignored.
65
- cdef int n = rank_invariant.ndim // 2
66
- if threshold:
67
- # zero out the "end"
68
- for dimension in range(n):
69
- slicer = tuple(
70
- [slice(None) for _ in range(n)]
71
- + [slice(None) if i != dimension else -1 for i in range(n)]
72
- )
73
- rank_invariant[slicer] = 0
74
- to_flip = tuple(range(n, 2 * n))
75
- return np.flip(signed_betti(np.flip(rank_invariant, to_flip)), to_flip)
76
-
77
-
78
-
79
-
80
-
81
-
82
-
83
- @cython.boundscheck(False)
84
- @cython.wraparound(False)
85
- def integrate_measure(
86
- some_float[:,:] pts,
87
- some_int[:] weights,
88
- filtration_grid:Optional[Iterable[np.ndarray]]=None,
89
- grid_strategy:str="regular",
90
- resolution:int|list[int]=100,
91
- bool return_grid=False,
92
- **get_fitration_kwargs,
93
- ):
94
- """
95
- Integrate a point measure on a grid.
96
- Measure is a sum of diracs, based on points `pts` and weights `weights`.
97
- For instance, if the signed measure comes from the hilbert signed measure,
98
- this integration will return the hilbert function on this grid.
99
- - pts : array of points (num_pts, D)
100
- - weights : array of weights (num_pts,)
101
- - filtration_grid (optional) : list of 1d arrays
102
- - resolution : int or list of int
103
- - return_grid : return the grid of the measure
104
- - **get_fitration_kwargs : arguments to compute the grid,
105
- if the grid is not given.
106
- """
107
- if filtration_grid is None:
108
- import multipers.simplex_tree_multi
109
- filtration_grid = mpg.compute_grid(
110
- np.asarray(pts).T,
111
- strategy=grid_strategy,
112
- resolution=resolution,
113
- **get_fitration_kwargs
114
- )
115
- resolution = np.asarray([len(f) for f in filtration_grid])
116
- cdef int num_pts = pts.shape[0]
117
- cdef int num_parameters = pts.shape[1]
118
- assert weights.shape[0] == num_pts
119
- out = np.zeros(shape=resolution, dtype=np.int32) ## dim cannot be known at compiletime
120
- # cdef some_float[:] filtration_of_parameter
121
- # cdef cnp.ndarray indices = np.zeros(shape=num_parameters, dtype=int)
122
- #
123
- pts_coords = np.empty((num_parameters, num_pts), dtype=np.int64)
124
- for parameter in range(num_parameters):
125
- pts_coords[parameter] = np.searchsorted(filtration_grid[parameter], pts[:,parameter])
126
- for i in range(num_pts):
127
- cone = tuple(slice(c,r) for r,c in zip(resolution,pts_coords[:,i]))
128
- out[cone] += weights[i]
129
- if return_grid:
130
- return out,filtration_grid
131
- return out
132
-
133
- ## for benchmark purposes
134
- def integrate_measure_python(pts, weights, filtrations):
135
- resolution = tuple([len(f) for f in filtrations])
136
- out = np.zeros(shape=resolution, dtype=pts.dtype)
137
- num_pts = pts.shape[0]
138
- num_parameters = pts.shape[1]
139
- for i in range(num_pts): #this is slow.
140
- indices = (filtrations[parameter]>=pts[i][parameter] for parameter in range(num_parameters))
141
- out[np.ix_(*indices)] += weights[i]
142
- return out
143
-
144
-
145
- def sparsify(x):
146
- """
147
- Given an arbitrary dimensional numpy array, returns (coordinates,data).
148
- --
149
- cost : scipy sparse + num_points*num_parameters^2 divisions
150
- """
151
- num_parameters = x.ndim
152
- sx = sparse.coo_array(x.ravel())
153
- idx = sx.col
154
- data = sx.data
155
- coords = np.empty((data.shape[0], num_parameters), dtype=np.int64)
156
- for parameter in range(num_parameters-1,-1,-1):
157
- idx,coord_of_parameter = np.divmod(idx, x.shape[parameter])
158
- coords[:, parameter] = coord_of_parameter
159
- return coords,data
160
-
161
-
162
-
163
-
164
- @cython.boundscheck(False)
165
- @cython.wraparound(False)
166
- def clean_signed_measure(some_float[:,:] pts, some_int[:] weights, dtype = np.float32):
167
- """
168
- Sum the diracs at the same locations. i.e.,
169
- returns the minimal sized measure to represent the input.
170
- Mostly useful for, e.g., euler_characteristic from simplical complexes.
171
- """
172
- cdef dict[tuple, int] out = {}
173
- cdef int num_diracs
174
- cdef int num_parameters
175
- num_diracs, num_parameters = pts.shape[:2]
176
- for i in range(num_diracs):
177
- key = tuple(pts[i]) # size cannot be known at compiletime
178
- out[key] = out.get(key,0)+ weights[i]
179
- num_keys = len(out)
180
- new_pts = np.fromiter(out.keys(), dtype=np.dtype((dtype,num_parameters)), count=num_keys)
181
- new_weights = np.fromiter(out.values(), dtype=np.int32, count=num_keys)
182
- idx = np.nonzero(new_weights)
183
- new_pts = new_pts[idx]
184
- new_weights = new_weights[idx]
185
- return (new_pts, new_weights)
186
-
187
- def clean_sms(sms):
188
- """
189
- Sum the diracs at the same locations. i.e.,
190
- returns the minimal sized measure to represent the input.
191
- Mostly useful for, e.g., euler_characteristic from simplical complexes.
192
- """
193
- return tuple(clean_signed_measure(pts,weights) for pts,weights in sms)
194
-
195
- def zero_out_sm(pts,weights, mass_default):
196
- """
197
- Zeros out the modules outside of \f$ \{ x\in \mathbb R^n \mid x \le \mathrm{mass_default}\}\f$.
198
- """
199
- from itertools import product
200
- # merge pts, weights
201
- PTS = np.concatenate([pts,weights[:,None]], axis=1)[None]
202
-
203
- num_diracs, num_parameters=pts.shape
204
-
205
- # corners of the square of dim num_parameters. shape : (num_corners,num_parameters)
206
- C = np.fromiter(product(*([[1,0]]*num_parameters)), dtype=np.dtype((np.bool_, num_parameters)),)
207
- Cw = 1-2*((~C).sum(axis=1)%2)
208
- # add 1 in the end to copy the shape
209
- C = np.concatenate([C, np.ones((C.shape[0],1))], axis=1).astype(np.bool_)
210
-
211
- # signs of each corner
212
- Cw = 1-2*((~C).sum(axis=1)%2)
213
-
214
- # something useless in the end to ensure that the shape is correct (as we added 1 in pts)
215
- mass_default = np.concatenate([mass_default,[np.nan]])
216
- mass_default = mass_default[None,None,:] ## num c, num_pts, num_params
217
- # each point `pt` becomes a corner of the square [`pt`, `mass_default`]
218
- new= np.where(C[:,None,:], PTS,mass_default).reshape(-1,(num_parameters+1))
219
- new_pts = new[:,:-1]
220
- new_masses = new[:,-1]*np.repeat(Cw,num_diracs)
221
- ## a bunch of stuff are in the same place, better clean it
222
- return clean_signed_measure(new_pts.astype(np.float64), new_masses.astype(np.int64), dtype=np.float64)
223
-
224
- def zero_out_sms(sms, mass_default):
225
- """
226
- Zeros out the modules outside of \f$ \{ x\in \mathbb R^n \mid x \le \mathrm{mass_default}\}\f$.
227
- """
228
- return tuple(zero_out_sm(pts,weights, mass_default) for pts,weights in sms)
229
-
230
- @cython.boundscheck(False)
231
- @cython.wraparound(False)
232
- def barcode_from_rank_sm(
233
- sm: tuple[np.ndarray, np.ndarray],
234
- basepoint: np.ndarray,
235
- direction: Optional[np.ndarray] = None,
236
- bool full = False,
237
- ):
238
- """
239
- Given a rank signed measure `sm` and a line with basepoint `basepoint` (1darray) and
240
- direction `direction` (1darray), projects the rank signed measure on the given line,
241
- and returns the associated estimated barcode.
242
- If full is True, the barcode is given as coordinates in R^{`num_parameters`} instead
243
- of coordinates w.r.t. the line.
244
- """
245
- basepoint = np.asarray(basepoint)
246
- num_parameters = basepoint.shape[0]
247
- x, w = sm
248
- assert (
249
- x.shape[1] // 2 == num_parameters
250
- ), f"Incoherent number of parameters. sm:{x.shape[1]//2} vs {num_parameters}"
251
- x, y = x[:, :num_parameters], x[:, num_parameters:]
252
- if direction is not None:
253
- direction = np.asarray(direction)
254
- ok_idx = direction > 0
255
- if ok_idx.sum() == 0:
256
- raise ValueError(f"Got invalid direction {direction}")
257
- zero_idx = None if np.all(ok_idx) else direction == 0
258
- else:
259
- direction = np.asarray([1], dtype=int)
260
- ok_idx = slice(None)
261
- zero_idx = None
262
- xa = np.max(
263
- (x[:, ok_idx] - basepoint[ok_idx]) / direction[ok_idx], axis=1, keepdims=1
264
- )
265
- ya = np.min(
266
- (y[:, ok_idx] - basepoint[ok_idx]) / direction[ok_idx], axis=1, keepdims=1
267
- )
268
- if zero_idx is not None:
269
- xb = np.where(x[:, zero_idx] <= basepoint[zero_idx], -np.inf, np.inf)
270
- yb = np.where(y[:, zero_idx] <= basepoint[zero_idx], -np.inf, np.inf)
271
- xs = np.max(np.concatenate([xa, xb], axis=1), axis=1, keepdims=1)
272
- ys = np.min(np.concatenate([ya, yb], axis=1), axis=1, keepdims=1)
273
- else:
274
- xs = xa
275
- ys = ya
276
- out = np.concatenate([xs, ys], axis=1,dtype=np.float64)
277
-
278
- ## TODO: check if this is faster than doing a np.repeat on x ?
279
- cdef dict[dict,tuple] d = {}
280
- cdef double[:,:] c_out = out # view
281
- cdef int64_t[:] c_w = np.asarray(w,dtype=np.int64)
282
- cdef int num_pts = out.shape[0]
283
- # for i, stuff in enumerate(out):
284
- # if stuff[0] < np.inf:
285
- # d[tuple(stuff)] = d.get(tuple(stuff), 0) + w[i]
286
- for i in range(num_pts):
287
- if c_out[i][0] < np.inf:
288
- machin = tuple(c_out[i])
289
- d[machin] = d.get(machin, 0) + c_w[i]
290
-
291
- out = np.fromiter(
292
- itertools.chain.from_iterable(([x] * w for x, w in d.items() if x[0] < x[1])),
293
- dtype=np.dtype((np.float64, 2)),
294
- )
295
- if full:
296
- out = basepoint[None, None] + out[..., None] * direction[None, None]
297
- return out
298
-
299
-
300
- def estimate_rank_from_rank_sm(sm:tuple, a, b) -> int:
301
- """
302
- Given a rank signed measure (sm) and two points (a) and (b),
303
- estimates the rank between these two points.
304
- """
305
- a = np.asarray(a)
306
- b = np.asarray(b)
307
- if not (a <= b).all():
308
- return 0
309
- x, w = sm
310
- num_parameters = x.shape[1] // 2
311
- assert (
312
- a.shape[0] == b.shape[0] == num_parameters
313
- ), f"Incoherent number of parameters. sm:{num_parameters} vs {a.shape[0]} and {b.shape[0]}"
314
- idx = (
315
- (x[:, :num_parameters] <= a[None]).all(1)
316
- * (x[:, num_parameters:] >= b[None]).all(1)
317
- ).ravel()
318
- return w[idx].sum()
319
-
320
-
1
+ # cimport multipers.tensor as mt
2
+ from libc.stdint cimport intptr_t, uint16_t, uint32_t, int32_t, int64_t
3
+ from libcpp.vector cimport vector
4
+ from libcpp cimport bool, int, float
5
+ import numpy as np
6
+ cimport numpy as cnp
7
+ import itertools
8
+ from typing import Optional,Iterable
9
+
10
+ from collections import defaultdict
11
+ cnp.import_array()
12
+ from scipy import sparse
13
+
14
+
15
+ import multipers.grids as mpg
16
+
17
+ ctypedef fused some_int:
18
+ int32_t
19
+ int64_t
20
+ int
21
+
22
+ ctypedef fused some_float:
23
+ int32_t
24
+ int64_t
25
+ int
26
+ float
27
+ double
28
+
29
+
30
+ import cython
31
+ cimport cython
32
+
33
+
34
+
35
+ # from scipy.sparse import coo_array
36
+ # from scipy.ndimage import convolve1d
37
+
38
+ def signed_betti(hilbert_function:np.ndarray, bool threshold=False):
39
+ cdef int n = hilbert_function.ndim
40
+ # zero out the "end" of the Hilbert function
41
+ if threshold:
42
+ for dimension in range(n):
43
+ slicer = tuple([slice(None) if i != dimension else -1 for i in range(n)])
44
+ hilbert_function[slicer] = 0
45
+ for i in range(n):
46
+ minus = tuple([slice(None) if j!=i else slice(0,-1) for j in range(n)])
47
+ plus = tuple([slice(None) if j!=i else slice(1,None) for j in range(n)])
48
+ hilbert_function[plus] -= hilbert_function[minus]
49
+ return hilbert_function
50
+
51
+ def rank_decomposition_by_rectangles(rank_invariant:np.ndarray, bool threshold=False):
52
+ # takes as input the rank invariant of an n-parameter persistence module
53
+ # M : [0, ..., s_1 - 1] x ... x [0, ..., s_n - 1] ---> Vec
54
+ # on a grid with dimensions of sizes s_1, ..., s_n. The input is assumed to be
55
+ # given as a tensor of dimensions (s_1, ..., s_n, s_1, ..., s_n), so that,
56
+ # at index [i_1, ..., i_n, j_1, ..., j_n] we have the rank of the structure
57
+ # map M(i) -> M(j), where i = (i_1, ..., i_n) and j = (j_1, ..., j_n), and
58
+ # i <= j, meaning that i_1 <= j_1, ..., i_n <= j_n.
59
+ # NOTE :
60
+ # - About the input, we assume that, if not( i <= j ), then at index
61
+ # [i_1, ..., i_n, j_1, ..., j_n] we have a zero.
62
+ # - Similarly, the output at index [i_1, ..., i_n, j_1, ..., j_n] only
63
+ # makes sense when i <= j. For indices where not( i <= j ) the output
64
+ # may take arbitrary values and they should be ignored.
65
+ cdef int n = rank_invariant.ndim // 2
66
+ if threshold:
67
+ # zero out the "end"
68
+ for dimension in range(n):
69
+ slicer = tuple(
70
+ [slice(None) for _ in range(n)]
71
+ + [slice(None) if i != dimension else -1 for i in range(n)]
72
+ )
73
+ rank_invariant[slicer] = 0
74
+ to_flip = tuple(range(n, 2 * n))
75
+ return np.flip(signed_betti(np.flip(rank_invariant, to_flip)), to_flip)
76
+
77
+
78
+
79
+
80
+
81
+
82
+
83
+ @cython.boundscheck(False)
84
+ @cython.wraparound(False)
85
+ def integrate_measure(
86
+ some_float[:,:] pts,
87
+ some_int[:] weights,
88
+ filtration_grid:Optional[Iterable[np.ndarray]]=None,
89
+ grid_strategy:str="regular",
90
+ resolution:int|list[int]=100,
91
+ bool return_grid=False,
92
+ **get_fitration_kwargs,
93
+ ):
94
+ """
95
+ Integrate a point measure on a grid.
96
+ Measure is a sum of diracs, based on points `pts` and weights `weights`.
97
+ For instance, if the signed measure comes from the hilbert signed measure,
98
+ this integration will return the hilbert function on this grid.
99
+ - pts : array of points (num_pts, D)
100
+ - weights : array of weights (num_pts,)
101
+ - filtration_grid (optional) : list of 1d arrays
102
+ - resolution : int or list of int
103
+ - return_grid : return the grid of the measure
104
+ - **get_fitration_kwargs : arguments to compute the grid,
105
+ if the grid is not given.
106
+ """
107
+ if filtration_grid is None:
108
+ import multipers.simplex_tree_multi
109
+ filtration_grid = mpg.compute_grid(
110
+ np.asarray(pts).T,
111
+ strategy=grid_strategy,
112
+ resolution=resolution,
113
+ **get_fitration_kwargs
114
+ )
115
+ resolution = np.asarray([len(f) for f in filtration_grid])
116
+ cdef int num_pts = pts.shape[0]
117
+ cdef int num_parameters = pts.shape[1]
118
+ assert weights.shape[0] == num_pts
119
+ out = np.zeros(shape=resolution, dtype=np.int32) ## dim cannot be known at compiletime
120
+ # cdef some_float[:] filtration_of_parameter
121
+ # cdef cnp.ndarray indices = np.zeros(shape=num_parameters, dtype=int)
122
+ #
123
+ pts_coords = np.empty((num_parameters, num_pts), dtype=np.int64)
124
+ for parameter in range(num_parameters):
125
+ pts_coords[parameter] = np.searchsorted(filtration_grid[parameter], pts[:,parameter])
126
+ for i in range(num_pts):
127
+ cone = tuple(slice(c,r) for r,c in zip(resolution,pts_coords[:,i]))
128
+ out[cone] += weights[i]
129
+ if return_grid:
130
+ return out,filtration_grid
131
+ return out
132
+
133
+ ## for benchmark purposes
134
+ def integrate_measure_python(pts, weights, filtrations):
135
+ resolution = tuple([len(f) for f in filtrations])
136
+ out = np.zeros(shape=resolution, dtype=pts.dtype)
137
+ num_pts = pts.shape[0]
138
+ num_parameters = pts.shape[1]
139
+ for i in range(num_pts): #this is slow.
140
+ indices = (filtrations[parameter]>=pts[i][parameter] for parameter in range(num_parameters))
141
+ out[np.ix_(*indices)] += weights[i]
142
+ return out
143
+
144
+
145
+ @cython.boundscheck(False)
146
+ @cython.wraparound(False)
147
+ def sparsify(x):
148
+ """
149
+ Given an arbitrary dimensional numpy array, returns (coordinates,data).
150
+ --
151
+ cost : scipy sparse + num_points*num_parameters^2 divisions
152
+ """
153
+ num_parameters = x.ndim
154
+ sx = sparse.coo_array(x.ravel())
155
+ idx = sx.col
156
+ data = sx.data
157
+ coords = np.empty((data.shape[0], num_parameters), dtype=np.int64)
158
+ for parameter in range(num_parameters-1,-1,-1):
159
+ idx,coord_of_parameter = np.divmod(idx, x.shape[parameter])
160
+ coords[:, parameter] = coord_of_parameter
161
+ return coords,data
162
+
163
+
164
+
165
+
166
+ @cython.boundscheck(False)
167
+ @cython.wraparound(False)
168
+ def clean_signed_measure(some_float[:,:] pts, some_int[:] weights, dtype = np.float32):
169
+ """
170
+ Sum the diracs at the same locations. i.e.,
171
+ returns the minimal sized measure to represent the input.
172
+ Mostly useful for, e.g., euler_characteristic from simplical complexes.
173
+ """
174
+ cdef dict[tuple, int] out = {}
175
+ cdef int num_diracs
176
+ cdef int num_parameters
177
+ num_diracs, num_parameters = pts.shape[:2]
178
+ for i in range(num_diracs):
179
+ key = tuple(pts[i]) # size cannot be known at compiletime
180
+ out[key] = out.get(key,0)+ weights[i]
181
+ num_keys = len(out)
182
+ new_pts = np.fromiter(out.keys(), dtype=np.dtype((dtype,num_parameters)), count=num_keys)
183
+ new_weights = np.fromiter(out.values(), dtype=np.int32, count=num_keys)
184
+ idx = np.nonzero(new_weights)
185
+ new_pts = new_pts[idx]
186
+ new_weights = new_weights[idx]
187
+ return (new_pts, new_weights)
188
+
189
+ def clean_sms(sms):
190
+ """
191
+ Sum the diracs at the same locations. i.e.,
192
+ returns the minimal sized measure to represent the input.
193
+ Mostly useful for, e.g., euler_characteristic from simplical complexes.
194
+ """
195
+ return tuple(clean_signed_measure(pts,weights) for pts,weights in sms)
196
+
197
+ def zero_out_sm(pts,weights, mass_default):
198
+ """
199
+ Zeros out the modules outside of \f$ \{ x\in \mathbb R^n \mid x \le \mathrm{mass_default}\}\f$.
200
+ """
201
+ from itertools import product
202
+ # merge pts, weights
203
+ PTS = np.concatenate([pts,weights[:,None]], axis=1)[None]
204
+
205
+ num_diracs, num_parameters=pts.shape
206
+
207
+ # corners of the square of dim num_parameters. shape : (num_corners,num_parameters)
208
+ C = np.fromiter(product(*([[1,0]]*num_parameters)), dtype=np.dtype((np.bool_, num_parameters)),)
209
+ Cw = 1-2*((~C).sum(axis=1)%2)
210
+ # add 1 in the end to copy the shape
211
+ C = np.concatenate([C, np.ones((C.shape[0],1))], axis=1).astype(np.bool_)
212
+
213
+ # signs of each corner
214
+ Cw = 1-2*((~C).sum(axis=1)%2)
215
+
216
+ # something useless in the end to ensure that the shape is correct (as we added 1 in pts)
217
+ mass_default = np.concatenate([mass_default,[np.nan]])
218
+ mass_default = mass_default[None,None,:] ## num c, num_pts, num_params
219
+ # each point `pt` becomes a corner of the square [`pt`, `mass_default`]
220
+ new= np.where(C[:,None,:], PTS,mass_default).reshape(-1,(num_parameters+1))
221
+ new_pts = new[:,:-1]
222
+ new_masses = new[:,-1]*np.repeat(Cw,num_diracs)
223
+ ## a bunch of stuff are in the same place, better clean it
224
+ return clean_signed_measure(new_pts.astype(np.float64), new_masses.astype(np.int64), dtype=np.float64)
225
+
226
+ def zero_out_sms(sms, mass_default):
227
+ """
228
+ Zeros out the modules outside of \f$ \{ x\in \mathbb R^n \mid x \le \mathrm{mass_default}\}\f$.
229
+ """
230
+ return tuple(zero_out_sm(pts,weights, mass_default) for pts,weights in sms)
231
+
232
+ @cython.boundscheck(False)
233
+ @cython.wraparound(False)
234
+ def barcode_from_rank_sm(
235
+ sm: tuple[np.ndarray, np.ndarray],
236
+ basepoint: np.ndarray,
237
+ direction: Optional[np.ndarray] = None,
238
+ bool full = False,
239
+ ):
240
+ """
241
+ Given a rank signed measure `sm` and a line with basepoint `basepoint` (1darray) and
242
+ direction `direction` (1darray), projects the rank signed measure on the given line,
243
+ and returns the associated estimated barcode.
244
+ If full is True, the barcode is given as coordinates in R^{`num_parameters`} instead
245
+ of coordinates w.r.t. the line.
246
+ """
247
+ basepoint = np.asarray(basepoint)
248
+ num_parameters = basepoint.shape[0]
249
+ x, w = sm
250
+ assert (
251
+ x.shape[1] // 2 == num_parameters
252
+ ), f"Incoherent number of parameters. sm:{x.shape[1]//2} vs {num_parameters}"
253
+ x, y = x[:, :num_parameters], x[:, num_parameters:]
254
+ if direction is not None:
255
+ direction = np.asarray(direction)
256
+ ok_idx = direction > 0
257
+ if ok_idx.sum() == 0:
258
+ raise ValueError(f"Got invalid direction {direction}")
259
+ zero_idx = None if np.all(ok_idx) else direction == 0
260
+ else:
261
+ direction = np.asarray([1], dtype=int)
262
+ ok_idx = slice(None)
263
+ zero_idx = None
264
+ xa = np.max(
265
+ (x[:, ok_idx] - basepoint[ok_idx]) / direction[ok_idx], axis=1, keepdims=1
266
+ )
267
+ ya = np.min(
268
+ (y[:, ok_idx] - basepoint[ok_idx]) / direction[ok_idx], axis=1, keepdims=1
269
+ )
270
+ if zero_idx is not None:
271
+ xb = np.where(x[:, zero_idx] <= basepoint[zero_idx], -np.inf, np.inf)
272
+ yb = np.where(y[:, zero_idx] <= basepoint[zero_idx], -np.inf, np.inf)
273
+ xs = np.max(np.concatenate([xa, xb], axis=1), axis=1, keepdims=1)
274
+ ys = np.min(np.concatenate([ya, yb], axis=1), axis=1, keepdims=1)
275
+ else:
276
+ xs = xa
277
+ ys = ya
278
+ out = np.concatenate([xs, ys], axis=1,dtype=np.float64)
279
+
280
+ ## TODO: check if this is faster than doing a np.repeat on x ?
281
+ cdef dict[dict,tuple] d = {}
282
+ cdef double[:,:] c_out = out # view
283
+ cdef int64_t[:] c_w = np.asarray(w,dtype=np.int64)
284
+ cdef int num_pts = out.shape[0]
285
+ # for i, stuff in enumerate(out):
286
+ # if stuff[0] < np.inf:
287
+ # d[tuple(stuff)] = d.get(tuple(stuff), 0) + w[i]
288
+ for i in range(num_pts):
289
+ if c_out[i][0] < np.inf:
290
+ machin = tuple(c_out[i])
291
+ d[machin] = d.get(machin, 0) + c_w[i]
292
+
293
+ out = np.fromiter(
294
+ itertools.chain.from_iterable(([x] * w for x, w in d.items() if x[0] < x[1])),
295
+ dtype=np.dtype((np.float64, 2)),
296
+ )
297
+ if full:
298
+ out = basepoint[None, None] + out[..., None] * direction[None, None]
299
+ return out
300
+
301
+
302
+ def estimate_rank_from_rank_sm(sm:tuple, a, b) -> int:
303
+ """
304
+ Given a rank signed measure (sm) and two points (a) and (b),
305
+ estimates the rank between these two points.
306
+ """
307
+ a = np.asarray(a)
308
+ b = np.asarray(b)
309
+ if not (a <= b).all():
310
+ return 0
311
+ x, w = sm
312
+ num_parameters = x.shape[1] // 2
313
+ assert (
314
+ a.shape[0] == b.shape[0] == num_parameters
315
+ ), f"Incoherent number of parameters. sm:{num_parameters} vs {a.shape[0]} and {b.shape[0]}"
316
+ idx = (
317
+ (x[:, :num_parameters] <= a[None]).all(1)
318
+ * (x[:, num_parameters:] >= b[None]).all(1)
319
+ ).ravel()
320
+ return w[idx].sum()
321
+
322
+