multipers 1.1.3__cp312-cp312-macosx_11_0_universal2.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 (63) hide show
  1. multipers/.dylibs/libtbb.12.12.dylib +0 -0
  2. multipers/.dylibs/libtbbmalloc.2.12.dylib +0 -0
  3. multipers/__init__.py +5 -0
  4. multipers/_old_rank_invariant.pyx +328 -0
  5. multipers/_signed_measure_meta.py +193 -0
  6. multipers/data/MOL2.py +350 -0
  7. multipers/data/UCR.py +18 -0
  8. multipers/data/__init__.py +1 -0
  9. multipers/data/graphs.py +466 -0
  10. multipers/data/immuno_regions.py +27 -0
  11. multipers/data/minimal_presentation_to_st_bf.py +0 -0
  12. multipers/data/pytorch2simplextree.py +91 -0
  13. multipers/data/shape3d.py +101 -0
  14. multipers/data/synthetic.py +68 -0
  15. multipers/distances.py +172 -0
  16. multipers/euler_characteristic.cpython-312-darwin.so +0 -0
  17. multipers/euler_characteristic.pyx +137 -0
  18. multipers/function_rips.cpython-312-darwin.so +0 -0
  19. multipers/function_rips.pyx +102 -0
  20. multipers/hilbert_function.cpython-312-darwin.so +0 -0
  21. multipers/hilbert_function.pyi +46 -0
  22. multipers/hilbert_function.pyx +151 -0
  23. multipers/io.cpython-312-darwin.so +0 -0
  24. multipers/io.pyx +176 -0
  25. multipers/ml/__init__.py +0 -0
  26. multipers/ml/accuracies.py +61 -0
  27. multipers/ml/convolutions.py +510 -0
  28. multipers/ml/invariants_with_persistable.py +79 -0
  29. multipers/ml/kernels.py +128 -0
  30. multipers/ml/mma.py +657 -0
  31. multipers/ml/one.py +472 -0
  32. multipers/ml/point_clouds.py +191 -0
  33. multipers/ml/signed_betti.py +50 -0
  34. multipers/ml/signed_measures.py +1479 -0
  35. multipers/ml/sliced_wasserstein.py +313 -0
  36. multipers/ml/tools.py +116 -0
  37. multipers/mma_structures.cpython-312-darwin.so +0 -0
  38. multipers/mma_structures.pxd +155 -0
  39. multipers/mma_structures.pyx +651 -0
  40. multipers/multiparameter_edge_collapse.py +29 -0
  41. multipers/multiparameter_module_approximation.cpython-312-darwin.so +0 -0
  42. multipers/multiparameter_module_approximation.pyi +439 -0
  43. multipers/multiparameter_module_approximation.pyx +311 -0
  44. multipers/pickle.py +53 -0
  45. multipers/plots.py +292 -0
  46. multipers/point_measure_integration.cpython-312-darwin.so +0 -0
  47. multipers/point_measure_integration.pyx +59 -0
  48. multipers/rank_invariant.cpython-312-darwin.so +0 -0
  49. multipers/rank_invariant.pyx +154 -0
  50. multipers/simplex_tree_multi.cpython-312-darwin.so +0 -0
  51. multipers/simplex_tree_multi.pxd +121 -0
  52. multipers/simplex_tree_multi.pyi +715 -0
  53. multipers/simplex_tree_multi.pyx +1417 -0
  54. multipers/slicer.cpython-312-darwin.so +0 -0
  55. multipers/slicer.pxd +94 -0
  56. multipers/slicer.pyx +276 -0
  57. multipers/tensor.pxd +13 -0
  58. multipers/test.pyx +44 -0
  59. multipers-1.1.3.dist-info/LICENSE +21 -0
  60. multipers-1.1.3.dist-info/METADATA +22 -0
  61. multipers-1.1.3.dist-info/RECORD +63 -0
  62. multipers-1.1.3.dist-info/WHEEL +5 -0
  63. multipers-1.1.3.dist-info/top_level.txt +1 -0
Binary file
multipers/__init__.py ADDED
@@ -0,0 +1,5 @@
1
+ from multipers.simplex_tree_multi import SimplexTreeMulti
2
+ # SimplexTreeMulti from multipers.multiparameter_module_approximation import *
3
+ # from multipers.rank_invariant import *
4
+ from multipers._signed_measure_meta import signed_measure
5
+ # import multipers.slicer
@@ -0,0 +1,328 @@
1
+ from libcpp.vector cimport vector
2
+ from libc.stdint cimport intptr_t
3
+ from libcpp cimport bool
4
+ from libcpp cimport int
5
+ from libcpp.utility cimport pair
6
+ from typing import Optional,Iterable,Callable
7
+
8
+ ctypedef vector[vector[vector[vector[int]]]] rank2
9
+ ctypedef vector[vector[int]] grid2D
10
+ ctypedef vector[grid2D] grid3D
11
+ ctypedef vector[grid3D] grid4D
12
+ ctypedef vector[grid4D] grid5D
13
+
14
+
15
+ ctypedef pair[vector[vector[int]],vector[int]] signed_measure_type
16
+ ctypedef pair[vector[vector[double]],vector[int]] signed_measure_double_type
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+ cdef extern from "multi_parameter_rank_invariant/_old_rank_invariant.h" namespace "Gudhi::multiparameter::rank_invariant":
26
+ rank2 get_2drank_invariant(const intptr_t, const vector[int]&, const int) nogil
27
+ grid3D get_2Dhilbert(const intptr_t, const vector[int]&, const vector[int], bool) except + nogil
28
+ vector[signed_measure_type] get_signed_measure(const intptr_t, const vector[int]&, int, vector[int], bool) except + nogil
29
+ grid4D get_3Dhilbert(const intptr_t, const vector[int]&, const vector[int]) except + nogil
30
+ grid5D get_4Dhilbert(const intptr_t, const vector[int]&, const vector[int]) except + nogil
31
+ grid2D get_euler2d(const intptr_t, const vector[int]&, bool, bool) except + nogil
32
+ grid3D get_euler3d(const intptr_t, const vector[int]&, bool, bool) except + nogil
33
+ grid4D get_euler4d(const intptr_t, const vector[int]&, bool, bool) except + nogil
34
+
35
+
36
+ cdef extern from "multi_parameter_rank_invariant/function_rips.h" namespace "Gudhi::multiparameter::rank_invariant::degree_rips":
37
+ signed_measure_double_type degree_rips_hilbert_signed_measure(intptr_t, int,int) except + nogil
38
+
39
+
40
+ from multipers.simplex_tree_multi import SimplexTreeMulti # Typing hack
41
+
42
+ cimport numpy as cnp
43
+ cnp.import_array()
44
+ import numpy as np
45
+
46
+ # # TODO : make a st python flag for coordinate_st, with grid resolution.
47
+ def rank_invariant2d(simplextree:SimplexTreeMulti, grid_shape:np.ndarray|list, int degree):
48
+ cdef intptr_t ptr = simplextree.thisptr
49
+ cdef int c_degree = degree
50
+ cdef vector[int] c_grid_shape = grid_shape
51
+ cdef vector[vector[vector[vector[int]]]] out
52
+ with nogil:
53
+ out = get_2drank_invariant(ptr, c_grid_shape, c_degree)
54
+ return np.asarray(out, dtype=int)
55
+
56
+
57
+
58
+ def _hilbert2d(
59
+ simplextree:SimplexTreeMulti,
60
+ grid_shape:np.ndarray|list,
61
+ vector[int] degrees,
62
+ bool mobius_inversion
63
+ ):
64
+ # assert simplextree.num_parameters == 2
65
+ cdef intptr_t ptr = simplextree.thisptr
66
+ cdef vector[int] c_grid_shape = grid_shape
67
+ cdef grid3D out
68
+ with nogil:
69
+ out = get_2Dhilbert(ptr, c_grid_shape, degrees, mobius_inversion)
70
+ return np.array(out, dtype=int)
71
+
72
+ # cdef _sm_2d(simplextree:SimplexTreeMulti, grid_shape:np.ndarray|list, int degree):
73
+ # # assert simplextree.num_parameters == 2
74
+ # cdef intptr_t ptr = simplextree.thisptr
75
+ # cdef int c_degree = degree
76
+ # cdef vector[int] c_grid_shape = grid_shape
77
+ # cdef signed_measure_type out
78
+ # with nogil:
79
+ # out = get_2D_SM(ptr, c_grid_shape, c_degree)
80
+ # return np.array(out.first, dtype=int), np.array(out.second, dtype=int)
81
+
82
+ cdef _hilbert3d(simplextree:SimplexTreeMulti, grid_shape:np.ndarray|list, vector[int] degrees):
83
+ # assert simplextree.num_parameters == 3
84
+ cdef intptr_t ptr = simplextree.thisptr
85
+ cdef vector[int] c_grid_shape = grid_shape
86
+ cdef grid4D out
87
+ with nogil:
88
+ out = get_3Dhilbert(ptr, c_grid_shape, degrees)
89
+ return np.array(out, dtype=int)
90
+
91
+ cdef _hilbert4d(simplextree:SimplexTreeMulti, grid_shape:np.ndarray|list, vector[int] degrees):
92
+ # assert simplextree.num_parameters == 3
93
+ cdef intptr_t ptr = simplextree.thisptr
94
+ cdef vector[int] c_grid_shape = grid_shape
95
+ cdef grid5D out
96
+ with nogil:
97
+ out = get_4Dhilbert(ptr, c_grid_shape, degrees)
98
+ return np.array(out, dtype=int)
99
+
100
+
101
+ def hilbert(simplextree:SimplexTreeMulti, grid_shape:np.ndarray|list, vector[int] degrees):
102
+ assert len(grid_shape) >= simplextree.num_parameters, "Grid shape not valid"
103
+ if simplextree.num_parameters == 2:
104
+ out = _hilbert2d(simplextree, grid_shape, degrees, False)
105
+ return out
106
+ if simplextree.num_parameters == 3:
107
+ return _hilbert3d(simplextree, grid_shape, degrees)
108
+ if simplextree.num_parameters == 4:
109
+ return _hilbert4d(simplextree, grid_shape, degrees)
110
+ raise Exception(f"Number of parameter has to be 2,3, or 4.")
111
+
112
+ def euler2d(simplextree:SimplexTreeMulti, grid_shape:np.ndarray|list, bool inverse=True, bool zero_pad=False):
113
+ cdef intptr_t ptr = simplextree.thisptr
114
+ cdef vector[int] c_grid_shape = grid_shape
115
+ cdef vector[vector[int]] out
116
+ with nogil:
117
+ out = get_euler2d(ptr, c_grid_shape, inverse, zero_pad)
118
+ return np.array(out, dtype=int)
119
+ def euler3d(simplextree:SimplexTreeMulti, grid_shape:np.ndarray|list, bool inverse=True, bool zero_pad=False):
120
+ cdef intptr_t ptr = simplextree.thisptr
121
+ cdef vector[int] c_grid_shape = grid_shape
122
+ cdef grid3D out
123
+ with nogil:
124
+ out = get_euler3d(ptr, c_grid_shape, inverse, zero_pad)
125
+ return np.array(out, dtype=int)
126
+ def euler4d(simplextree:SimplexTreeMulti, grid_shape:np.ndarray|list, bool inverse=True, bool zero_pad=False):
127
+ cdef intptr_t ptr = simplextree.thisptr
128
+ cdef vector[int] c_grid_shape = grid_shape
129
+ cdef grid4D out
130
+ with nogil:
131
+ out = get_euler4d(ptr, c_grid_shape,inverse, zero_pad)
132
+ return np.array(out, dtype=int)
133
+
134
+ def euler(simplextree:SimplexTreeMulti, grid_shape:np.ndarray|list, degree:int=None, bool inverse=False, bool zero_pad=False):
135
+ if simplextree.num_parameters == 2:
136
+ return euler2d(simplextree, grid_shape, inverse, zero_pad)
137
+ if simplextree.num_parameters == 3:
138
+ return euler3d(simplextree, grid_shape, inverse, zero_pad)
139
+ if simplextree.num_parameters == 4:
140
+ return euler4d(simplextree, grid_shape, inverse, zero_pad)
141
+ raise Exception(f"Number of parameter has to be 2,3, or 4.")
142
+
143
+
144
+ def signed_measure(
145
+ simplextree:SimplexTreeMulti,
146
+ grid_shape:Optional[np.ndarray]|list|int|None=None,
147
+ degree:Optional[int]|None=None,
148
+ degrees:Optional[Iterable[int]]=None,
149
+ grid_conversion=None,
150
+ infer_grid=True,
151
+ bool unsparse=False,
152
+ invariant:str | None="rank_invariant",
153
+ plot:bool=False,
154
+ grid_strategy:str='exact',
155
+ mass_default:str|None|Iterable[float]='auto',
156
+ **get_filtration_grid_kwargs,
157
+ ):
158
+ """
159
+ Computes a discrete signed measure from various invariants.
160
+
161
+ Parameters
162
+ ----------
163
+ - simplextree : SimplexTreeMulti
164
+ The multifiltered complex on which to compute the invariant.
165
+ The filtrations values are assumed to be the coordinates in that filtration, i.e. integers
166
+ - grid_conversion : (num_parameter) x (filtration values)
167
+ Reconverts the coordinate signed measure in that grid.
168
+ Default behavior is searching the grid in the simplextree.
169
+ - grid_shape : Iterable[int] or Int or None:
170
+ The coordinate grid shape.
171
+ If int, every parameter gets this resolution.
172
+ If None and grid_conversion is also None, the grid is infered from the filtration values.
173
+ - degree : int|None
174
+ If the invariant is hilbert or the rank invariant, the homological degree.
175
+ If the invariant is euler, this parameter is ignored.
176
+ - degrees :
177
+ Compute multiple signed measures of different degrees at once.
178
+ - mass_default='auto' : filtration value or 'auto' or None
179
+ where to put the mass default to ensure having a zero mass measure. If None, the mass default is not corrected.
180
+ - unsparse = False : bool
181
+ Unsparse the output.
182
+ - invariant = None : str
183
+ The invariant to use to compute the signed measure.
184
+ Possible options : 'euler' or 'hilbert' or 'rank_invariant'
185
+ - plot : bool
186
+ Will plot the signed measure if possible, i.e. if num_parameters is 2
187
+
188
+ Output
189
+ ------
190
+
191
+ Default
192
+ - dirac locations : np.ndarray of shape (num_diracs x simplextree.num_parameters)
193
+ - dirac weights : np.ndarray of shape (num_diracs)
194
+
195
+ if unsparse is true, returns the unsparsified tensor.
196
+ """
197
+ cdef bool zero_pad = not (mass_default is None)
198
+ if degree is None and degrees is None:
199
+ degrees=[None]
200
+ elif degree is not None and degrees is None:
201
+ degrees = [degree]
202
+ elif degree is not None and degrees is not None:
203
+ degrees = np.concatenate([[degree], degrees])
204
+
205
+ _unique_degrees = np.unique(degrees)
206
+ assert (_unique_degrees[-1] is None and len(_unique_degrees) == 1) or np.issubdtype(_unique_degrees.dtype,int), f"One invariant at the time ! Asked for computations of {degrees}"
207
+
208
+ if _unique_degrees[0] is None:
209
+ assert invariant not in ["hilbert", "rank_invariant"], f"Provide a degree to compute {invariant} !"
210
+ invariant=2
211
+ degree=0
212
+ else:
213
+ invariant=1 if invariant is None or invariant is "hilbert" else 3
214
+
215
+ if grid_conversion is None and grid_shape is None:
216
+ if len(simplextree.filtration_grid[0]) > 0 and infer_grid:
217
+ grid_conversion = [np.asarray(f) for f in simplextree.filtration_grid]
218
+ else:
219
+ if infer_grid:
220
+ grid_conversion = simplextree.get_filtration_grid(grid_strategy=grid_strategy,**get_filtration_grid_kwargs)
221
+ if zero_pad:
222
+ mass_default_location = (1.1*f[-1] - f[0] for f in grid_conversion) if mass_default=='auto' else mass_default
223
+ grid_conversion = [np.concatenate([f, [xi]]) for f,xi in zip(grid_conversion, mass_default_location)]
224
+ ## Copy necessary, as we want a coordinate simplextree
225
+ simplextree_copy = SimplexTreeMulti(simplextree)
226
+ simplextree_copy, simplextree = simplextree, simplextree_copy
227
+ simplextree.grid_squeeze(grid_conversion, coordinate_values=True)
228
+ else:
229
+ grid_shape = np.asarray(simplextree.filtration_bounds()[1], dtype=int)+1
230
+
231
+ if grid_conversion is not None and len(simplextree.filtration_grid[0]) == 0 and infer_grid:
232
+ simplextree.grid_squeeze(grid_conversion, coordinate_values=True)
233
+ try:
234
+ int(grid_shape)
235
+ grid_shape = [grid_shape]*simplextree.num_parameters
236
+ except:
237
+ if grid_shape is None:
238
+ grid_shape = [len(f) for f in grid_conversion]
239
+
240
+ num_degrees = len(degrees)
241
+ cdef intptr_t ptr = simplextree.thisptr
242
+ cdef vector[int] c_grid_shape = grid_shape
243
+ cdef vector[signed_measure_type] _out
244
+ cdef int cinvariant =invariant
245
+ # cdef int cdegree = degree
246
+ if invariant == 2: degrees=np.array([],dtype=int)
247
+ cdef vector[int] cdegrees = degrees
248
+ num_parameters = simplextree.num_parameters
249
+ if invariant == 3:
250
+ assert simplextree.num_parameters == 2, "Rank invariant computations are limited for 2-parameter simplextree for the moment"
251
+ assert len(degrees) ==1, 'One degree at the time, WIP.'
252
+ from multipers.ml.signed_betti import rank_decomposition_by_rectangles
253
+ from torch import Tensor
254
+ out = rank_invariant2d(simplextree, grid_shape,degrees[0])
255
+ out = rank_decomposition_by_rectangles(out, threshold= zero_pad)
256
+ out = Tensor(out.copy()).to_sparse()
257
+ def _is_trivial(rectangle:np.ndarray):
258
+ birth=rectangle[:num_parameters]
259
+ death=rectangle[num_parameters:]
260
+ return np.all(birth<=death) and not np.array_equal(birth,death)
261
+ coords = np.asarray(out.indices().T, dtype=int)
262
+ weights = np.asarray(out.values(), dtype=int)
263
+ correct_indices = np.asarray([_is_trivial(rectangle) for rectangle in coords])
264
+ if len(correct_indices) == 0:
265
+ pts, weights = np.empty((0, num_parameters)), np.empty((0))
266
+ else:
267
+ pts = np.asarray(coords[correct_indices])
268
+ weights = weights[correct_indices]
269
+ out = [ ## same output format as others
270
+ (pts,weights)
271
+ ]
272
+ else:
273
+ with nogil:
274
+ _out = get_signed_measure(ptr, c_grid_shape, cinvariant, cdegrees, zero_pad)
275
+
276
+ out = [[np.asarray(_out[i].first, dtype=int), np.asarray(_out[i].second, dtype=int)] for i in range(num_degrees)]
277
+
278
+ for i,(pts, weights) in enumerate(out):
279
+ if len(pts) == 0:
280
+ out[i][0]=np.empty(shape=(0,2*num_parameters if invariant == 3 else num_parameters), dtype=int)
281
+
282
+ if unsparse:
283
+ from torch import sparse_coo_tensor
284
+ if invariant == 3:
285
+ grid_shape = list(grid_shape) + list(grid_shape)
286
+ return [np.asarray(sparse_coo_tensor(indices=pts.T,values=weights, size=tuple(grid_shape)).to_dense(), dtype=weights.dtype) for pts,weights in out]
287
+
288
+ if grid_conversion is not None:
289
+ for degree,(pts,weights) in enumerate(out):
290
+ coords = np.empty(shape=pts.shape)
291
+ for i in range(coords.shape[1]):
292
+ coords[:,i] = grid_conversion[i % num_parameters][pts[:,i]]
293
+ out[degree]=(coords, weights)
294
+
295
+ if plot:
296
+ assert simplextree.num_parameters == 2, "Plot only available for 2-parameter persistence."
297
+ from multipers.plots import plot_signed_measures
298
+ plot_signed_measures(out)
299
+ # fig, axes = plt.subplots(nrows=1,ncols=num_degrees)
300
+ # if num_degrees == 1:
301
+ # axes = [axes]
302
+ # for ax, signed_measure in zip(axes,out):
303
+ # plt.sca(ax)
304
+ # # ax.set_aspect('equal')
305
+ # if (invariant != 3):
306
+ # color_weights = np.empty(weights.shape, dtype=int)
307
+ # color_weights[weights>0] = 1
308
+ # color_weights[weights<0] = -1
309
+ # plt.scatter(coords[:,0],coords[:,1], c=color_weights, cmap="coolwarm")
310
+ # else:
311
+ # def _plot_rectangle(rectangle:np.ndarray, weight):
312
+ # x_axis=rectangle[[0,2]]
313
+ # y_axis=rectangle[[1,3]]
314
+ # color = "blue" if weight > 0 else "red"
315
+ # plt.plot(x_axis, y_axis, c=color)
316
+ # for rectangle, weight in zip(coords, weights):
317
+ # _plot_rectangle(rectangle=rectangle, weight=weight)
318
+ return out
319
+
320
+ def degree_rips(simplextree, int num_degrees, int homological_degree):
321
+ cdef intptr_t ptr = simplextree.thisptr
322
+ cdef signed_measure_double_type out
323
+ with nogil:
324
+ out = degree_rips_hilbert_signed_measure(ptr, num_degrees, homological_degree)
325
+ pts, weights = np.asarray(out.first), np.asarray(out.second, dtype=int)
326
+ if len(pts) == 0:
327
+ pts=np.empty(shape=(0,2), dtype=float)
328
+ return pts,weights
@@ -0,0 +1,193 @@
1
+ from multipers.simplex_tree_multi import SimplexTreeMulti # Typing hack
2
+ from typing import Optional
3
+ import numpy as np
4
+ from multipers.simplex_tree_multi import _available_strategies
5
+
6
+
7
+ def signed_measure(
8
+ simplextree: SimplexTreeMulti,
9
+ degree: Optional[int] = None,
10
+ degrees=[None],
11
+ mass_default=None,
12
+ grid_strategy: _available_strategies = "exact",
13
+ invariant: Optional[str] = None,
14
+ plot: bool = False,
15
+ verbose: bool = False,
16
+ n_jobs: int = -1,
17
+ expand_collapse: bool = False,
18
+ backend: str = "multipers",
19
+ thread_id: str = "",
20
+ mpfree_path: Optional[str] = None,
21
+ grid_conversion: Optional[list] = None,
22
+ num_collapses: int = 0,
23
+ **infer_grid_kwargs,
24
+ ):
25
+ """
26
+ Computes the signed measures given by the decomposition of the hilbert function or the euler characteristic.
27
+
28
+ Input
29
+ -----
30
+ - simplextree:SimplexTreeMulti, the multifiltered simplicial complex. Its recommended to squeeze the simplextree first.
31
+ - mass_default: Either None, or 'auto' or 'inf', or array-like of floats. Where to put the default mass to get a zero-mass measure.
32
+ - degree:int|None / degrees:list[int] the degrees to compute. None represents the euler characteristic.
33
+ - plot:bool, plots the computed measures if true.
34
+ - n_jobs:int, number of jobs. Defaults to #cpu, but when doing parallel computations of signed measures, we recommend setting this to 1.
35
+ - verbose:bool, prints c++ logs.
36
+
37
+ Output
38
+ ------
39
+ `[signed_measure_of_degree for degree in degrees]`
40
+ with `signed_measure_of_degree` of the form `(dirac location, dirac weights)`.
41
+ """
42
+ assert invariant is None or invariant in [
43
+ "hilbert",
44
+ "rank_invariant",
45
+ "euler",
46
+ "rank",
47
+ "euler_characteristic",
48
+ "hilbert_function",
49
+ ]
50
+ assert not plot or simplextree.num_parameters == 2, "Can only plot 2d measures."
51
+ if len(degrees) == 1 and degrees[0] is None and degree is not None:
52
+ degrees = [degree]
53
+ if None in degrees:
54
+ assert len(degrees) == 1
55
+
56
+ if len(degrees) == 0:
57
+ return []
58
+
59
+ if not simplextree._is_squeezed:
60
+ simplextree_ = SimplexTreeMulti(simplextree)
61
+ if grid_conversion is None:
62
+ simplextree_.grid_squeeze(
63
+ grid_strategy=grid_strategy,
64
+ coordinate_values=not (backend == "mpfree"),
65
+ **infer_grid_kwargs,
66
+ ) # put a warning ?
67
+ else:
68
+ simplextree_.grid_squeeze(
69
+ grid_conversion,
70
+ coordinate_values=not (backend == "mpfree"),
71
+ **infer_grid_kwargs,
72
+ )
73
+ if num_collapses != 0:
74
+ simplextree_.collapse_edges(num_collapses)
75
+ else:
76
+ simplextree_ = simplextree
77
+ if backend == "mpfree":
78
+ if mpfree_path is not None:
79
+ import multipers.io as mio
80
+
81
+ mio.mpfree_path = mpfree_path
82
+ assert (
83
+ len(degrees) == 1
84
+ and mass_default is None
85
+ and (invariant is None or "hilbert" in invariant)
86
+ )
87
+ from multipers.io import minimal_presentation_from_mpfree
88
+
89
+ minimal_presentation = minimal_presentation_from_mpfree(
90
+ simplextree,
91
+ True,
92
+ degrees[0],
93
+ id=thread_id,
94
+ )
95
+ if grid_conversion is not None:
96
+ grid_conversion = grid_conversion
97
+ elif simplextree._is_squeezed:
98
+ grid_conversion = simplextree.filtration_grid
99
+ else:
100
+ grid_conversion = None
101
+ sms = _signed_measure_from_scc(
102
+ minimal_presentation, grid_conversion=grid_conversion
103
+ )
104
+ if plot:
105
+ from multipers.plots import plot_signed_measures
106
+
107
+ plot_signed_measures(sms)
108
+ return sms
109
+ # assert simplextree.num_parameters == 2
110
+ if mass_default is None:
111
+ mass_default = mass_default
112
+ elif mass_default == "inf":
113
+ mass_default = np.array([np.inf] * simplextree.num_parameters)
114
+ elif mass_default == "auto":
115
+ grid_conversion = [np.asarray(f) for f in simplextree_.filtration_grid]
116
+ mass_default = np.array(
117
+ [1.1 * np.max(f) - 0.1 * np.min(f) for f in grid_conversion]
118
+ )
119
+ else:
120
+ mass_default = np.asarray(mass_default)
121
+ assert (
122
+ mass_default.ndim == 1
123
+ and mass_default.shape[0] == simplextree.num_parameters
124
+ )
125
+
126
+ if invariant in ["rank_invariant", "rank"]:
127
+ assert (
128
+ simplextree.num_parameters == 2
129
+ ), "Rank invariant only implemented for 2-parameter modules."
130
+ from multipers.rank_invariant import signed_measure as smri
131
+
132
+ sms = smri(
133
+ simplextree_,
134
+ mass_default=mass_default,
135
+ degrees=degrees,
136
+ plot=plot,
137
+ expand_collapse=expand_collapse,
138
+ )
139
+ elif len(degrees) == 1 and degrees[0] is None:
140
+ assert invariant is None or invariant in [
141
+ "euler",
142
+ "euler_characteristic",
143
+ ], "Provide a degree to compute hilbert function."
144
+ from multipers.euler_characteristic import euler_signed_measure
145
+
146
+ sms = [
147
+ euler_signed_measure(
148
+ simplextree_,
149
+ mass_default=mass_default,
150
+ verbose=verbose,
151
+ plot=plot,
152
+ grid_conversion=grid_conversion,
153
+ )
154
+ ]
155
+ else:
156
+ assert invariant is None or invariant in [
157
+ "hilbert",
158
+ "hilbert_function",
159
+ ], "Found homological degrees for euler computation."
160
+ from multipers.hilbert_function import hilbert_signed_measure
161
+
162
+ sms = hilbert_signed_measure(
163
+ simplextree_,
164
+ degrees=degrees,
165
+ mass_default=mass_default,
166
+ verbose=verbose,
167
+ plot=plot,
168
+ n_jobs=n_jobs,
169
+ expand_collapse=expand_collapse,
170
+ grid_conversion=grid_conversion,
171
+ )
172
+
173
+ return sms
174
+
175
+
176
+ def _signed_measure_from_scc(minimal_presentation, grid_conversion=None):
177
+ pts = np.concatenate([b[0] for b in minimal_presentation if len(b[0]) > 0])
178
+ weights = np.concatenate(
179
+ [
180
+ (1 - 2 * (i % 2)) * np.ones(len(b[0]))
181
+ for i, b in enumerate(minimal_presentation)
182
+ if len(b[0]) > 0
183
+ ]
184
+ )
185
+ if grid_conversion is not None:
186
+ pts = np.asarray(pts, dtype=int)
187
+ coords = np.empty(shape=pts.shape, dtype=float)
188
+ for i in range(coords.shape[1]):
189
+ coords[:, i] = np.asarray(grid_conversion[i])[pts[:, i]]
190
+ sm = [(coords, weights)]
191
+ else:
192
+ sm = [(pts, weights)]
193
+ return sm