multipers 2.3.1__cp311-cp311-win_amd64.whl → 2.3.2b1__cp311-cp311-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 (49) hide show
  1. multipers/_signed_measure_meta.py +71 -65
  2. multipers/array_api/__init__.py +39 -0
  3. multipers/array_api/numpy.py +34 -0
  4. multipers/array_api/torch.py +35 -0
  5. multipers/distances.py +6 -2
  6. multipers/filtrations/density.py +23 -12
  7. multipers/filtrations/filtrations.py +74 -15
  8. multipers/function_rips.cp311-win_amd64.pyd +0 -0
  9. multipers/grids.cp311-win_amd64.pyd +0 -0
  10. multipers/grids.pyx +144 -61
  11. multipers/gudhi/Simplex_tree_multi_interface.h +35 -0
  12. multipers/gudhi/gudhi/Multi_persistence/Box.h +3 -0
  13. multipers/gudhi/gudhi/One_critical_filtration.h +17 -9
  14. multipers/gudhi/mma_interface_matrix.h +5 -3
  15. multipers/gudhi/truc.h +488 -42
  16. multipers/io.cp311-win_amd64.pyd +0 -0
  17. multipers/io.pyx +16 -86
  18. multipers/ml/mma.py +3 -3
  19. multipers/ml/signed_measures.py +60 -62
  20. multipers/mma_structures.cp311-win_amd64.pyd +0 -0
  21. multipers/mma_structures.pxd +2 -1
  22. multipers/mma_structures.pyx +56 -12
  23. multipers/mma_structures.pyx.tp +14 -3
  24. multipers/multiparameter_module_approximation/approximation.h +45 -13
  25. multipers/multiparameter_module_approximation.cp311-win_amd64.pyd +0 -0
  26. multipers/multiparameter_module_approximation.pyx +22 -6
  27. multipers/plots.py +1 -0
  28. multipers/point_measure.cp311-win_amd64.pyd +0 -0
  29. multipers/point_measure.pyx +6 -2
  30. multipers/simplex_tree_multi.cp311-win_amd64.pyd +0 -0
  31. multipers/simplex_tree_multi.pxd +1 -0
  32. multipers/simplex_tree_multi.pyx +487 -109
  33. multipers/simplex_tree_multi.pyx.tp +67 -18
  34. multipers/slicer.cp311-win_amd64.pyd +0 -0
  35. multipers/slicer.pxd +699 -217
  36. multipers/slicer.pxd.tp +22 -6
  37. multipers/slicer.pyx +5312 -1365
  38. multipers/slicer.pyx.tp +199 -46
  39. multipers/tbb12.dll +0 -0
  40. multipers/tbbbind_2_5.dll +0 -0
  41. multipers/tbbmalloc.dll +0 -0
  42. multipers/tbbmalloc_proxy.dll +0 -0
  43. multipers/tests/__init__.py +9 -4
  44. multipers/torch/diff_grids.py +30 -7
  45. {multipers-2.3.1.dist-info → multipers-2.3.2b1.dist-info}/METADATA +4 -25
  46. {multipers-2.3.1.dist-info → multipers-2.3.2b1.dist-info}/RECORD +49 -46
  47. {multipers-2.3.1.dist-info → multipers-2.3.2b1.dist-info}/WHEEL +1 -1
  48. {multipers-2.3.1.dist-info → multipers-2.3.2b1.dist-info/licenses}/LICENSE +0 -0
  49. {multipers-2.3.1.dist-info → multipers-2.3.2b1.dist-info}/top_level.txt +0 -0
@@ -51,7 +51,8 @@ cimport cython
51
51
  from gudhi.simplex_tree import SimplexTree ## Small hack for typing
52
52
  from typing import Iterable,Literal,Optional
53
53
  from tqdm import tqdm
54
- from multipers.grids import Lstrategies, compute_grid
54
+ from multipers.grids import Lstrategies, compute_grid, sanitize_grid
55
+ from multipers.array_api import api_from_tensor
55
56
  from multipers.point_measure import signed_betti, rank_decomposition_by_rectangles, sparsify
56
57
 
57
58
  from warnings import warn
@@ -81,8 +82,8 @@ cdef class SimplexTreeMulti_KFi32:
81
82
  """
82
83
  cdef public intptr_t thisptr
83
84
 
84
- cdef public vector[vector[double]] filtration_grid
85
- cdef public bool _is_function_simplextree
85
+ cdef public object filtration_grid
86
+ cdef public bool _is_function_simplextree # TODO : deprecate
86
87
  # Get the pointer casted as it should be
87
88
  cdef Simplex_tree_multi_interface[KFi32, int32_t]* get_ptr(self) noexcept nogil:
88
89
  return <Simplex_tree_multi_interface[KFi32, int32_t]*>(self.thisptr)
@@ -153,7 +154,7 @@ cdef class SimplexTreeMulti_KFi32:
153
154
  self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[KFi32, int32_t]())
154
155
  self.set_num_parameter(num_parameters)
155
156
  self._is_function_simplextree = False
156
- self.filtration_grid=[[]*num_parameters]
157
+ self.filtration_grid=[]
157
158
 
158
159
  def __dealloc__(self):
159
160
  cdef Simplex_tree_multi_interface[KFi32,int32_t]* ptr = self.get_ptr()
@@ -821,11 +822,26 @@ cdef class SimplexTreeMulti_KFi32:
821
822
  out = self.get_ptr().get_filtration_values(degrees)
822
823
  filtrations_values = [np.asarray(filtration) for filtration in out]
823
824
  # Removes infs
824
- if inf_to_nan:
825
+ if inf_to_nan and np.dtype(filtrations_values[0].dtype).kind == 'f':
825
826
  for i,f in enumerate(filtrations_values):
826
827
  filtrations_values[i][f == np.inf] = np.nan
827
828
  filtrations_values[i][f == - np.inf] = np.nan
828
829
  return filtrations_values
830
+ def _clean_filtration_grid(self):
831
+ """
832
+ Removes the values in filtration_grid that are not linked to any splx.
833
+ """
834
+ if not self.is_squeezed:
835
+ raise ValueError("No grid to clean.")
836
+ F = self.filtration_grid
837
+ self.filtration_grid=None
838
+ cleaned_coordinates = compute_grid(self)
839
+ new_st = self.grid_squeeze(cleaned_coordinates)
840
+
841
+ self.thisptr, new_st.thisptr = new_st.thisptr, self.thisptr
842
+ self.filtration_grid = tuple(f[g] for f,g in zip(F,cleaned_coordinates))
843
+ return self
844
+
829
845
 
830
846
 
831
847
  def get_filtration_grid(self, resolution:Iterable[int]|None=None, degrees:Iterable[int]|None=None, drop_quantiles:float|tuple=0, grid_strategy:_available_strategies="exact")->Iterable[np.ndarray]:
@@ -861,8 +877,16 @@ cdef class SimplexTreeMulti_KFi32:
861
877
  return compute_grid(filtrations_values, resolution=resolution,strategy=grid_strategy,drop_quantiles=drop_quantiles)
862
878
 
863
879
 
864
-
865
- def grid_squeeze(self, filtration_grid:np.ndarray|list|None=None, bool coordinate_values=True, force=False, grid_strategy:_available_strategies = "exact", inplace=False, **filtration_grid_kwargs):
880
+ def grid_squeeze(
881
+ self,
882
+ filtration_grid:np.ndarray|list|None=None,
883
+ bool coordinate_values=True,
884
+ force=False,
885
+ strategy:_available_strategies = "exact",
886
+ grid_strategy=None,
887
+ inplace=False,
888
+ **filtration_grid_kwargs
889
+ )->SimplexTreeMulti_KFi32 | SimplexTreeMulti_KFi32:
866
890
  """
867
891
  Fit the filtration of the simplextree to a grid.
868
892
 
@@ -873,26 +897,50 @@ cdef class SimplexTreeMulti_KFi32:
873
897
  """
874
898
  if not force and self.is_squeezed:
875
899
  raise Exception("SimplexTree already squeezed, use `force=True` if that's really what you want to do.")
900
+
901
+ if grid_strategy is not None:
902
+ warn("`grid_strategy` is deprecated, use `strategy` instead.",DeprecationWarning)
903
+ strategy=grid_strategy
904
+
905
+ if self.is_squeezed:
906
+ warn("(copy warning) Squeezing an already squeezed slicer.")
907
+ temp = self.unsqueeze()
908
+ subgrid = compute_grid(self.filtration_grid, strategy=strategy, resolution=resolution)
909
+ return temp.grid_squeeze(subgrid, coordinates=coordinates, inplace=inplace)
910
+
876
911
  #TODO : multi-critical
877
912
  if filtration_grid is None:
878
- filtration_grid = self.get_filtration_grid(grid_strategy=grid_strategy, **filtration_grid_kwargs)
879
- cdef vector[vector[double]] c_filtration_grid = filtration_grid
880
- assert <int>c_filtration_grid.size() == self.get_ptr().get_number_of_parameters(), f"Grid has to be of size {self.num_parameters}, got {filtration_grid.size()}"
881
- cdef intptr_t ptr = self.thisptr
913
+ filtration_grid = self.get_filtration_grid(grid_strategy=strategy, **filtration_grid_kwargs)
914
+ else:
915
+ filtration_grid = sanitize_grid(filtration_grid)
916
+ if len(filtration_grid) != self.num_parameters:
917
+ raise ValueError(f"Invalid grid to squeeze onto. Got {len(filtration_grid)=} != {self.num_parameters=}.")
918
+ api = api_from_tensor(filtration_grid[0])
919
+ cdef vector[vector[double]] c_filtration_grid = tuple(api.asnumpy(f).astype(np.float64) for f in filtration_grid) # may be faster with loop on views
882
920
  if coordinate_values and inplace:
883
- self.filtration_grid = c_filtration_grid
921
+ self.filtration_grid = filtration_grid
884
922
  if inplace or not coordinate_values:
885
923
  self.get_ptr().squeeze_filtration_inplace(c_filtration_grid, coordinate_values)
886
924
  else:
887
925
  out = SimplexTreeMulti_KFi32(num_parameters=self.num_parameters)
888
926
  self.get_ptr().squeeze_filtration(out.thisptr, c_filtration_grid)
889
- out.filtration_grid = c_filtration_grid
927
+ out.filtration_grid = filtration_grid
890
928
  return out
891
929
  return self
892
930
 
931
+ def unsqueeze(self, grid=None)->SimplexTreeMulti_KFf64:
932
+ from multipers.grids import sanitize_grid
933
+ grid = self.filtration_grid if grid is None else grid
934
+
935
+ cdef vector[vector[double]] cgrid = sanitize_grid(grid, numpyfy=True)
936
+ new_slicer = SimplexTreeMulti_KFf64()
937
+ new_slicer.get_ptr().unsqueeze_filtration(self.thisptr, cgrid)
938
+
939
+ return new_slicer
940
+
893
941
  @property
894
942
  def is_squeezed(self)->bool:
895
- return self.num_vertices > 0 and len(self.filtration_grid)>0 and len(self.filtration_grid[0]) > 0
943
+ return self.num_vertices > 0 and self.filtration_grid is not None and len(self.filtration_grid)>0 and len(self.filtration_grid[0]) > 0
896
944
 
897
945
  @property
898
946
  def dtype(self)->type:
@@ -1185,8 +1233,8 @@ cdef class SimplexTreeMulti_Fi32:
1185
1233
  """
1186
1234
  cdef public intptr_t thisptr
1187
1235
 
1188
- cdef public vector[vector[double]] filtration_grid
1189
- cdef public bool _is_function_simplextree
1236
+ cdef public object filtration_grid
1237
+ cdef public bool _is_function_simplextree # TODO : deprecate
1190
1238
  # Get the pointer casted as it should be
1191
1239
  cdef Simplex_tree_multi_interface[Fi32, int32_t]* get_ptr(self) noexcept nogil:
1192
1240
  return <Simplex_tree_multi_interface[Fi32, int32_t]*>(self.thisptr)
@@ -1257,7 +1305,7 @@ cdef class SimplexTreeMulti_Fi32:
1257
1305
  self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[Fi32, int32_t]())
1258
1306
  self.set_num_parameter(num_parameters)
1259
1307
  self._is_function_simplextree = False
1260
- self.filtration_grid=[[]*num_parameters]
1308
+ self.filtration_grid=[]
1261
1309
 
1262
1310
  def __dealloc__(self):
1263
1311
  cdef Simplex_tree_multi_interface[Fi32,int32_t]* ptr = self.get_ptr()
@@ -2202,11 +2250,26 @@ cdef class SimplexTreeMulti_Fi32:
2202
2250
  out = self.get_ptr().get_filtration_values(degrees)
2203
2251
  filtrations_values = [np.asarray(filtration) for filtration in out]
2204
2252
  # Removes infs
2205
- if inf_to_nan:
2253
+ if inf_to_nan and np.dtype(filtrations_values[0].dtype).kind == 'f':
2206
2254
  for i,f in enumerate(filtrations_values):
2207
2255
  filtrations_values[i][f == np.inf] = np.nan
2208
2256
  filtrations_values[i][f == - np.inf] = np.nan
2209
2257
  return filtrations_values
2258
+ def _clean_filtration_grid(self):
2259
+ """
2260
+ Removes the values in filtration_grid that are not linked to any splx.
2261
+ """
2262
+ if not self.is_squeezed:
2263
+ raise ValueError("No grid to clean.")
2264
+ F = self.filtration_grid
2265
+ self.filtration_grid=None
2266
+ cleaned_coordinates = compute_grid(self)
2267
+ new_st = self.grid_squeeze(cleaned_coordinates)
2268
+
2269
+ self.thisptr, new_st.thisptr = new_st.thisptr, self.thisptr
2270
+ self.filtration_grid = tuple(f[g] for f,g in zip(F,cleaned_coordinates))
2271
+ return self
2272
+
2210
2273
 
2211
2274
 
2212
2275
  def get_filtration_grid(self, resolution:Iterable[int]|None=None, degrees:Iterable[int]|None=None, drop_quantiles:float|tuple=0, grid_strategy:_available_strategies="exact")->Iterable[np.ndarray]:
@@ -2242,8 +2305,16 @@ cdef class SimplexTreeMulti_Fi32:
2242
2305
  return compute_grid(filtrations_values, resolution=resolution,strategy=grid_strategy,drop_quantiles=drop_quantiles)
2243
2306
 
2244
2307
 
2245
-
2246
- def grid_squeeze(self, filtration_grid:np.ndarray|list|None=None, bool coordinate_values=True, force=False, grid_strategy:_available_strategies = "exact", inplace=False, **filtration_grid_kwargs):
2308
+ def grid_squeeze(
2309
+ self,
2310
+ filtration_grid:np.ndarray|list|None=None,
2311
+ bool coordinate_values=True,
2312
+ force=False,
2313
+ strategy:_available_strategies = "exact",
2314
+ grid_strategy=None,
2315
+ inplace=False,
2316
+ **filtration_grid_kwargs
2317
+ )->SimplexTreeMulti_Fi32 | SimplexTreeMulti_Fi32:
2247
2318
  """
2248
2319
  Fit the filtration of the simplextree to a grid.
2249
2320
 
@@ -2254,26 +2325,50 @@ cdef class SimplexTreeMulti_Fi32:
2254
2325
  """
2255
2326
  if not force and self.is_squeezed:
2256
2327
  raise Exception("SimplexTree already squeezed, use `force=True` if that's really what you want to do.")
2328
+
2329
+ if grid_strategy is not None:
2330
+ warn("`grid_strategy` is deprecated, use `strategy` instead.",DeprecationWarning)
2331
+ strategy=grid_strategy
2332
+
2333
+ if self.is_squeezed:
2334
+ warn("(copy warning) Squeezing an already squeezed slicer.")
2335
+ temp = self.unsqueeze()
2336
+ subgrid = compute_grid(self.filtration_grid, strategy=strategy, resolution=resolution)
2337
+ return temp.grid_squeeze(subgrid, coordinates=coordinates, inplace=inplace)
2338
+
2257
2339
  #TODO : multi-critical
2258
2340
  if filtration_grid is None:
2259
- filtration_grid = self.get_filtration_grid(grid_strategy=grid_strategy, **filtration_grid_kwargs)
2260
- cdef vector[vector[double]] c_filtration_grid = filtration_grid
2261
- assert <int>c_filtration_grid.size() == self.get_ptr().get_number_of_parameters(), f"Grid has to be of size {self.num_parameters}, got {filtration_grid.size()}"
2262
- cdef intptr_t ptr = self.thisptr
2341
+ filtration_grid = self.get_filtration_grid(grid_strategy=strategy, **filtration_grid_kwargs)
2342
+ else:
2343
+ filtration_grid = sanitize_grid(filtration_grid)
2344
+ if len(filtration_grid) != self.num_parameters:
2345
+ raise ValueError(f"Invalid grid to squeeze onto. Got {len(filtration_grid)=} != {self.num_parameters=}.")
2346
+ api = api_from_tensor(filtration_grid[0])
2347
+ cdef vector[vector[double]] c_filtration_grid = tuple(api.asnumpy(f).astype(np.float64) for f in filtration_grid) # may be faster with loop on views
2263
2348
  if coordinate_values and inplace:
2264
- self.filtration_grid = c_filtration_grid
2349
+ self.filtration_grid = filtration_grid
2265
2350
  if inplace or not coordinate_values:
2266
2351
  self.get_ptr().squeeze_filtration_inplace(c_filtration_grid, coordinate_values)
2267
2352
  else:
2268
2353
  out = SimplexTreeMulti_Fi32(num_parameters=self.num_parameters)
2269
2354
  self.get_ptr().squeeze_filtration(out.thisptr, c_filtration_grid)
2270
- out.filtration_grid = c_filtration_grid
2355
+ out.filtration_grid = filtration_grid
2271
2356
  return out
2272
2357
  return self
2273
2358
 
2359
+ def unsqueeze(self, grid=None)->SimplexTreeMulti_Ff64:
2360
+ from multipers.grids import sanitize_grid
2361
+ grid = self.filtration_grid if grid is None else grid
2362
+
2363
+ cdef vector[vector[double]] cgrid = sanitize_grid(grid, numpyfy=True)
2364
+ new_slicer = SimplexTreeMulti_Ff64()
2365
+ new_slicer.get_ptr().unsqueeze_filtration(self.thisptr, cgrid)
2366
+
2367
+ return new_slicer
2368
+
2274
2369
  @property
2275
2370
  def is_squeezed(self)->bool:
2276
- return self.num_vertices > 0 and len(self.filtration_grid)>0 and len(self.filtration_grid[0]) > 0
2371
+ return self.num_vertices > 0 and self.filtration_grid is not None and len(self.filtration_grid)>0 and len(self.filtration_grid[0]) > 0
2277
2372
 
2278
2373
  @property
2279
2374
  def dtype(self)->type:
@@ -2564,8 +2659,8 @@ cdef class SimplexTreeMulti_KFi64:
2564
2659
  """
2565
2660
  cdef public intptr_t thisptr
2566
2661
 
2567
- cdef public vector[vector[double]] filtration_grid
2568
- cdef public bool _is_function_simplextree
2662
+ cdef public object filtration_grid
2663
+ cdef public bool _is_function_simplextree # TODO : deprecate
2569
2664
  # Get the pointer casted as it should be
2570
2665
  cdef Simplex_tree_multi_interface[KFi64, int64_t]* get_ptr(self) noexcept nogil:
2571
2666
  return <Simplex_tree_multi_interface[KFi64, int64_t]*>(self.thisptr)
@@ -2636,7 +2731,7 @@ cdef class SimplexTreeMulti_KFi64:
2636
2731
  self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[KFi64, int64_t]())
2637
2732
  self.set_num_parameter(num_parameters)
2638
2733
  self._is_function_simplextree = False
2639
- self.filtration_grid=[[]*num_parameters]
2734
+ self.filtration_grid=[]
2640
2735
 
2641
2736
  def __dealloc__(self):
2642
2737
  cdef Simplex_tree_multi_interface[KFi64,int64_t]* ptr = self.get_ptr()
@@ -3304,11 +3399,26 @@ cdef class SimplexTreeMulti_KFi64:
3304
3399
  out = self.get_ptr().get_filtration_values(degrees)
3305
3400
  filtrations_values = [np.asarray(filtration) for filtration in out]
3306
3401
  # Removes infs
3307
- if inf_to_nan:
3402
+ if inf_to_nan and np.dtype(filtrations_values[0].dtype).kind == 'f':
3308
3403
  for i,f in enumerate(filtrations_values):
3309
3404
  filtrations_values[i][f == np.inf] = np.nan
3310
3405
  filtrations_values[i][f == - np.inf] = np.nan
3311
3406
  return filtrations_values
3407
+ def _clean_filtration_grid(self):
3408
+ """
3409
+ Removes the values in filtration_grid that are not linked to any splx.
3410
+ """
3411
+ if not self.is_squeezed:
3412
+ raise ValueError("No grid to clean.")
3413
+ F = self.filtration_grid
3414
+ self.filtration_grid=None
3415
+ cleaned_coordinates = compute_grid(self)
3416
+ new_st = self.grid_squeeze(cleaned_coordinates)
3417
+
3418
+ self.thisptr, new_st.thisptr = new_st.thisptr, self.thisptr
3419
+ self.filtration_grid = tuple(f[g] for f,g in zip(F,cleaned_coordinates))
3420
+ return self
3421
+
3312
3422
 
3313
3423
 
3314
3424
  def get_filtration_grid(self, resolution:Iterable[int]|None=None, degrees:Iterable[int]|None=None, drop_quantiles:float|tuple=0, grid_strategy:_available_strategies="exact")->Iterable[np.ndarray]:
@@ -3344,8 +3454,16 @@ cdef class SimplexTreeMulti_KFi64:
3344
3454
  return compute_grid(filtrations_values, resolution=resolution,strategy=grid_strategy,drop_quantiles=drop_quantiles)
3345
3455
 
3346
3456
 
3347
-
3348
- def grid_squeeze(self, filtration_grid:np.ndarray|list|None=None, bool coordinate_values=True, force=False, grid_strategy:_available_strategies = "exact", inplace=False, **filtration_grid_kwargs):
3457
+ def grid_squeeze(
3458
+ self,
3459
+ filtration_grid:np.ndarray|list|None=None,
3460
+ bool coordinate_values=True,
3461
+ force=False,
3462
+ strategy:_available_strategies = "exact",
3463
+ grid_strategy=None,
3464
+ inplace=False,
3465
+ **filtration_grid_kwargs
3466
+ )->SimplexTreeMulti_KFi32 | SimplexTreeMulti_KFi64:
3349
3467
  """
3350
3468
  Fit the filtration of the simplextree to a grid.
3351
3469
 
@@ -3356,26 +3474,50 @@ cdef class SimplexTreeMulti_KFi64:
3356
3474
  """
3357
3475
  if not force and self.is_squeezed:
3358
3476
  raise Exception("SimplexTree already squeezed, use `force=True` if that's really what you want to do.")
3477
+
3478
+ if grid_strategy is not None:
3479
+ warn("`grid_strategy` is deprecated, use `strategy` instead.",DeprecationWarning)
3480
+ strategy=grid_strategy
3481
+
3482
+ if self.is_squeezed:
3483
+ warn("(copy warning) Squeezing an already squeezed slicer.")
3484
+ temp = self.unsqueeze()
3485
+ subgrid = compute_grid(self.filtration_grid, strategy=strategy, resolution=resolution)
3486
+ return temp.grid_squeeze(subgrid, coordinates=coordinates, inplace=inplace)
3487
+
3359
3488
  #TODO : multi-critical
3360
3489
  if filtration_grid is None:
3361
- filtration_grid = self.get_filtration_grid(grid_strategy=grid_strategy, **filtration_grid_kwargs)
3362
- cdef vector[vector[double]] c_filtration_grid = filtration_grid
3363
- assert <int>c_filtration_grid.size() == self.get_ptr().get_number_of_parameters(), f"Grid has to be of size {self.num_parameters}, got {filtration_grid.size()}"
3364
- cdef intptr_t ptr = self.thisptr
3490
+ filtration_grid = self.get_filtration_grid(grid_strategy=strategy, **filtration_grid_kwargs)
3491
+ else:
3492
+ filtration_grid = sanitize_grid(filtration_grid)
3493
+ if len(filtration_grid) != self.num_parameters:
3494
+ raise ValueError(f"Invalid grid to squeeze onto. Got {len(filtration_grid)=} != {self.num_parameters=}.")
3495
+ api = api_from_tensor(filtration_grid[0])
3496
+ cdef vector[vector[double]] c_filtration_grid = tuple(api.asnumpy(f).astype(np.float64) for f in filtration_grid) # may be faster with loop on views
3365
3497
  if coordinate_values and inplace:
3366
- self.filtration_grid = c_filtration_grid
3498
+ self.filtration_grid = filtration_grid
3367
3499
  if inplace or not coordinate_values:
3368
3500
  self.get_ptr().squeeze_filtration_inplace(c_filtration_grid, coordinate_values)
3369
3501
  else:
3370
3502
  out = SimplexTreeMulti_KFi32(num_parameters=self.num_parameters)
3371
3503
  self.get_ptr().squeeze_filtration(out.thisptr, c_filtration_grid)
3372
- out.filtration_grid = c_filtration_grid
3504
+ out.filtration_grid = filtration_grid
3373
3505
  return out
3374
3506
  return self
3375
3507
 
3508
+ def unsqueeze(self, grid=None)->SimplexTreeMulti_KFf64:
3509
+ from multipers.grids import sanitize_grid
3510
+ grid = self.filtration_grid if grid is None else grid
3511
+
3512
+ cdef vector[vector[double]] cgrid = sanitize_grid(grid, numpyfy=True)
3513
+ new_slicer = SimplexTreeMulti_KFf64()
3514
+ new_slicer.get_ptr().unsqueeze_filtration(self.thisptr, cgrid)
3515
+
3516
+ return new_slicer
3517
+
3376
3518
  @property
3377
3519
  def is_squeezed(self)->bool:
3378
- return self.num_vertices > 0 and len(self.filtration_grid)>0 and len(self.filtration_grid[0]) > 0
3520
+ return self.num_vertices > 0 and self.filtration_grid is not None and len(self.filtration_grid)>0 and len(self.filtration_grid[0]) > 0
3379
3521
 
3380
3522
  @property
3381
3523
  def dtype(self)->type:
@@ -3668,8 +3810,8 @@ cdef class SimplexTreeMulti_Fi64:
3668
3810
  """
3669
3811
  cdef public intptr_t thisptr
3670
3812
 
3671
- cdef public vector[vector[double]] filtration_grid
3672
- cdef public bool _is_function_simplextree
3813
+ cdef public object filtration_grid
3814
+ cdef public bool _is_function_simplextree # TODO : deprecate
3673
3815
  # Get the pointer casted as it should be
3674
3816
  cdef Simplex_tree_multi_interface[Fi64, int64_t]* get_ptr(self) noexcept nogil:
3675
3817
  return <Simplex_tree_multi_interface[Fi64, int64_t]*>(self.thisptr)
@@ -3740,7 +3882,7 @@ cdef class SimplexTreeMulti_Fi64:
3740
3882
  self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[Fi64, int64_t]())
3741
3883
  self.set_num_parameter(num_parameters)
3742
3884
  self._is_function_simplextree = False
3743
- self.filtration_grid=[[]*num_parameters]
3885
+ self.filtration_grid=[]
3744
3886
 
3745
3887
  def __dealloc__(self):
3746
3888
  cdef Simplex_tree_multi_interface[Fi64,int64_t]* ptr = self.get_ptr()
@@ -4685,11 +4827,26 @@ cdef class SimplexTreeMulti_Fi64:
4685
4827
  out = self.get_ptr().get_filtration_values(degrees)
4686
4828
  filtrations_values = [np.asarray(filtration) for filtration in out]
4687
4829
  # Removes infs
4688
- if inf_to_nan:
4830
+ if inf_to_nan and np.dtype(filtrations_values[0].dtype).kind == 'f':
4689
4831
  for i,f in enumerate(filtrations_values):
4690
4832
  filtrations_values[i][f == np.inf] = np.nan
4691
4833
  filtrations_values[i][f == - np.inf] = np.nan
4692
4834
  return filtrations_values
4835
+ def _clean_filtration_grid(self):
4836
+ """
4837
+ Removes the values in filtration_grid that are not linked to any splx.
4838
+ """
4839
+ if not self.is_squeezed:
4840
+ raise ValueError("No grid to clean.")
4841
+ F = self.filtration_grid
4842
+ self.filtration_grid=None
4843
+ cleaned_coordinates = compute_grid(self)
4844
+ new_st = self.grid_squeeze(cleaned_coordinates)
4845
+
4846
+ self.thisptr, new_st.thisptr = new_st.thisptr, self.thisptr
4847
+ self.filtration_grid = tuple(f[g] for f,g in zip(F,cleaned_coordinates))
4848
+ return self
4849
+
4693
4850
 
4694
4851
 
4695
4852
  def get_filtration_grid(self, resolution:Iterable[int]|None=None, degrees:Iterable[int]|None=None, drop_quantiles:float|tuple=0, grid_strategy:_available_strategies="exact")->Iterable[np.ndarray]:
@@ -4725,8 +4882,16 @@ cdef class SimplexTreeMulti_Fi64:
4725
4882
  return compute_grid(filtrations_values, resolution=resolution,strategy=grid_strategy,drop_quantiles=drop_quantiles)
4726
4883
 
4727
4884
 
4728
-
4729
- def grid_squeeze(self, filtration_grid:np.ndarray|list|None=None, bool coordinate_values=True, force=False, grid_strategy:_available_strategies = "exact", inplace=False, **filtration_grid_kwargs):
4885
+ def grid_squeeze(
4886
+ self,
4887
+ filtration_grid:np.ndarray|list|None=None,
4888
+ bool coordinate_values=True,
4889
+ force=False,
4890
+ strategy:_available_strategies = "exact",
4891
+ grid_strategy=None,
4892
+ inplace=False,
4893
+ **filtration_grid_kwargs
4894
+ )->SimplexTreeMulti_Fi32 | SimplexTreeMulti_Fi64:
4730
4895
  """
4731
4896
  Fit the filtration of the simplextree to a grid.
4732
4897
 
@@ -4737,26 +4902,50 @@ cdef class SimplexTreeMulti_Fi64:
4737
4902
  """
4738
4903
  if not force and self.is_squeezed:
4739
4904
  raise Exception("SimplexTree already squeezed, use `force=True` if that's really what you want to do.")
4905
+
4906
+ if grid_strategy is not None:
4907
+ warn("`grid_strategy` is deprecated, use `strategy` instead.",DeprecationWarning)
4908
+ strategy=grid_strategy
4909
+
4910
+ if self.is_squeezed:
4911
+ warn("(copy warning) Squeezing an already squeezed slicer.")
4912
+ temp = self.unsqueeze()
4913
+ subgrid = compute_grid(self.filtration_grid, strategy=strategy, resolution=resolution)
4914
+ return temp.grid_squeeze(subgrid, coordinates=coordinates, inplace=inplace)
4915
+
4740
4916
  #TODO : multi-critical
4741
4917
  if filtration_grid is None:
4742
- filtration_grid = self.get_filtration_grid(grid_strategy=grid_strategy, **filtration_grid_kwargs)
4743
- cdef vector[vector[double]] c_filtration_grid = filtration_grid
4744
- assert <int>c_filtration_grid.size() == self.get_ptr().get_number_of_parameters(), f"Grid has to be of size {self.num_parameters}, got {filtration_grid.size()}"
4745
- cdef intptr_t ptr = self.thisptr
4918
+ filtration_grid = self.get_filtration_grid(grid_strategy=strategy, **filtration_grid_kwargs)
4919
+ else:
4920
+ filtration_grid = sanitize_grid(filtration_grid)
4921
+ if len(filtration_grid) != self.num_parameters:
4922
+ raise ValueError(f"Invalid grid to squeeze onto. Got {len(filtration_grid)=} != {self.num_parameters=}.")
4923
+ api = api_from_tensor(filtration_grid[0])
4924
+ cdef vector[vector[double]] c_filtration_grid = tuple(api.asnumpy(f).astype(np.float64) for f in filtration_grid) # may be faster with loop on views
4746
4925
  if coordinate_values and inplace:
4747
- self.filtration_grid = c_filtration_grid
4926
+ self.filtration_grid = filtration_grid
4748
4927
  if inplace or not coordinate_values:
4749
4928
  self.get_ptr().squeeze_filtration_inplace(c_filtration_grid, coordinate_values)
4750
4929
  else:
4751
4930
  out = SimplexTreeMulti_Fi32(num_parameters=self.num_parameters)
4752
4931
  self.get_ptr().squeeze_filtration(out.thisptr, c_filtration_grid)
4753
- out.filtration_grid = c_filtration_grid
4932
+ out.filtration_grid = filtration_grid
4754
4933
  return out
4755
4934
  return self
4756
4935
 
4936
+ def unsqueeze(self, grid=None)->SimplexTreeMulti_Ff64:
4937
+ from multipers.grids import sanitize_grid
4938
+ grid = self.filtration_grid if grid is None else grid
4939
+
4940
+ cdef vector[vector[double]] cgrid = sanitize_grid(grid, numpyfy=True)
4941
+ new_slicer = SimplexTreeMulti_Ff64()
4942
+ new_slicer.get_ptr().unsqueeze_filtration(self.thisptr, cgrid)
4943
+
4944
+ return new_slicer
4945
+
4757
4946
  @property
4758
4947
  def is_squeezed(self)->bool:
4759
- return self.num_vertices > 0 and len(self.filtration_grid)>0 and len(self.filtration_grid[0]) > 0
4948
+ return self.num_vertices > 0 and self.filtration_grid is not None and len(self.filtration_grid)>0 and len(self.filtration_grid[0]) > 0
4760
4949
 
4761
4950
  @property
4762
4951
  def dtype(self)->type:
@@ -5047,8 +5236,8 @@ cdef class SimplexTreeMulti_KFf32:
5047
5236
  """
5048
5237
  cdef public intptr_t thisptr
5049
5238
 
5050
- cdef public vector[vector[double]] filtration_grid
5051
- cdef public bool _is_function_simplextree
5239
+ cdef public object filtration_grid
5240
+ cdef public bool _is_function_simplextree # TODO : deprecate
5052
5241
  # Get the pointer casted as it should be
5053
5242
  cdef Simplex_tree_multi_interface[KFf32, float]* get_ptr(self) noexcept nogil:
5054
5243
  return <Simplex_tree_multi_interface[KFf32, float]*>(self.thisptr)
@@ -5119,7 +5308,7 @@ cdef class SimplexTreeMulti_KFf32:
5119
5308
  self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[KFf32, float]())
5120
5309
  self.set_num_parameter(num_parameters)
5121
5310
  self._is_function_simplextree = False
5122
- self.filtration_grid=[[]*num_parameters]
5311
+ self.filtration_grid=[]
5123
5312
 
5124
5313
  def __dealloc__(self):
5125
5314
  cdef Simplex_tree_multi_interface[KFf32,float]* ptr = self.get_ptr()
@@ -5787,11 +5976,26 @@ cdef class SimplexTreeMulti_KFf32:
5787
5976
  out = self.get_ptr().get_filtration_values(degrees)
5788
5977
  filtrations_values = [np.asarray(filtration) for filtration in out]
5789
5978
  # Removes infs
5790
- if inf_to_nan:
5979
+ if inf_to_nan and np.dtype(filtrations_values[0].dtype).kind == 'f':
5791
5980
  for i,f in enumerate(filtrations_values):
5792
5981
  filtrations_values[i][f == np.inf] = np.nan
5793
5982
  filtrations_values[i][f == - np.inf] = np.nan
5794
5983
  return filtrations_values
5984
+ def _clean_filtration_grid(self):
5985
+ """
5986
+ Removes the values in filtration_grid that are not linked to any splx.
5987
+ """
5988
+ if not self.is_squeezed:
5989
+ raise ValueError("No grid to clean.")
5990
+ F = self.filtration_grid
5991
+ self.filtration_grid=None
5992
+ cleaned_coordinates = compute_grid(self)
5993
+ new_st = self.grid_squeeze(cleaned_coordinates)
5994
+
5995
+ self.thisptr, new_st.thisptr = new_st.thisptr, self.thisptr
5996
+ self.filtration_grid = tuple(f[g] for f,g in zip(F,cleaned_coordinates))
5997
+ return self
5998
+
5795
5999
 
5796
6000
 
5797
6001
  def get_filtration_grid(self, resolution:Iterable[int]|None=None, degrees:Iterable[int]|None=None, drop_quantiles:float|tuple=0, grid_strategy:_available_strategies="exact")->Iterable[np.ndarray]:
@@ -5827,8 +6031,16 @@ cdef class SimplexTreeMulti_KFf32:
5827
6031
  return compute_grid(filtrations_values, resolution=resolution,strategy=grid_strategy,drop_quantiles=drop_quantiles)
5828
6032
 
5829
6033
 
5830
-
5831
- def grid_squeeze(self, filtration_grid:np.ndarray|list|None=None, bool coordinate_values=True, force=False, grid_strategy:_available_strategies = "exact", inplace=False, **filtration_grid_kwargs):
6034
+ def grid_squeeze(
6035
+ self,
6036
+ filtration_grid:np.ndarray|list|None=None,
6037
+ bool coordinate_values=True,
6038
+ force=False,
6039
+ strategy:_available_strategies = "exact",
6040
+ grid_strategy=None,
6041
+ inplace=False,
6042
+ **filtration_grid_kwargs
6043
+ )->SimplexTreeMulti_KFi32 | SimplexTreeMulti_KFf32:
5832
6044
  """
5833
6045
  Fit the filtration of the simplextree to a grid.
5834
6046
 
@@ -5839,26 +6051,50 @@ cdef class SimplexTreeMulti_KFf32:
5839
6051
  """
5840
6052
  if not force and self.is_squeezed:
5841
6053
  raise Exception("SimplexTree already squeezed, use `force=True` if that's really what you want to do.")
6054
+
6055
+ if grid_strategy is not None:
6056
+ warn("`grid_strategy` is deprecated, use `strategy` instead.",DeprecationWarning)
6057
+ strategy=grid_strategy
6058
+
6059
+ if self.is_squeezed:
6060
+ warn("(copy warning) Squeezing an already squeezed slicer.")
6061
+ temp = self.unsqueeze()
6062
+ subgrid = compute_grid(self.filtration_grid, strategy=strategy, resolution=resolution)
6063
+ return temp.grid_squeeze(subgrid, coordinates=coordinates, inplace=inplace)
6064
+
5842
6065
  #TODO : multi-critical
5843
6066
  if filtration_grid is None:
5844
- filtration_grid = self.get_filtration_grid(grid_strategy=grid_strategy, **filtration_grid_kwargs)
5845
- cdef vector[vector[double]] c_filtration_grid = filtration_grid
5846
- assert <int>c_filtration_grid.size() == self.get_ptr().get_number_of_parameters(), f"Grid has to be of size {self.num_parameters}, got {filtration_grid.size()}"
5847
- cdef intptr_t ptr = self.thisptr
6067
+ filtration_grid = self.get_filtration_grid(grid_strategy=strategy, **filtration_grid_kwargs)
6068
+ else:
6069
+ filtration_grid = sanitize_grid(filtration_grid)
6070
+ if len(filtration_grid) != self.num_parameters:
6071
+ raise ValueError(f"Invalid grid to squeeze onto. Got {len(filtration_grid)=} != {self.num_parameters=}.")
6072
+ api = api_from_tensor(filtration_grid[0])
6073
+ cdef vector[vector[double]] c_filtration_grid = tuple(api.asnumpy(f).astype(np.float64) for f in filtration_grid) # may be faster with loop on views
5848
6074
  if coordinate_values and inplace:
5849
- self.filtration_grid = c_filtration_grid
6075
+ self.filtration_grid = filtration_grid
5850
6076
  if inplace or not coordinate_values:
5851
6077
  self.get_ptr().squeeze_filtration_inplace(c_filtration_grid, coordinate_values)
5852
6078
  else:
5853
6079
  out = SimplexTreeMulti_KFi32(num_parameters=self.num_parameters)
5854
6080
  self.get_ptr().squeeze_filtration(out.thisptr, c_filtration_grid)
5855
- out.filtration_grid = c_filtration_grid
6081
+ out.filtration_grid = filtration_grid
5856
6082
  return out
5857
6083
  return self
5858
6084
 
6085
+ def unsqueeze(self, grid=None)->SimplexTreeMulti_KFf64:
6086
+ from multipers.grids import sanitize_grid
6087
+ grid = self.filtration_grid if grid is None else grid
6088
+
6089
+ cdef vector[vector[double]] cgrid = sanitize_grid(grid, numpyfy=True)
6090
+ new_slicer = SimplexTreeMulti_KFf64()
6091
+ new_slicer.get_ptr().unsqueeze_filtration(self.thisptr, cgrid)
6092
+
6093
+ return new_slicer
6094
+
5859
6095
  @property
5860
6096
  def is_squeezed(self)->bool:
5861
- return self.num_vertices > 0 and len(self.filtration_grid)>0 and len(self.filtration_grid[0]) > 0
6097
+ return self.num_vertices > 0 and self.filtration_grid is not None and len(self.filtration_grid)>0 and len(self.filtration_grid[0]) > 0
5862
6098
 
5863
6099
  @property
5864
6100
  def dtype(self)->type:
@@ -6151,8 +6387,8 @@ cdef class SimplexTreeMulti_Ff32:
6151
6387
  """
6152
6388
  cdef public intptr_t thisptr
6153
6389
 
6154
- cdef public vector[vector[double]] filtration_grid
6155
- cdef public bool _is_function_simplextree
6390
+ cdef public object filtration_grid
6391
+ cdef public bool _is_function_simplextree # TODO : deprecate
6156
6392
  # Get the pointer casted as it should be
6157
6393
  cdef Simplex_tree_multi_interface[Ff32, float]* get_ptr(self) noexcept nogil:
6158
6394
  return <Simplex_tree_multi_interface[Ff32, float]*>(self.thisptr)
@@ -6223,7 +6459,7 @@ cdef class SimplexTreeMulti_Ff32:
6223
6459
  self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[Ff32, float]())
6224
6460
  self.set_num_parameter(num_parameters)
6225
6461
  self._is_function_simplextree = False
6226
- self.filtration_grid=[[]*num_parameters]
6462
+ self.filtration_grid=[]
6227
6463
 
6228
6464
  def __dealloc__(self):
6229
6465
  cdef Simplex_tree_multi_interface[Ff32,float]* ptr = self.get_ptr()
@@ -7168,11 +7404,26 @@ cdef class SimplexTreeMulti_Ff32:
7168
7404
  out = self.get_ptr().get_filtration_values(degrees)
7169
7405
  filtrations_values = [np.asarray(filtration) for filtration in out]
7170
7406
  # Removes infs
7171
- if inf_to_nan:
7407
+ if inf_to_nan and np.dtype(filtrations_values[0].dtype).kind == 'f':
7172
7408
  for i,f in enumerate(filtrations_values):
7173
7409
  filtrations_values[i][f == np.inf] = np.nan
7174
7410
  filtrations_values[i][f == - np.inf] = np.nan
7175
7411
  return filtrations_values
7412
+ def _clean_filtration_grid(self):
7413
+ """
7414
+ Removes the values in filtration_grid that are not linked to any splx.
7415
+ """
7416
+ if not self.is_squeezed:
7417
+ raise ValueError("No grid to clean.")
7418
+ F = self.filtration_grid
7419
+ self.filtration_grid=None
7420
+ cleaned_coordinates = compute_grid(self)
7421
+ new_st = self.grid_squeeze(cleaned_coordinates)
7422
+
7423
+ self.thisptr, new_st.thisptr = new_st.thisptr, self.thisptr
7424
+ self.filtration_grid = tuple(f[g] for f,g in zip(F,cleaned_coordinates))
7425
+ return self
7426
+
7176
7427
 
7177
7428
 
7178
7429
  def get_filtration_grid(self, resolution:Iterable[int]|None=None, degrees:Iterable[int]|None=None, drop_quantiles:float|tuple=0, grid_strategy:_available_strategies="exact")->Iterable[np.ndarray]:
@@ -7208,8 +7459,16 @@ cdef class SimplexTreeMulti_Ff32:
7208
7459
  return compute_grid(filtrations_values, resolution=resolution,strategy=grid_strategy,drop_quantiles=drop_quantiles)
7209
7460
 
7210
7461
 
7211
-
7212
- def grid_squeeze(self, filtration_grid:np.ndarray|list|None=None, bool coordinate_values=True, force=False, grid_strategy:_available_strategies = "exact", inplace=False, **filtration_grid_kwargs):
7462
+ def grid_squeeze(
7463
+ self,
7464
+ filtration_grid:np.ndarray|list|None=None,
7465
+ bool coordinate_values=True,
7466
+ force=False,
7467
+ strategy:_available_strategies = "exact",
7468
+ grid_strategy=None,
7469
+ inplace=False,
7470
+ **filtration_grid_kwargs
7471
+ )->SimplexTreeMulti_Fi32 | SimplexTreeMulti_Ff32:
7213
7472
  """
7214
7473
  Fit the filtration of the simplextree to a grid.
7215
7474
 
@@ -7220,26 +7479,50 @@ cdef class SimplexTreeMulti_Ff32:
7220
7479
  """
7221
7480
  if not force and self.is_squeezed:
7222
7481
  raise Exception("SimplexTree already squeezed, use `force=True` if that's really what you want to do.")
7482
+
7483
+ if grid_strategy is not None:
7484
+ warn("`grid_strategy` is deprecated, use `strategy` instead.",DeprecationWarning)
7485
+ strategy=grid_strategy
7486
+
7487
+ if self.is_squeezed:
7488
+ warn("(copy warning) Squeezing an already squeezed slicer.")
7489
+ temp = self.unsqueeze()
7490
+ subgrid = compute_grid(self.filtration_grid, strategy=strategy, resolution=resolution)
7491
+ return temp.grid_squeeze(subgrid, coordinates=coordinates, inplace=inplace)
7492
+
7223
7493
  #TODO : multi-critical
7224
7494
  if filtration_grid is None:
7225
- filtration_grid = self.get_filtration_grid(grid_strategy=grid_strategy, **filtration_grid_kwargs)
7226
- cdef vector[vector[double]] c_filtration_grid = filtration_grid
7227
- assert <int>c_filtration_grid.size() == self.get_ptr().get_number_of_parameters(), f"Grid has to be of size {self.num_parameters}, got {filtration_grid.size()}"
7228
- cdef intptr_t ptr = self.thisptr
7495
+ filtration_grid = self.get_filtration_grid(grid_strategy=strategy, **filtration_grid_kwargs)
7496
+ else:
7497
+ filtration_grid = sanitize_grid(filtration_grid)
7498
+ if len(filtration_grid) != self.num_parameters:
7499
+ raise ValueError(f"Invalid grid to squeeze onto. Got {len(filtration_grid)=} != {self.num_parameters=}.")
7500
+ api = api_from_tensor(filtration_grid[0])
7501
+ cdef vector[vector[double]] c_filtration_grid = tuple(api.asnumpy(f).astype(np.float64) for f in filtration_grid) # may be faster with loop on views
7229
7502
  if coordinate_values and inplace:
7230
- self.filtration_grid = c_filtration_grid
7503
+ self.filtration_grid = filtration_grid
7231
7504
  if inplace or not coordinate_values:
7232
7505
  self.get_ptr().squeeze_filtration_inplace(c_filtration_grid, coordinate_values)
7233
7506
  else:
7234
7507
  out = SimplexTreeMulti_Fi32(num_parameters=self.num_parameters)
7235
7508
  self.get_ptr().squeeze_filtration(out.thisptr, c_filtration_grid)
7236
- out.filtration_grid = c_filtration_grid
7509
+ out.filtration_grid = filtration_grid
7237
7510
  return out
7238
7511
  return self
7239
7512
 
7513
+ def unsqueeze(self, grid=None)->SimplexTreeMulti_Ff64:
7514
+ from multipers.grids import sanitize_grid
7515
+ grid = self.filtration_grid if grid is None else grid
7516
+
7517
+ cdef vector[vector[double]] cgrid = sanitize_grid(grid, numpyfy=True)
7518
+ new_slicer = SimplexTreeMulti_Ff64()
7519
+ new_slicer.get_ptr().unsqueeze_filtration(self.thisptr, cgrid)
7520
+
7521
+ return new_slicer
7522
+
7240
7523
  @property
7241
7524
  def is_squeezed(self)->bool:
7242
- return self.num_vertices > 0 and len(self.filtration_grid)>0 and len(self.filtration_grid[0]) > 0
7525
+ return self.num_vertices > 0 and self.filtration_grid is not None and len(self.filtration_grid)>0 and len(self.filtration_grid[0]) > 0
7243
7526
 
7244
7527
  @property
7245
7528
  def dtype(self)->type:
@@ -7530,8 +7813,8 @@ cdef class SimplexTreeMulti_KFf64:
7530
7813
  """
7531
7814
  cdef public intptr_t thisptr
7532
7815
 
7533
- cdef public vector[vector[double]] filtration_grid
7534
- cdef public bool _is_function_simplextree
7816
+ cdef public object filtration_grid
7817
+ cdef public bool _is_function_simplextree # TODO : deprecate
7535
7818
  # Get the pointer casted as it should be
7536
7819
  cdef Simplex_tree_multi_interface[KFf64, double]* get_ptr(self) noexcept nogil:
7537
7820
  return <Simplex_tree_multi_interface[KFf64, double]*>(self.thisptr)
@@ -7602,7 +7885,7 @@ cdef class SimplexTreeMulti_KFf64:
7602
7885
  self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[KFf64, double]())
7603
7886
  self.set_num_parameter(num_parameters)
7604
7887
  self._is_function_simplextree = False
7605
- self.filtration_grid=[[]*num_parameters]
7888
+ self.filtration_grid=[]
7606
7889
 
7607
7890
  def __dealloc__(self):
7608
7891
  cdef Simplex_tree_multi_interface[KFf64,double]* ptr = self.get_ptr()
@@ -8270,11 +8553,26 @@ cdef class SimplexTreeMulti_KFf64:
8270
8553
  out = self.get_ptr().get_filtration_values(degrees)
8271
8554
  filtrations_values = [np.asarray(filtration) for filtration in out]
8272
8555
  # Removes infs
8273
- if inf_to_nan:
8556
+ if inf_to_nan and np.dtype(filtrations_values[0].dtype).kind == 'f':
8274
8557
  for i,f in enumerate(filtrations_values):
8275
8558
  filtrations_values[i][f == np.inf] = np.nan
8276
8559
  filtrations_values[i][f == - np.inf] = np.nan
8277
8560
  return filtrations_values
8561
+ def _clean_filtration_grid(self):
8562
+ """
8563
+ Removes the values in filtration_grid that are not linked to any splx.
8564
+ """
8565
+ if not self.is_squeezed:
8566
+ raise ValueError("No grid to clean.")
8567
+ F = self.filtration_grid
8568
+ self.filtration_grid=None
8569
+ cleaned_coordinates = compute_grid(self)
8570
+ new_st = self.grid_squeeze(cleaned_coordinates)
8571
+
8572
+ self.thisptr, new_st.thisptr = new_st.thisptr, self.thisptr
8573
+ self.filtration_grid = tuple(f[g] for f,g in zip(F,cleaned_coordinates))
8574
+ return self
8575
+
8278
8576
 
8279
8577
 
8280
8578
  def get_filtration_grid(self, resolution:Iterable[int]|None=None, degrees:Iterable[int]|None=None, drop_quantiles:float|tuple=0, grid_strategy:_available_strategies="exact")->Iterable[np.ndarray]:
@@ -8310,8 +8608,16 @@ cdef class SimplexTreeMulti_KFf64:
8310
8608
  return compute_grid(filtrations_values, resolution=resolution,strategy=grid_strategy,drop_quantiles=drop_quantiles)
8311
8609
 
8312
8610
 
8313
-
8314
- def grid_squeeze(self, filtration_grid:np.ndarray|list|None=None, bool coordinate_values=True, force=False, grid_strategy:_available_strategies = "exact", inplace=False, **filtration_grid_kwargs):
8611
+ def grid_squeeze(
8612
+ self,
8613
+ filtration_grid:np.ndarray|list|None=None,
8614
+ bool coordinate_values=True,
8615
+ force=False,
8616
+ strategy:_available_strategies = "exact",
8617
+ grid_strategy=None,
8618
+ inplace=False,
8619
+ **filtration_grid_kwargs
8620
+ )->SimplexTreeMulti_KFi32 | SimplexTreeMulti_KFf64:
8315
8621
  """
8316
8622
  Fit the filtration of the simplextree to a grid.
8317
8623
 
@@ -8322,26 +8628,50 @@ cdef class SimplexTreeMulti_KFf64:
8322
8628
  """
8323
8629
  if not force and self.is_squeezed:
8324
8630
  raise Exception("SimplexTree already squeezed, use `force=True` if that's really what you want to do.")
8631
+
8632
+ if grid_strategy is not None:
8633
+ warn("`grid_strategy` is deprecated, use `strategy` instead.",DeprecationWarning)
8634
+ strategy=grid_strategy
8635
+
8636
+ if self.is_squeezed:
8637
+ warn("(copy warning) Squeezing an already squeezed slicer.")
8638
+ temp = self.unsqueeze()
8639
+ subgrid = compute_grid(self.filtration_grid, strategy=strategy, resolution=resolution)
8640
+ return temp.grid_squeeze(subgrid, coordinates=coordinates, inplace=inplace)
8641
+
8325
8642
  #TODO : multi-critical
8326
8643
  if filtration_grid is None:
8327
- filtration_grid = self.get_filtration_grid(grid_strategy=grid_strategy, **filtration_grid_kwargs)
8328
- cdef vector[vector[double]] c_filtration_grid = filtration_grid
8329
- assert <int>c_filtration_grid.size() == self.get_ptr().get_number_of_parameters(), f"Grid has to be of size {self.num_parameters}, got {filtration_grid.size()}"
8330
- cdef intptr_t ptr = self.thisptr
8644
+ filtration_grid = self.get_filtration_grid(grid_strategy=strategy, **filtration_grid_kwargs)
8645
+ else:
8646
+ filtration_grid = sanitize_grid(filtration_grid)
8647
+ if len(filtration_grid) != self.num_parameters:
8648
+ raise ValueError(f"Invalid grid to squeeze onto. Got {len(filtration_grid)=} != {self.num_parameters=}.")
8649
+ api = api_from_tensor(filtration_grid[0])
8650
+ cdef vector[vector[double]] c_filtration_grid = tuple(api.asnumpy(f).astype(np.float64) for f in filtration_grid) # may be faster with loop on views
8331
8651
  if coordinate_values and inplace:
8332
- self.filtration_grid = c_filtration_grid
8652
+ self.filtration_grid = filtration_grid
8333
8653
  if inplace or not coordinate_values:
8334
8654
  self.get_ptr().squeeze_filtration_inplace(c_filtration_grid, coordinate_values)
8335
8655
  else:
8336
8656
  out = SimplexTreeMulti_KFi32(num_parameters=self.num_parameters)
8337
8657
  self.get_ptr().squeeze_filtration(out.thisptr, c_filtration_grid)
8338
- out.filtration_grid = c_filtration_grid
8658
+ out.filtration_grid = filtration_grid
8339
8659
  return out
8340
8660
  return self
8341
8661
 
8662
+ def unsqueeze(self, grid=None)->SimplexTreeMulti_KFf64:
8663
+ from multipers.grids import sanitize_grid
8664
+ grid = self.filtration_grid if grid is None else grid
8665
+
8666
+ cdef vector[vector[double]] cgrid = sanitize_grid(grid, numpyfy=True)
8667
+ new_slicer = SimplexTreeMulti_KFf64()
8668
+ new_slicer.get_ptr().unsqueeze_filtration(self.thisptr, cgrid)
8669
+
8670
+ return new_slicer
8671
+
8342
8672
  @property
8343
8673
  def is_squeezed(self)->bool:
8344
- return self.num_vertices > 0 and len(self.filtration_grid)>0 and len(self.filtration_grid[0]) > 0
8674
+ return self.num_vertices > 0 and self.filtration_grid is not None and len(self.filtration_grid)>0 and len(self.filtration_grid[0]) > 0
8345
8675
 
8346
8676
  @property
8347
8677
  def dtype(self)->type:
@@ -8634,8 +8964,8 @@ cdef class SimplexTreeMulti_Ff64:
8634
8964
  """
8635
8965
  cdef public intptr_t thisptr
8636
8966
 
8637
- cdef public vector[vector[double]] filtration_grid
8638
- cdef public bool _is_function_simplextree
8967
+ cdef public object filtration_grid
8968
+ cdef public bool _is_function_simplextree # TODO : deprecate
8639
8969
  # Get the pointer casted as it should be
8640
8970
  cdef Simplex_tree_multi_interface[Ff64, double]* get_ptr(self) noexcept nogil:
8641
8971
  return <Simplex_tree_multi_interface[Ff64, double]*>(self.thisptr)
@@ -8706,7 +9036,7 @@ cdef class SimplexTreeMulti_Ff64:
8706
9036
  self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[Ff64, double]())
8707
9037
  self.set_num_parameter(num_parameters)
8708
9038
  self._is_function_simplextree = False
8709
- self.filtration_grid=[[]*num_parameters]
9039
+ self.filtration_grid=[]
8710
9040
 
8711
9041
  def __dealloc__(self):
8712
9042
  cdef Simplex_tree_multi_interface[Ff64,double]* ptr = self.get_ptr()
@@ -9651,11 +9981,26 @@ cdef class SimplexTreeMulti_Ff64:
9651
9981
  out = self.get_ptr().get_filtration_values(degrees)
9652
9982
  filtrations_values = [np.asarray(filtration) for filtration in out]
9653
9983
  # Removes infs
9654
- if inf_to_nan:
9984
+ if inf_to_nan and np.dtype(filtrations_values[0].dtype).kind == 'f':
9655
9985
  for i,f in enumerate(filtrations_values):
9656
9986
  filtrations_values[i][f == np.inf] = np.nan
9657
9987
  filtrations_values[i][f == - np.inf] = np.nan
9658
9988
  return filtrations_values
9989
+ def _clean_filtration_grid(self):
9990
+ """
9991
+ Removes the values in filtration_grid that are not linked to any splx.
9992
+ """
9993
+ if not self.is_squeezed:
9994
+ raise ValueError("No grid to clean.")
9995
+ F = self.filtration_grid
9996
+ self.filtration_grid=None
9997
+ cleaned_coordinates = compute_grid(self)
9998
+ new_st = self.grid_squeeze(cleaned_coordinates)
9999
+
10000
+ self.thisptr, new_st.thisptr = new_st.thisptr, self.thisptr
10001
+ self.filtration_grid = tuple(f[g] for f,g in zip(F,cleaned_coordinates))
10002
+ return self
10003
+
9659
10004
 
9660
10005
 
9661
10006
  def get_filtration_grid(self, resolution:Iterable[int]|None=None, degrees:Iterable[int]|None=None, drop_quantiles:float|tuple=0, grid_strategy:_available_strategies="exact")->Iterable[np.ndarray]:
@@ -9691,8 +10036,16 @@ cdef class SimplexTreeMulti_Ff64:
9691
10036
  return compute_grid(filtrations_values, resolution=resolution,strategy=grid_strategy,drop_quantiles=drop_quantiles)
9692
10037
 
9693
10038
 
9694
-
9695
- def grid_squeeze(self, filtration_grid:np.ndarray|list|None=None, bool coordinate_values=True, force=False, grid_strategy:_available_strategies = "exact", inplace=False, **filtration_grid_kwargs):
10039
+ def grid_squeeze(
10040
+ self,
10041
+ filtration_grid:np.ndarray|list|None=None,
10042
+ bool coordinate_values=True,
10043
+ force=False,
10044
+ strategy:_available_strategies = "exact",
10045
+ grid_strategy=None,
10046
+ inplace=False,
10047
+ **filtration_grid_kwargs
10048
+ )->SimplexTreeMulti_Fi32 | SimplexTreeMulti_Ff64:
9696
10049
  """
9697
10050
  Fit the filtration of the simplextree to a grid.
9698
10051
 
@@ -9703,26 +10056,50 @@ cdef class SimplexTreeMulti_Ff64:
9703
10056
  """
9704
10057
  if not force and self.is_squeezed:
9705
10058
  raise Exception("SimplexTree already squeezed, use `force=True` if that's really what you want to do.")
10059
+
10060
+ if grid_strategy is not None:
10061
+ warn("`grid_strategy` is deprecated, use `strategy` instead.",DeprecationWarning)
10062
+ strategy=grid_strategy
10063
+
10064
+ if self.is_squeezed:
10065
+ warn("(copy warning) Squeezing an already squeezed slicer.")
10066
+ temp = self.unsqueeze()
10067
+ subgrid = compute_grid(self.filtration_grid, strategy=strategy, resolution=resolution)
10068
+ return temp.grid_squeeze(subgrid, coordinates=coordinates, inplace=inplace)
10069
+
9706
10070
  #TODO : multi-critical
9707
10071
  if filtration_grid is None:
9708
- filtration_grid = self.get_filtration_grid(grid_strategy=grid_strategy, **filtration_grid_kwargs)
9709
- cdef vector[vector[double]] c_filtration_grid = filtration_grid
9710
- assert <int>c_filtration_grid.size() == self.get_ptr().get_number_of_parameters(), f"Grid has to be of size {self.num_parameters}, got {filtration_grid.size()}"
9711
- cdef intptr_t ptr = self.thisptr
10072
+ filtration_grid = self.get_filtration_grid(grid_strategy=strategy, **filtration_grid_kwargs)
10073
+ else:
10074
+ filtration_grid = sanitize_grid(filtration_grid)
10075
+ if len(filtration_grid) != self.num_parameters:
10076
+ raise ValueError(f"Invalid grid to squeeze onto. Got {len(filtration_grid)=} != {self.num_parameters=}.")
10077
+ api = api_from_tensor(filtration_grid[0])
10078
+ cdef vector[vector[double]] c_filtration_grid = tuple(api.asnumpy(f).astype(np.float64) for f in filtration_grid) # may be faster with loop on views
9712
10079
  if coordinate_values and inplace:
9713
- self.filtration_grid = c_filtration_grid
10080
+ self.filtration_grid = filtration_grid
9714
10081
  if inplace or not coordinate_values:
9715
10082
  self.get_ptr().squeeze_filtration_inplace(c_filtration_grid, coordinate_values)
9716
10083
  else:
9717
10084
  out = SimplexTreeMulti_Fi32(num_parameters=self.num_parameters)
9718
10085
  self.get_ptr().squeeze_filtration(out.thisptr, c_filtration_grid)
9719
- out.filtration_grid = c_filtration_grid
10086
+ out.filtration_grid = filtration_grid
9720
10087
  return out
9721
10088
  return self
9722
10089
 
10090
+ def unsqueeze(self, grid=None)->SimplexTreeMulti_Ff64:
10091
+ from multipers.grids import sanitize_grid
10092
+ grid = self.filtration_grid if grid is None else grid
10093
+
10094
+ cdef vector[vector[double]] cgrid = sanitize_grid(grid, numpyfy=True)
10095
+ new_slicer = SimplexTreeMulti_Ff64()
10096
+ new_slicer.get_ptr().unsqueeze_filtration(self.thisptr, cgrid)
10097
+
10098
+ return new_slicer
10099
+
9723
10100
  @property
9724
10101
  def is_squeezed(self)->bool:
9725
- return self.num_vertices > 0 and len(self.filtration_grid)>0 and len(self.filtration_grid[0]) > 0
10102
+ return self.num_vertices > 0 and self.filtration_grid is not None and len(self.filtration_grid)>0 and len(self.filtration_grid[0]) > 0
9726
10103
 
9727
10104
  @property
9728
10105
  def dtype(self)->type:
@@ -10243,7 +10620,8 @@ def _euler_signed_measure(simplextree, mass_default=None, bool verbose=False):
10243
10620
  `[signed_measure_of_degree for degree in degrees]`
10244
10621
  with `signed_measure_of_degree` of the form `(dirac location, dirac weights)`.
10245
10622
  """
10246
- assert len(simplextree.filtration_grid[0]) > 0, "Squeeze grid first."
10623
+ if not simplextree.is_squeezed:
10624
+ raise ValueError("Squeeze grid first.")
10247
10625
  cdef bool zero_pad = mass_default is not None
10248
10626
  # assert simplextree.num_parameters == 2
10249
10627
  grid_shape = np.array([len(f) for f in simplextree.filtration_grid])
@@ -10361,9 +10739,9 @@ def _rank_signed_measure(simplextree, vector[indices_type] degrees, mass_default
10361
10739
  else:
10362
10740
  mass_default = np.asarray(mass_default)
10363
10741
  assert mass_default.ndim == 1 and mass_default.shape[0] == simplextree.num_parameters, "Mass default has to be an array like of shape (num_parameters,)"
10364
- if zero_pad:
10365
- for i, _ in enumerate(grid_shape):
10366
- grid_shape[i] += 1 # adds a 0
10742
+ # if zero_pad:
10743
+ # for i, _ in enumerate(grid_shape):
10744
+ # grid_shape[i] += 1 # adds a 0
10367
10745
  # grid_conversion = tuple(np.concatenate([f, [mass_default[i]]]) for i,f in enumerate(grid_conversion))
10368
10746
 
10369
10747
  assert len(grid_shape) == simplextree.num_parameters, "Grid shape size has to be the number of parameters."