multipers 2.0.0__cp310-cp310-macosx_13_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 (78) hide show
  1. multipers/.dylibs/libc++.1.0.dylib +0 -0
  2. multipers/.dylibs/libtbb.12.12.dylib +0 -0
  3. multipers/.dylibs/libtbbmalloc.2.12.dylib +0 -0
  4. multipers/__init__.py +11 -0
  5. multipers/_signed_measure_meta.py +268 -0
  6. multipers/_slicer_meta.py +171 -0
  7. multipers/data/MOL2.py +350 -0
  8. multipers/data/UCR.py +18 -0
  9. multipers/data/__init__.py +1 -0
  10. multipers/data/graphs.py +466 -0
  11. multipers/data/immuno_regions.py +27 -0
  12. multipers/data/minimal_presentation_to_st_bf.py +0 -0
  13. multipers/data/pytorch2simplextree.py +91 -0
  14. multipers/data/shape3d.py +101 -0
  15. multipers/data/synthetic.py +68 -0
  16. multipers/distances.py +198 -0
  17. multipers/euler_characteristic.pyx +132 -0
  18. multipers/filtration_conversions.pxd +229 -0
  19. multipers/filtrations.pxd +225 -0
  20. multipers/function_rips.cpython-310-darwin.so +0 -0
  21. multipers/function_rips.pyx +105 -0
  22. multipers/grids.cpython-310-darwin.so +0 -0
  23. multipers/grids.pyx +281 -0
  24. multipers/hilbert_function.pyi +46 -0
  25. multipers/hilbert_function.pyx +153 -0
  26. multipers/io.cpython-310-darwin.so +0 -0
  27. multipers/io.pyx +571 -0
  28. multipers/ml/__init__.py +0 -0
  29. multipers/ml/accuracies.py +90 -0
  30. multipers/ml/convolutions.py +532 -0
  31. multipers/ml/invariants_with_persistable.py +79 -0
  32. multipers/ml/kernels.py +176 -0
  33. multipers/ml/mma.py +659 -0
  34. multipers/ml/one.py +472 -0
  35. multipers/ml/point_clouds.py +238 -0
  36. multipers/ml/signed_betti.py +50 -0
  37. multipers/ml/signed_measures.py +1542 -0
  38. multipers/ml/sliced_wasserstein.py +461 -0
  39. multipers/ml/tools.py +113 -0
  40. multipers/mma_structures.cpython-310-darwin.so +0 -0
  41. multipers/mma_structures.pxd +127 -0
  42. multipers/mma_structures.pyx +2433 -0
  43. multipers/multiparameter_edge_collapse.py +41 -0
  44. multipers/multiparameter_module_approximation.cpython-310-darwin.so +0 -0
  45. multipers/multiparameter_module_approximation.pyx +211 -0
  46. multipers/pickle.py +53 -0
  47. multipers/plots.py +326 -0
  48. multipers/point_measure_integration.cpython-310-darwin.so +0 -0
  49. multipers/point_measure_integration.pyx +139 -0
  50. multipers/rank_invariant.cpython-310-darwin.so +0 -0
  51. multipers/rank_invariant.pyx +229 -0
  52. multipers/simplex_tree_multi.cpython-310-darwin.so +0 -0
  53. multipers/simplex_tree_multi.pxd +129 -0
  54. multipers/simplex_tree_multi.pyi +715 -0
  55. multipers/simplex_tree_multi.pyx +4655 -0
  56. multipers/slicer.cpython-310-darwin.so +0 -0
  57. multipers/slicer.pxd +781 -0
  58. multipers/slicer.pyx +3393 -0
  59. multipers/tensor.pxd +13 -0
  60. multipers/test.pyx +44 -0
  61. multipers/tests/__init__.py +40 -0
  62. multipers/tests/old_test_rank_invariant.py +91 -0
  63. multipers/tests/test_diff_helper.py +74 -0
  64. multipers/tests/test_hilbert_function.py +82 -0
  65. multipers/tests/test_mma.py +51 -0
  66. multipers/tests/test_point_clouds.py +59 -0
  67. multipers/tests/test_python-cpp_conversion.py +82 -0
  68. multipers/tests/test_signed_betti.py +181 -0
  69. multipers/tests/test_simplextreemulti.py +98 -0
  70. multipers/tests/test_slicer.py +63 -0
  71. multipers/torch/__init__.py +1 -0
  72. multipers/torch/diff_grids.py +217 -0
  73. multipers/torch/rips_density.py +257 -0
  74. multipers-2.0.0.dist-info/LICENSE +21 -0
  75. multipers-2.0.0.dist-info/METADATA +29 -0
  76. multipers-2.0.0.dist-info/RECORD +78 -0
  77. multipers-2.0.0.dist-info/WHEEL +5 -0
  78. multipers-2.0.0.dist-info/top_level.txt +1 -0
Binary file
Binary file
multipers/__init__.py ADDED
@@ -0,0 +1,11 @@
1
+ # Doc
2
+ import multipers.io
3
+ import multipers.multiparameter_module_approximation
4
+ import multipers.simplex_tree_multi
5
+ import multipers.slicer
6
+ from multipers._signed_measure_meta import signed_measure
7
+ from multipers._slicer_meta import Slicer
8
+ from multipers.multiparameter_module_approximation import module_approximation
9
+
10
+ # Shortcuts
11
+ from multipers.simplex_tree_multi import SimplexTreeMulti
@@ -0,0 +1,268 @@
1
+ from typing import Optional, Union
2
+
3
+ import numpy as np
4
+
5
+ from multipers.grids import compute_grid, sms_in_grid
6
+ from multipers.plots import plot_signed_measures
7
+ from multipers.point_measure_integration import clean_sms
8
+ from multipers.rank_invariant import rank_from_slicer
9
+ from multipers.simplex_tree_multi import (
10
+ SimplexTreeMulti_type,
11
+ _available_strategies,
12
+ is_simplextree_multi,
13
+ )
14
+ from multipers.slicer import Slicer_type, is_slicer
15
+
16
+
17
+ def signed_measure(
18
+ simplextree: Union[SimplexTreeMulti_type, Slicer_type],
19
+ degree: Optional[int] = None,
20
+ degrees=[None],
21
+ mass_default=None,
22
+ grid_strategy: _available_strategies = "exact",
23
+ invariant: Optional[str] = None,
24
+ plot: bool = False,
25
+ verbose: bool = False,
26
+ n_jobs: int = -1,
27
+ expand_collapse: bool = False,
28
+ backend: str = "multipers",
29
+ thread_id: str = "",
30
+ input_path: Optional[str] = None,
31
+ grid_conversion: Optional[list] = None,
32
+ coordinate_measure: bool = False,
33
+ num_collapses: int = 0,
34
+ clean: bool = False,
35
+ **infer_grid_kwargs,
36
+ ) -> list[tuple[np.ndarray, np.ndarray]]:
37
+ """
38
+ Computes the signed measures given by the decomposition of the hilbert
39
+ function or the euler characteristic.
40
+
41
+ Input
42
+ -----
43
+ - simplextree:SimplexTreeMulti, the multifiltered simplicial complex.
44
+ Its recommended to squeeze the simplextree first.
45
+ - mass_default: Either None, or 'auto' or 'inf', or array-like of floats.
46
+ Where to put the default mass to get a zero-mass measure.
47
+ - degree:int|None / degrees:list[int] the degrees to compute.
48
+ None represents the euler characteristic.
49
+ - plot:bool, plots the computed measures if true.
50
+ - n_jobs:int, number of jobs.
51
+ Defaults to #cpu, but when doing parallel computations of signed measures, we recommend setting this to 1.
52
+ - verbose:bool, prints c++ logs.
53
+
54
+ Output
55
+ ------
56
+ `[signed_measure_of_degree for degree in degrees]`
57
+ with `signed_measure_of_degree` of the form `(dirac location, dirac weights)`.
58
+ """
59
+ if len(degrees) == 1 and degrees[0] is None and degree is not None:
60
+ degrees = [degree]
61
+ if None in degrees:
62
+ assert len(degrees) == 1
63
+ if len(degrees) == 0:
64
+ return []
65
+ if is_slicer(simplextree):
66
+ if grid_conversion is None and not simplextree.is_squeezed:
67
+ grid_conversion = compute_grid(
68
+ simplextree.get_filtrations_values().T,
69
+ strategy=grid_strategy,
70
+ **infer_grid_kwargs,
71
+ )
72
+ if not simplextree.is_squeezed:
73
+ simplextree_ = simplextree.grid_squeeze(grid_conversion, coordinates=True)
74
+ else:
75
+ simplextree_ = simplextree
76
+ if grid_conversion is None:
77
+ grid_conversion = tuple(
78
+ np.asarray(f, dtype=np.float64) for f in simplextree.filtration_grid
79
+ )
80
+ if invariant == "rank": # TODO Hilbert from slicer
81
+ degrees = np.asarray(degrees, dtype=int)
82
+ return rank_from_slicer(
83
+ simplextree_,
84
+ degrees=degrees,
85
+ n_jobs=n_jobs,
86
+ grid_shape=tuple(len(g) for g in grid_conversion),
87
+ grid_conversion=grid_conversion,
88
+ plot=plot,
89
+ )
90
+ return _signed_measure_from_slicer(
91
+ simplextree_,
92
+ plot=plot,
93
+ grid_conversion=grid_conversion,
94
+ clean=clean,
95
+ )
96
+ assert is_simplextree_multi(simplextree), "Input has to be simplextree or slicer."
97
+ assert invariant is None or invariant in [
98
+ "hilbert",
99
+ "rank_invariant",
100
+ "euler",
101
+ "rank",
102
+ "euler_characteristic",
103
+ "hilbert_function",
104
+ ]
105
+ assert not plot or simplextree.num_parameters == 2, "Can only plot 2d measures."
106
+
107
+ if not simplextree._is_squeezed:
108
+ if grid_conversion is None:
109
+ grid_conversion = simplextree.get_filtration_grid(
110
+ grid_strategy=grid_strategy,
111
+ **infer_grid_kwargs,
112
+ ) # put a warning ?
113
+ simplextree_ = simplextree.grid_squeeze(
114
+ grid_conversion,
115
+ coordinate_values=True,
116
+ inplace=False,
117
+ **infer_grid_kwargs,
118
+ )
119
+ if num_collapses != 0:
120
+ simplextree_.collapse_edges(num_collapses)
121
+ else:
122
+ simplextree_ = simplextree
123
+ if grid_conversion is None:
124
+ grid_conversion = [np.asarray(f) for f in simplextree_.filtration_grid]
125
+ if coordinate_measure:
126
+ grid_conversion = None
127
+
128
+ if backend != "multipers":
129
+ if input_path is not None:
130
+ import multipers.io as mio
131
+
132
+ mio.input_path = input_path
133
+ assert (
134
+ len(degrees) == 1
135
+ and mass_default is None
136
+ and (invariant is None or "hilbert" in invariant)
137
+ )
138
+ from multipers.io import reduce_complex
139
+
140
+ minimal_presentation = reduce_complex(
141
+ simplextree_,
142
+ full_resolution=True,
143
+ dimension=degrees[0],
144
+ id=thread_id,
145
+ backend=backend,
146
+ verbose=verbose,
147
+ )
148
+ sms = _signed_measure_from_scc(
149
+ minimal_presentation, grid_conversion=grid_conversion
150
+ )
151
+ if plot:
152
+ from multipers.plots import plot_signed_measures
153
+
154
+ plot_signed_measures(sms)
155
+ return sms
156
+ # assert simplextree.num_parameters == 2
157
+ if mass_default is None:
158
+ mass_default = mass_default
159
+ elif mass_default == "inf":
160
+ mass_default = np.array([np.inf] * simplextree.num_parameters)
161
+ elif mass_default == "auto":
162
+ grid_conversion = [np.asarray(f) for f in simplextree_.filtration_grid]
163
+ mass_default = np.array(
164
+ [1.1 * np.max(f) - 0.1 * np.min(f) for f in grid_conversion]
165
+ )
166
+ else:
167
+ mass_default = np.asarray(mass_default)
168
+ assert (
169
+ mass_default.ndim == 1
170
+ and mass_default.shape[0] == simplextree.num_parameters
171
+ )
172
+ # assert not coordinate_measure or grid_conversion is None
173
+
174
+ if invariant in ["rank_invariant", "rank"]:
175
+ assert (
176
+ simplextree.num_parameters == 2
177
+ ), "Rank invariant only implemented for 2-parameter modules."
178
+ assert not coordinate_measure, "Not implemented"
179
+ from multipers.simplex_tree_multi import _rank_signed_measure as smri
180
+
181
+ sms = smri(
182
+ simplextree_,
183
+ mass_default=mass_default,
184
+ degrees=degrees,
185
+ plot=plot,
186
+ expand_collapse=expand_collapse,
187
+ )
188
+ elif len(degrees) == 1 and degrees[0] is None:
189
+ assert invariant is None or invariant in [
190
+ "euler",
191
+ "euler_characteristic",
192
+ ], "Provide a degree to compute hilbert function."
193
+ # assert not coordinate_measure, "Not implemented"
194
+ from multipers.simplex_tree_multi import _euler_signed_measure
195
+
196
+ sms = [
197
+ _euler_signed_measure(
198
+ simplextree_,
199
+ mass_default=mass_default,
200
+ verbose=verbose,
201
+ plot=plot,
202
+ grid_conversion=grid_conversion,
203
+ )
204
+ ]
205
+ else:
206
+ assert invariant is None or invariant in [
207
+ "hilbert",
208
+ "hilbert_function",
209
+ ], "Found homological degrees for euler computation."
210
+ from multipers.simplex_tree_multi import (
211
+ _hilbert_signed_measure as hilbert_signed_measure,
212
+ )
213
+
214
+ sms = hilbert_signed_measure(
215
+ simplextree_,
216
+ degrees=degrees,
217
+ mass_default=mass_default,
218
+ verbose=verbose,
219
+ plot=plot,
220
+ n_jobs=n_jobs,
221
+ expand_collapse=expand_collapse,
222
+ grid_conversion=grid_conversion,
223
+ )
224
+
225
+ if clean:
226
+ sms = clean_sms(sms)
227
+ return sms
228
+
229
+
230
+ def _signed_measure_from_scc(
231
+ minimal_presentation, grid_conversion=None
232
+ ) -> list[tuple[np.ndarray, np.ndarray]]:
233
+ pts = np.concatenate([b[0] for b in minimal_presentation if len(b[0]) > 0])
234
+ weights = np.concatenate(
235
+ [
236
+ (1 - 2 * (i % 2)) * np.ones(len(b[0]))
237
+ for i, b in enumerate(minimal_presentation)
238
+ ]
239
+ )
240
+ sm = [(pts, weights)]
241
+ if grid_conversion is not None:
242
+ sm = sms_in_grid(sm, grid_conversion)
243
+ return sm
244
+
245
+
246
+ def _signed_measure_from_slicer(
247
+ slicer: Slicer_type,
248
+ plot: bool = False,
249
+ grid_conversion=None,
250
+ clean: bool = False,
251
+ ) -> list[tuple[np.ndarray, np.ndarray]]:
252
+ pts = slicer.get_filtrations()
253
+ dims = slicer.get_dimensions()
254
+ weights = 1 - 2 * (
255
+ (1 + dims) % 2
256
+ ) # dim 0 is always empty : TODO : make that more clean
257
+ sm = [(pts, weights)]
258
+ if slicer.is_squeezed and grid_conversion is None:
259
+ grid_conversion = [
260
+ np.asarray(f, dtype=np.float64) for f in slicer.filtration_grid
261
+ ]
262
+ if grid_conversion is not None:
263
+ sm = sms_in_grid(sm, grid_conversion)
264
+ if clean:
265
+ sm = clean_sms(sm)
266
+ if plot:
267
+ plot_signed_measures(sm)
268
+ return sm
@@ -0,0 +1,171 @@
1
+ from copy import deepcopy
2
+ from typing import Literal, Optional
3
+
4
+ import numpy as np
5
+
6
+ import multipers.io as mio
7
+ import multipers.slicer as mps
8
+ from multipers.simplex_tree_multi import is_simplextree_multi
9
+
10
+
11
+ ## TODO : maybe optimize this with cython
12
+ def _blocks2boundary_dimension_grades(
13
+ blocks,
14
+ filtration_type=np.float64,
15
+ num_parameters: int = -1,
16
+ inplace: bool = False,
17
+ is_kcritical: bool = False,
18
+ ):
19
+ """
20
+ Turns blocks, aka scc, into the input of non-simplicial slicers.
21
+ """
22
+ if num_parameters < 0:
23
+ for b in blocks:
24
+ if len(b[0]) > 0:
25
+ if is_kcritical:
26
+ num_parameters = np.asarray(b[0][0]).shape[1]
27
+ else:
28
+ num_parameters = np.asarray(b[0]).shape[1]
29
+ break
30
+ if num_parameters < 0:
31
+ raise ValueError("Empty Filtration")
32
+ rblocks = blocks if inplace else deepcopy(blocks)
33
+ rblocks.reverse()
34
+ block_sizes = [len(b[0]) for b in rblocks]
35
+ S = np.cumsum([0, 0] + block_sizes)
36
+ if is_kcritical:
37
+ multifiltration = tuple(
38
+ stuff
39
+ for b in rblocks
40
+ for stuff in (b[0] if len(b[0]) > 0 else [np.empty((0, num_parameters))])
41
+ )
42
+
43
+ else:
44
+ multifiltration = np.concatenate(
45
+ tuple(
46
+ b[0] if len(b[0]) > 0 else np.empty((0, num_parameters))
47
+ for b in rblocks
48
+ ),
49
+ dtype=filtration_type,
50
+ )
51
+ boundary = tuple(x + S[i] for i, b in enumerate(rblocks) for x in b[1])
52
+ dimensions = np.fromiter(
53
+ (i for i, b in enumerate(rblocks) for _ in range(len(b[0]))), dtype=int
54
+ )
55
+ return boundary, dimensions, multifiltration
56
+
57
+
58
+ def _slicer_from_simplextree(st, backend, vineyard):
59
+ if vineyard:
60
+ if backend == "matrix":
61
+ slicer = mps._SlicerVineSimplicial(st)
62
+ elif backend == "clement":
63
+ raise ValueError("This one takes a minpres")
64
+ elif backend == "graph":
65
+ slicer = mps._SlicerVineGraph(st)
66
+ else:
67
+ raise ValueError(f"Inimplemented backend {backend}.")
68
+ else:
69
+ if backend == "matrix":
70
+ slicer = mps._SlicerNoVineSimplicial(st)
71
+ if backend == "clement":
72
+ raise ValueError("Clement is Vineyard")
73
+ if backend == "graph":
74
+ raise ValueError("Graph is Vineyard")
75
+ return slicer
76
+
77
+
78
+ def _slicer_from_blocks(
79
+ blocks,
80
+ backend,
81
+ vineyard: bool,
82
+ is_kcritical: bool,
83
+ dtype: type,
84
+ col: str,
85
+ ):
86
+ boundary, dimensions, multifiltrations = _blocks2boundary_dimension_grades(
87
+ blocks,
88
+ inplace=False,
89
+ is_kcritical=is_kcritical,
90
+ )
91
+ if backend == "matrix":
92
+ slicer = mps.get_matrix_slicer(vineyard, is_kcritical, dtype, col)(
93
+ boundary, dimensions, multifiltrations
94
+ )
95
+ elif backend == "clement":
96
+ assert dtype == np.float32 and not is_kcritical and vineyard
97
+ slicer = mps._SlicerClement(boundary, dimensions, multifiltrations)
98
+ else:
99
+ raise ValueError(f"Inimplemented backend {backend}.")
100
+ return slicer
101
+
102
+
103
+ def Slicer(
104
+ st,
105
+ backend: Literal["matrix", "clement", "graph"] = "matrix",
106
+ vineyard: bool = True,
107
+ reduce: bool = False,
108
+ reduce_backend: Optional[str] = None,
109
+ dtype=np.float64,
110
+ is_kcritical: bool = False,
111
+ column_type: str = "INTRUSIVE_SET",
112
+ ) -> mps.Slicer_type:
113
+ """
114
+ Given a simplextree or blocks (a.k.a scc for python),
115
+ returns a structure that can compute persistence on line (or more)
116
+ slices, eventually vineyard update, etc.
117
+
118
+ This can be used to compute interval-decomposable module approximations
119
+ or signed measures, using, e.g.
120
+ - `multipers.module_approximation(this, *args)`
121
+ - `multipers.signed_measure(this, *args)`
122
+
123
+ Note : it is recommended and sometime required to apply
124
+ a minimal presentation before computing these functions !
125
+ `mp.slicer.minimal_presentation(slicer, *args, **kwargs)`
126
+
127
+ Input
128
+ -----
129
+ - st : SimplexTreeMulti or scc-like blocks or path to scc file
130
+ - backend: slicer backend, e.g, "matrix", "clement", "graph"
131
+ - vineyard: vineyard capable (may slow down computations if true)
132
+ Output
133
+ ------
134
+ The corresponding slicer.
135
+ """
136
+ if mps.is_slicer(st):
137
+ slicer = mps.get_matrix_slicer(vineyard, is_kcritical, dtype, column_type)(
138
+ st.get_boundaries(), st.get_dimensions(), st.get_filtrations()
139
+ )
140
+ if st.is_squeezed:
141
+ slicer.filtration_grid = st.filtration_grid
142
+ elif is_simplextree_multi(st) and backend == "graph":
143
+ slicer = _slicer_from_simplextree(st, backend, vineyard)
144
+ if st._is_squeezed:
145
+ slicer.filtration_grid = st.filtration_grid
146
+ elif backend == "graph":
147
+ raise ValueError(
148
+ """
149
+ Graph is simplicial, incompatible with minpres.
150
+ You can try using `multipers.slicer.to_simplextree`."""
151
+ )
152
+ else:
153
+ filtration_grid = None
154
+ if is_simplextree_multi(st):
155
+ blocks = st._to_scc()
156
+ if st._is_squeezed:
157
+ filtration_grid = st.filtration_grid
158
+ elif isinstance(st, str):
159
+ blocks = mio.scc_parser(st)
160
+ else:
161
+ blocks = st
162
+ slicer = _slicer_from_blocks(
163
+ blocks, backend, vineyard, is_kcritical, dtype, column_type
164
+ )
165
+ if filtration_grid is not None:
166
+ slicer.filtration_grid = filtration_grid
167
+ if reduce:
168
+ slicer = mps.minimal_presentation(
169
+ slicer, backend=reduce_backend, slicer_backend=backend, vineyard=vineyard
170
+ )
171
+ return slicer