multipers 2.0.2__tar.gz → 2.0.3__tar.gz

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 (94) hide show
  1. {multipers-2.0.2/multipers.egg-info → multipers-2.0.3}/PKG-INFO +3 -1
  2. multipers-2.0.3/multipers/_signed_measure_meta.py +283 -0
  3. {multipers-2.0.2 → multipers-2.0.3}/multipers/_slicer_meta.py +2 -2
  4. {multipers-2.0.2 → multipers-2.0.3}/multipers/function_rips.cpp +886 -278
  5. {multipers-2.0.2 → multipers-2.0.3}/multipers/function_rips.pyx +2 -2
  6. {multipers-2.0.2 → multipers-2.0.3}/multipers/grids.cpp +6822 -4900
  7. {multipers-2.0.2 → multipers-2.0.3}/multipers/grids.pyx +16 -6
  8. {multipers-2.0.2 → multipers-2.0.3}/multipers/hilbert_function.pyx +2 -2
  9. {multipers-2.0.2 → multipers-2.0.3}/multipers/io.cpp +8270 -10511
  10. {multipers-2.0.2 → multipers-2.0.3}/multipers/io.pyx +2 -2
  11. {multipers-2.0.2 → multipers-2.0.3}/multipers/mma_structures.cpp +871 -263
  12. {multipers-2.0.2 → multipers-2.0.3}/multipers/multiparameter_module_approximation.cpp +871 -263
  13. {multipers-2.0.2 → multipers-2.0.3}/multipers/point_measure_integration.cpp +27382 -6786
  14. {multipers-2.0.2 → multipers-2.0.3}/multipers/point_measure_integration.pyx +38 -1
  15. {multipers-2.0.2 → multipers-2.0.3}/multipers/rank_invariant.cpp +1180 -863
  16. {multipers-2.0.2 → multipers-2.0.3}/multipers/rank_invariant.pyx +5 -10
  17. {multipers-2.0.2 → multipers-2.0.3}/multipers/simplex_tree_multi.cpp +29027 -31465
  18. {multipers-2.0.2 → multipers-2.0.3}/multipers/simplex_tree_multi.pyx +43 -54
  19. {multipers-2.0.2 → multipers-2.0.3}/multipers/slicer.cpp +62959 -37603
  20. {multipers-2.0.2 → multipers-2.0.3}/multipers/slicer.pyx +539 -6
  21. {multipers-2.0.2 → multipers-2.0.3}/multipers/tests/test_slicer.py +42 -0
  22. {multipers-2.0.2 → multipers-2.0.3/multipers.egg-info}/PKG-INFO +3 -1
  23. {multipers-2.0.2 → multipers-2.0.3}/multipers.egg-info/SOURCES.txt +0 -1
  24. {multipers-2.0.2 → multipers-2.0.3}/multipers.egg-info/requires.txt +2 -0
  25. {multipers-2.0.2 → multipers-2.0.3}/setup.py +3 -1
  26. multipers-2.0.2/multipers/_signed_measure_meta.py +0 -268
  27. multipers-2.0.2/multipers/simplex_tree_multi.pyi +0 -715
  28. {multipers-2.0.2 → multipers-2.0.3}/LICENSE +0 -0
  29. {multipers-2.0.2 → multipers-2.0.3}/MANIFEST.in +0 -0
  30. {multipers-2.0.2 → multipers-2.0.3}/README.md +0 -0
  31. {multipers-2.0.2 → multipers-2.0.3}/multipers/__init__.py +0 -0
  32. {multipers-2.0.2 → multipers-2.0.3}/multipers/data/MOL2.py +0 -0
  33. {multipers-2.0.2 → multipers-2.0.3}/multipers/data/UCR.py +0 -0
  34. {multipers-2.0.2 → multipers-2.0.3}/multipers/data/__init__.py +0 -0
  35. {multipers-2.0.2 → multipers-2.0.3}/multipers/data/graphs.py +0 -0
  36. {multipers-2.0.2 → multipers-2.0.3}/multipers/data/immuno_regions.py +0 -0
  37. {multipers-2.0.2 → multipers-2.0.3}/multipers/data/minimal_presentation_to_st_bf.py +0 -0
  38. {multipers-2.0.2 → multipers-2.0.3}/multipers/data/pytorch2simplextree.py +0 -0
  39. {multipers-2.0.2 → multipers-2.0.3}/multipers/data/shape3d.py +0 -0
  40. {multipers-2.0.2 → multipers-2.0.3}/multipers/data/synthetic.py +0 -0
  41. {multipers-2.0.2 → multipers-2.0.3}/multipers/distances.py +0 -0
  42. {multipers-2.0.2 → multipers-2.0.3}/multipers/euler_characteristic.pyx +0 -0
  43. {multipers-2.0.2 → multipers-2.0.3}/multipers/filtration_conversions.pxd +0 -0
  44. {multipers-2.0.2 → multipers-2.0.3}/multipers/filtrations.pxd +0 -0
  45. {multipers-2.0.2 → multipers-2.0.3}/multipers/gudhi/Persistence_slices_interface.h +0 -0
  46. {multipers-2.0.2 → multipers-2.0.3}/multipers/gudhi/Simplex_tree_multi_interface.h +0 -0
  47. {multipers-2.0.2 → multipers-2.0.3}/multipers/gudhi/gudhi/Simplex_tree/multi_filtrations/Box.h +0 -0
  48. {multipers-2.0.2 → multipers-2.0.3}/multipers/gudhi/gudhi/Simplex_tree/multi_filtrations/Finitely_critical_filtrations.h +0 -0
  49. {multipers-2.0.2 → multipers-2.0.3}/multipers/gudhi/gudhi/Simplex_tree/multi_filtrations/Line.h +0 -0
  50. {multipers-2.0.2 → multipers-2.0.3}/multipers/hilbert_function.pyi +0 -0
  51. {multipers-2.0.2 → multipers-2.0.3}/multipers/ml/__init__.py +0 -0
  52. {multipers-2.0.2 → multipers-2.0.3}/multipers/ml/accuracies.py +0 -0
  53. {multipers-2.0.2 → multipers-2.0.3}/multipers/ml/convolutions.py +0 -0
  54. {multipers-2.0.2 → multipers-2.0.3}/multipers/ml/invariants_with_persistable.py +0 -0
  55. {multipers-2.0.2 → multipers-2.0.3}/multipers/ml/kernels.py +0 -0
  56. {multipers-2.0.2 → multipers-2.0.3}/multipers/ml/mma.py +0 -0
  57. {multipers-2.0.2 → multipers-2.0.3}/multipers/ml/one.py +0 -0
  58. {multipers-2.0.2 → multipers-2.0.3}/multipers/ml/point_clouds.py +0 -0
  59. {multipers-2.0.2 → multipers-2.0.3}/multipers/ml/signed_betti.py +0 -0
  60. {multipers-2.0.2 → multipers-2.0.3}/multipers/ml/signed_measures.py +0 -0
  61. {multipers-2.0.2 → multipers-2.0.3}/multipers/ml/sliced_wasserstein.py +0 -0
  62. {multipers-2.0.2 → multipers-2.0.3}/multipers/ml/tools.py +0 -0
  63. {multipers-2.0.2 → multipers-2.0.3}/multipers/mma_structures.pxd +0 -0
  64. {multipers-2.0.2 → multipers-2.0.3}/multipers/mma_structures.pyx +0 -0
  65. {multipers-2.0.2 → multipers-2.0.3}/multipers/multi_parameter_rank_invariant/euler_characteristic.h +0 -0
  66. {multipers-2.0.2 → multipers-2.0.3}/multipers/multi_parameter_rank_invariant/function_rips.h +0 -0
  67. {multipers-2.0.2 → multipers-2.0.3}/multipers/multi_parameter_rank_invariant/hilbert_function.h +0 -0
  68. {multipers-2.0.2 → multipers-2.0.3}/multipers/multi_parameter_rank_invariant/rank_invariant.h +0 -0
  69. {multipers-2.0.2 → multipers-2.0.3}/multipers/multiparameter_edge_collapse.py +0 -0
  70. {multipers-2.0.2 → multipers-2.0.3}/multipers/multiparameter_module_approximation/approximation.h +0 -0
  71. {multipers-2.0.2 → multipers-2.0.3}/multipers/multiparameter_module_approximation/utilities.h +0 -0
  72. {multipers-2.0.2 → multipers-2.0.3}/multipers/multiparameter_module_approximation.pyx +0 -0
  73. {multipers-2.0.2 → multipers-2.0.3}/multipers/pickle.py +0 -0
  74. {multipers-2.0.2 → multipers-2.0.3}/multipers/plots.py +0 -0
  75. {multipers-2.0.2 → multipers-2.0.3}/multipers/simplex_tree_multi.pxd +0 -0
  76. {multipers-2.0.2 → multipers-2.0.3}/multipers/slicer.pxd +0 -0
  77. {multipers-2.0.2 → multipers-2.0.3}/multipers/tensor.pxd +0 -0
  78. {multipers-2.0.2 → multipers-2.0.3}/multipers/test.pyx +0 -0
  79. {multipers-2.0.2 → multipers-2.0.3}/multipers/tests/__init__.py +0 -0
  80. {multipers-2.0.2 → multipers-2.0.3}/multipers/tests/old_test_rank_invariant.py +0 -0
  81. {multipers-2.0.2 → multipers-2.0.3}/multipers/tests/test_diff_helper.py +0 -0
  82. {multipers-2.0.2 → multipers-2.0.3}/multipers/tests/test_hilbert_function.py +0 -0
  83. {multipers-2.0.2 → multipers-2.0.3}/multipers/tests/test_mma.py +0 -0
  84. {multipers-2.0.2 → multipers-2.0.3}/multipers/tests/test_point_clouds.py +0 -0
  85. {multipers-2.0.2 → multipers-2.0.3}/multipers/tests/test_python-cpp_conversion.py +0 -0
  86. {multipers-2.0.2 → multipers-2.0.3}/multipers/tests/test_signed_betti.py +0 -0
  87. {multipers-2.0.2 → multipers-2.0.3}/multipers/tests/test_simplextreemulti.py +0 -0
  88. {multipers-2.0.2 → multipers-2.0.3}/multipers/torch/__init__.py +0 -0
  89. {multipers-2.0.2 → multipers-2.0.3}/multipers/torch/diff_grids.py +0 -0
  90. {multipers-2.0.2 → multipers-2.0.3}/multipers/torch/rips_density.py +0 -0
  91. {multipers-2.0.2 → multipers-2.0.3}/multipers.egg-info/dependency_links.txt +0 -0
  92. {multipers-2.0.2 → multipers-2.0.3}/multipers.egg-info/top_level.txt +0 -0
  93. {multipers-2.0.2 → multipers-2.0.3}/pyproject.toml +0 -0
  94. {multipers-2.0.2 → multipers-2.0.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: multipers
3
- Version: 2.0.2
3
+ Version: 2.0.3
4
4
  Summary: Scikit-style Multiparameter persistence toolkit
5
5
  Home-page: https://github.com/DavidLapous/multipers
6
6
  Author: David Loiseaux
@@ -26,3 +26,5 @@ Requires-Dist: pykeops
26
26
  Requires-Dist: scikit-learn
27
27
  Requires-Dist: joblib
28
28
  Requires-Dist: pot
29
+ Requires-Dist: tqdm
30
+ Requires-Dist: matplotlib
@@ -0,0 +1,283 @@
1
+ from typing import Iterable, 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, zero_out_sms
8
+ from multipers.rank_invariant import rank_from_slicer
9
+ from multipers.simplex_tree_multi import (SimplexTreeMulti_type,
10
+ _available_strategies,
11
+ is_simplextree_multi)
12
+ from multipers.slicer import Slicer_type, is_slicer
13
+
14
+
15
+ def signed_measure(
16
+ filtered_complex: Union[SimplexTreeMulti_type, Slicer_type],
17
+ degree: Optional[int] = None,
18
+ degrees: Iterable[int | None] = [None],
19
+ mass_default=None,
20
+ grid_strategy: _available_strategies = "exact",
21
+ invariant: Optional[str] = None,
22
+ plot: bool = False,
23
+ verbose: bool = False,
24
+ n_jobs: int = -1,
25
+ expand_collapse: bool = False,
26
+ backend: Optional[str] = None,
27
+ thread_id: str = "",
28
+ grid_conversion: Optional[list] = None,
29
+ coordinate_measure: bool = False,
30
+ num_collapses: int = 0,
31
+ clean: Optional[bool] = None,
32
+ vineyard:bool=False,
33
+ **infer_grid_kwargs,
34
+ ) -> list[tuple[np.ndarray, np.ndarray]]:
35
+ """
36
+ Computes the signed measures given by the decomposition of the hilbert
37
+ function or the euler characteristic, or the rank invariant.
38
+
39
+ Input
40
+ -----
41
+ - filtered_complex: given by a simplextree or a slicer.
42
+ - degree:int|None / degrees:list[int] the degrees to compute.
43
+ None represents the euler characteristic.
44
+ - mass_default: Either None, or 'auto' or 'inf', or array-like of floats.
45
+ Where to put the default mass to get a zero-mass measure.
46
+ - grid_strategy: If not squeezed yet, the strategy to coarsen the grid; see ``strategy`` in :func:`multipers.grids.compute_grid`.
47
+ - invariant: The invariant to use, either "hilbert", "rank", or "euler".
48
+ - plot:bool, plots the computed measures if true.
49
+ - n_jobs:int, number of jobs.
50
+ Defaults to #cpu, but when doing parallel computations of signed measures, we recommend setting this to 1.
51
+ - verbose:bool, prints c++ logs.
52
+ - expand_collapse: when the input is a simplextree, only expands the complex when computing 1-dimensional slices. Meant to reduce memory footprint at some computational expense.
53
+ - backend:str when the input is a simplextree, reduce first the filtered complex using an external library
54
+ see ``backend`` in :func:`multipers.io.reduce_complex`.
55
+ - grid_conversion: If given, re-evaluates the final signed measure in this grid.
56
+ - coordinate_measure: bool, if True, compute the signed measure as a coordinates given in grid_conversion.
57
+ - num_collapses: int, if `filtered_complex` is a simplextree, does some collapses if possible.
58
+ - clean: reduces the output signed measure. Only useful for euler computations.
59
+
60
+ Output
61
+ ------
62
+
63
+ `[signed_measure_of_degree for degree in degrees]`
64
+ with `signed_measure_of_degree` of the form `(dirac location, dirac weights)`.
65
+ """
66
+
67
+ if len(degrees) == 1 and degrees[0] is None and degree is not None:
68
+ degrees = [degree]
69
+ if None in degrees:
70
+ assert len(degrees) == 1
71
+ invariant = "euler"
72
+ if len(degrees) == 0:
73
+ return []
74
+ if clean is None:
75
+ clean = True if None in degrees else False
76
+
77
+ assert invariant is None or invariant in [
78
+ "hilbert",
79
+ "rank_invariant",
80
+ "euler",
81
+ "rank",
82
+ "euler_characteristic",
83
+ "hilbert_function",
84
+ ]
85
+
86
+ assert (
87
+ not plot or filtered_complex.num_parameters == 2
88
+ ), "Can only plot 2d measures."
89
+
90
+ if grid_conversion is None and not filtered_complex.is_squeezed:
91
+ grid_conversion = compute_grid(
92
+ filtered_complex, strategy=grid_strategy, **infer_grid_kwargs
93
+ )
94
+ if filtered_complex.is_squeezed and grid_conversion is None:
95
+ grid_conversion = tuple(np.asarray(f) for f in filtered_complex.filtration_grid)
96
+
97
+ if mass_default is None:
98
+ mass_default = mass_default
99
+ elif mass_default == "inf":
100
+ mass_default = np.array([np.inf] * filtered_complex.num_parameters)
101
+ elif mass_default == "auto": # this will not work with torch, but we don't want to autodiff this anyway
102
+ mass_default = np.array(
103
+ [1.1 * np.max(f) - 0.1 * np.min(f) for f in grid_conversion]
104
+ )
105
+ else:
106
+ mass_default = np.asarray(mass_default)
107
+ assert (
108
+ mass_default.ndim == 1
109
+ and mass_default.shape[0] == filtered_complex.num_parameters
110
+ )
111
+
112
+ INPUT_ARGS = locals()
113
+ if not filtered_complex.is_squeezed:
114
+ filtered_complex_ = filtered_complex.grid_squeeze(
115
+ grid_conversion, coordinates=True
116
+ )
117
+ else:
118
+ filtered_complex_ = filtered_complex
119
+
120
+ num_parameters = filtered_complex.num_parameters
121
+ assert num_parameters == len(
122
+ grid_conversion
123
+ ), f"Number of parameter do not coincide. Got (grid_conversion) {len(grid_conversion)} and (filtered complex) {num_parameters}."
124
+
125
+ fix_mass_default = mass_default is not None
126
+ if is_slicer(filtered_complex_):
127
+ if backend is not None:
128
+ from multipers.slicer import minimal_presentation
129
+ assert (
130
+ invariant != "euler"
131
+ ), "Euler Characteristic cannot be speed up by a backend"
132
+ # This returns a list of reduced complexes
133
+ reduced_complex = minimal_presentation(filtered_complex_, degrees=degrees, backend=backend, vineyard = vineyard)
134
+ if invariant in ("rank", "rank_invariant"):
135
+ sms = [
136
+ rank_from_slicer(
137
+ s,
138
+ degrees=[1],
139
+ n_jobs=n_jobs,
140
+ grid_shape=tuple(len(g) for g in grid_conversion),
141
+ plot=plot,
142
+ )
143
+ for s, d in zip(reduced_complex, degrees)
144
+ ]
145
+ else:
146
+ sms = [
147
+ _signed_measure_from_slicer(s)[0]
148
+ for s in reduced_complex
149
+ ]
150
+ else: # No backend
151
+ if invariant in ("rank", "rank_invariant"): # TODO Hilbert from slicer
152
+ degrees = np.asarray(degrees, dtype=int)
153
+ return rank_from_slicer(
154
+ filtered_complex_,
155
+ degrees=degrees,
156
+ n_jobs=n_jobs,
157
+ grid_shape=tuple(len(g) for g in grid_conversion),
158
+ plot=plot,
159
+ )
160
+ elif invariant is None or "euler" in invariant:
161
+ sms = _signed_measure_from_slicer(
162
+ filtered_complex_,
163
+ )
164
+ else:
165
+ if filtered_complex_.is_minpres:
166
+ assert len(degrees) == 1
167
+ sms = _signed_measure_from_slicer(filtered_complex_)
168
+
169
+ else:
170
+ from multipers.slicer import minimal_presentation
171
+ reduced_complex = minimal_presentation(filtered_complex_, degrees=degrees, backend=backend, vineyard=vineyard)
172
+ sms = [_signed_measure_from_slicer(s)[0]
173
+ for s in reduced_complex
174
+ ]
175
+
176
+
177
+ elif is_simplextree_multi(filtered_complex_):
178
+ if num_collapses != 0:
179
+ filtered_complex_.collapse_edges(num_collapses)
180
+ if backend is not None:
181
+ from multipers.slicer import minimal_presentation
182
+ reduced_complex = minimal_presentation(filtered_complex_, degrees=degrees, backend=backend, vineyard=vineyard)
183
+ # this is a list of slicer -> need to go back
184
+ INPUT_ARGS.pop('filtered_complex')
185
+ INPUT_ARGS.pop('degrees')
186
+ INPUT_ARGS.pop('degree')
187
+ INPUT_ARGS.pop('backend')
188
+ plot = INPUT_ARGS.pop('plot') ## plot has to be dealt with later
189
+ output = [_signed_measure_from_slicer(s, degree=d, **INPUT_ARGS)[0] for s,d in zip(reduced_complex, degrees)]
190
+ if plot:
191
+ plot_signed_measures(output)
192
+ return output
193
+ ## we still have a simplextree here
194
+ if invariant in ["rank_invariant", "rank"]:
195
+ assert (
196
+ filtered_complex.num_parameters == 2
197
+ ), "Rank invariant only implemented for 2-parameter modules."
198
+ assert not coordinate_measure, "Not implemented"
199
+ from multipers.simplex_tree_multi import \
200
+ _rank_signed_measure as smri
201
+
202
+ sms = smri(
203
+ filtered_complex_,
204
+ mass_default=mass_default,
205
+ degrees=degrees,
206
+ plot=plot,
207
+ expand_collapse=expand_collapse,
208
+ )
209
+ fix_mass_default = False
210
+ elif len(degrees) == 1 and degrees[0] is None:
211
+ assert invariant is None or invariant in [
212
+ "euler",
213
+ "euler_characteristic",
214
+ ], "Provide a degree to compute hilbert function."
215
+ # assert not coordinate_measure, "Not implemented"
216
+ from multipers.simplex_tree_multi import _euler_signed_measure
217
+
218
+ sms = [
219
+ _euler_signed_measure(
220
+ filtered_complex_,
221
+ mass_default=mass_default,
222
+ verbose=verbose,
223
+ )
224
+ ]
225
+ fix_mass_default = False
226
+ else:
227
+ assert invariant is None or invariant in [
228
+ "hilbert",
229
+ "hilbert_function",
230
+ ], "Found homological degrees for euler computation."
231
+ from multipers.simplex_tree_multi import \
232
+ _hilbert_signed_measure as hilbert_signed_measure
233
+
234
+ sms = hilbert_signed_measure(
235
+ filtered_complex_,
236
+ degrees=degrees,
237
+ mass_default=mass_default,
238
+ verbose=verbose,
239
+ n_jobs=n_jobs,
240
+ expand_collapse=expand_collapse,
241
+ )
242
+ fix_mass_default = False
243
+ else:
244
+ raise ValueError("Filtered complex has to be a SimplexTree or a Slicer.")
245
+
246
+ if clean:
247
+ sms = clean_sms(sms)
248
+ if grid_conversion is not None and not coordinate_measure:
249
+ sms = sms_in_grid(sms, grid_conversion=grid_conversion, mass_default=mass_default, num_parameters=num_parameters)
250
+
251
+ if fix_mass_default:
252
+ # TODO : some methods need to use this, this could be optimized
253
+ sms = zero_out_sms(sms, mass_default=mass_default)
254
+ if plot:
255
+ plot_signed_measures(sms)
256
+ return sms
257
+
258
+
259
+ def _signed_measure_from_scc(
260
+ minimal_presentation, grid_conversion=None
261
+ ) -> list[tuple[np.ndarray, np.ndarray]]:
262
+ pts = np.concatenate([b[0] for b in minimal_presentation if len(b[0]) > 0])
263
+ weights = np.concatenate(
264
+ [
265
+ (1 - 2 * (i % 2)) * np.ones(len(b[0]))
266
+ for i, b in enumerate(minimal_presentation)
267
+ ]
268
+ )
269
+ sm = [(pts, weights)]
270
+ return sm
271
+
272
+
273
+ def _signed_measure_from_slicer(
274
+ slicer: Slicer_type,
275
+ ) -> list[tuple[np.ndarray, np.ndarray]]:
276
+ assert not slicer.is_kcritical, "Not implemented for k-critical filtrations yet."
277
+ pts = np.array(slicer.get_filtrations())
278
+ dims = slicer.get_dimensions()
279
+ weights = 1 - 2 * (
280
+ (1 + dims) % 2
281
+ ) # dim 0 is always empty : TODO : make that more clean
282
+ sm = [(pts, weights)]
283
+ return sm
@@ -141,7 +141,7 @@ def Slicer(
141
141
  slicer.filtration_grid = st.filtration_grid
142
142
  elif is_simplextree_multi(st) and backend == "graph":
143
143
  slicer = _slicer_from_simplextree(st, backend, vineyard)
144
- if st._is_squeezed:
144
+ if st.is_squeezed:
145
145
  slicer.filtration_grid = st.filtration_grid
146
146
  elif backend == "graph":
147
147
  raise ValueError(
@@ -153,7 +153,7 @@ You can try using `multipers.slicer.to_simplextree`."""
153
153
  filtration_grid = None
154
154
  if is_simplextree_multi(st):
155
155
  blocks = st._to_scc()
156
- if st._is_squeezed:
156
+ if st.is_squeezed:
157
157
  filtration_grid = st.filtration_grid
158
158
  elif isinstance(st, str):
159
159
  blocks = mio.scc_parser(st)