multipers 2.3.0__cp312-cp312-win_amd64.whl → 2.3.2__cp312-cp312-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.
- multipers/_signed_measure_meta.py +71 -65
- multipers/array_api/__init__.py +39 -0
- multipers/array_api/numpy.py +34 -0
- multipers/array_api/torch.py +35 -0
- multipers/distances.py +6 -2
- multipers/{ml/convolutions.py → filtrations/density.py} +67 -13
- multipers/filtrations/filtrations.py +76 -17
- multipers/function_rips.cp312-win_amd64.pyd +0 -0
- multipers/grids.cp312-win_amd64.pyd +0 -0
- multipers/grids.pyx +144 -61
- multipers/gudhi/Simplex_tree_multi_interface.h +36 -1
- multipers/gudhi/gudhi/Multi_persistence/Box.h +3 -0
- multipers/gudhi/gudhi/One_critical_filtration.h +18 -9
- multipers/gudhi/mma_interface_h0.h +1 -1
- multipers/gudhi/mma_interface_matrix.h +10 -1
- multipers/gudhi/naive_merge_tree.h +1 -1
- multipers/gudhi/truc.h +555 -42
- multipers/io.cp312-win_amd64.pyd +0 -0
- multipers/io.pyx +26 -93
- multipers/ml/mma.py +4 -4
- multipers/ml/point_clouds.py +2 -2
- multipers/ml/signed_measures.py +63 -65
- multipers/mma_structures.cp312-win_amd64.pyd +0 -0
- multipers/mma_structures.pxd +2 -1
- multipers/mma_structures.pyx +56 -16
- multipers/mma_structures.pyx.tp +14 -5
- multipers/multiparameter_module_approximation/approximation.h +48 -14
- multipers/multiparameter_module_approximation.cp312-win_amd64.pyd +0 -0
- multipers/multiparameter_module_approximation.pyx +27 -8
- multipers/plots.py +2 -1
- multipers/point_measure.cp312-win_amd64.pyd +0 -0
- multipers/point_measure.pyx +6 -2
- multipers/simplex_tree_multi.cp312-win_amd64.pyd +0 -0
- multipers/simplex_tree_multi.pxd +1 -0
- multipers/simplex_tree_multi.pyx +632 -146
- multipers/simplex_tree_multi.pyx.tp +92 -24
- multipers/slicer.cp312-win_amd64.pyd +0 -0
- multipers/slicer.pxd +779 -177
- multipers/slicer.pxd.tp +24 -5
- multipers/slicer.pyx +5657 -1427
- multipers/slicer.pyx.tp +211 -48
- multipers/tbb12.dll +0 -0
- multipers/tbbbind_2_5.dll +0 -0
- multipers/tbbmalloc.dll +0 -0
- multipers/tbbmalloc_proxy.dll +0 -0
- multipers/tensor/tensor.h +1 -1
- multipers/tests/__init__.py +9 -4
- multipers/torch/diff_grids.py +30 -7
- multipers/torch/rips_density.py +1 -1
- {multipers-2.3.0.dist-info → multipers-2.3.2.dist-info}/METADATA +4 -25
- {multipers-2.3.0.dist-info → multipers-2.3.2.dist-info}/RECORD +54 -51
- {multipers-2.3.0.dist-info → multipers-2.3.2.dist-info}/WHEEL +1 -1
- {multipers-2.3.0.dist-info → multipers-2.3.2.dist-info/licenses}/LICENSE +0 -0
- {multipers-2.3.0.dist-info → multipers-2.3.2.dist-info}/top_level.txt +0 -0
multipers/simplex_tree_multi.pyx
CHANGED
|
@@ -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,15 +82,15 @@ cdef class SimplexTreeMulti_KFi32:
|
|
|
81
82
|
"""
|
|
82
83
|
cdef public intptr_t thisptr
|
|
83
84
|
|
|
84
|
-
cdef public
|
|
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)
|
|
89
90
|
|
|
90
91
|
# cdef Simplex_tree_persistence_interface * pcohptr
|
|
91
92
|
# Fake constructor that does nothing but documenting the constructor
|
|
92
|
-
def __init__(self, other = None, num_parameters:int
|
|
93
|
+
def __init__(self, other = None, num_parameters:int=-1,default_values=[], safe_conversion=False):
|
|
93
94
|
"""SimplexTreeMulti constructor.
|
|
94
95
|
|
|
95
96
|
:param other: If `other` is `None` (default value), an empty `SimplexTreeMulti` is created.
|
|
@@ -114,7 +115,7 @@ cdef class SimplexTreeMulti_KFi32:
|
|
|
114
115
|
def is_kcritical(self)->bool:
|
|
115
116
|
return True
|
|
116
117
|
# The real cython constructor
|
|
117
|
-
def __cinit__(self, other = None, int num_parameters
|
|
118
|
+
def __cinit__(self, other = None, int num_parameters=-1,
|
|
118
119
|
default_values=np.asarray([SimplexTreeMulti_KFi32.T_minus_inf()]), # I'm not sure why `[]` does not work. Cython bug ?
|
|
119
120
|
bool safe_conversion=False,
|
|
120
121
|
): #TODO doc
|
|
@@ -127,10 +128,13 @@ cdef class SimplexTreeMulti_KFi32:
|
|
|
127
128
|
if isinstance(other, SimplexTreeMulti_KFi32):
|
|
128
129
|
other_ptr = other.thisptr
|
|
129
130
|
self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[KFi32, int32_t](dereference(<Simplex_tree_multi_interface[KFi32, int32_t]*>other_ptr))) ## prevents calling destructor of other
|
|
130
|
-
num_parameters
|
|
131
|
+
if num_parameters <=0:
|
|
132
|
+
num_parameters = other.num_parameters
|
|
131
133
|
self.filtration_grid = other.filtration_grid
|
|
132
134
|
elif isinstance(other, SimplexTree): # Constructs a SimplexTreeMulti from a SimplexTree
|
|
133
135
|
self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[KFi32, int32_t]())
|
|
136
|
+
if num_parameters <= 0:
|
|
137
|
+
num_parameters = 1
|
|
134
138
|
if safe_conversion or SAFE_CONVERSION:
|
|
135
139
|
new_st_multi = _safe_simplextree_multify_KFi32(other, num_parameters = num_parameters, default_values=np.asarray(default_values))
|
|
136
140
|
self.thisptr, new_st_multi.thisptr = new_st_multi.thisptr, self.thisptr
|
|
@@ -145,10 +149,12 @@ cdef class SimplexTreeMulti_KFi32:
|
|
|
145
149
|
else:
|
|
146
150
|
raise TypeError("`other` argument requires to be of type `SimplexTree`, `SimplexTreeMulti`, or `None`.")
|
|
147
151
|
else:
|
|
152
|
+
if num_parameters <=0:
|
|
153
|
+
num_parameters = 2 # I don't know how dangerous this is, but this is mostly used.
|
|
148
154
|
self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[KFi32, int32_t]())
|
|
149
|
-
self.
|
|
155
|
+
self.set_num_parameter(num_parameters)
|
|
150
156
|
self._is_function_simplextree = False
|
|
151
|
-
self.filtration_grid=[
|
|
157
|
+
self.filtration_grid=[]
|
|
152
158
|
|
|
153
159
|
def __dealloc__(self):
|
|
154
160
|
cdef Simplex_tree_multi_interface[KFi32,int32_t]* ptr = self.get_ptr()
|
|
@@ -158,6 +164,9 @@ cdef class SimplexTreeMulti_KFi32:
|
|
|
158
164
|
|
|
159
165
|
def __repr__(self):
|
|
160
166
|
return f"SimplexTreeMulti[dtype={np.dtype(self.dtype).name},num_param={self.num_parameters},kcritical={self.is_kcritical},is_squeezed={self.is_squeezed},max_dim={self.dimension}]"
|
|
167
|
+
def __len__(self):
|
|
168
|
+
return self.num_simplices
|
|
169
|
+
|
|
161
170
|
def __getstate__(self):
|
|
162
171
|
""":returns: Serialized (or flattened) SimplexTree data structure in order to pickle SimplexTree.
|
|
163
172
|
:rtype: numpy.array of shape (n,)
|
|
@@ -813,11 +822,26 @@ cdef class SimplexTreeMulti_KFi32:
|
|
|
813
822
|
out = self.get_ptr().get_filtration_values(degrees)
|
|
814
823
|
filtrations_values = [np.asarray(filtration) for filtration in out]
|
|
815
824
|
# Removes infs
|
|
816
|
-
if inf_to_nan:
|
|
825
|
+
if inf_to_nan and np.dtype(filtrations_values[0].dtype).kind == 'f':
|
|
817
826
|
for i,f in enumerate(filtrations_values):
|
|
818
827
|
filtrations_values[i][f == np.inf] = np.nan
|
|
819
828
|
filtrations_values[i][f == - np.inf] = np.nan
|
|
820
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
|
+
|
|
821
845
|
|
|
822
846
|
|
|
823
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]:
|
|
@@ -853,8 +877,16 @@ cdef class SimplexTreeMulti_KFi32:
|
|
|
853
877
|
return compute_grid(filtrations_values, resolution=resolution,strategy=grid_strategy,drop_quantiles=drop_quantiles)
|
|
854
878
|
|
|
855
879
|
|
|
856
|
-
|
|
857
|
-
|
|
880
|
+
def grid_squeeze(
|
|
881
|
+
self,
|
|
882
|
+
filtration_grid:np.ndarray|list|None=None,
|
|
883
|
+
bool coordinate_values=True,
|
|
884
|
+
bool force=False,
|
|
885
|
+
str strategy:_available_strategies = "exact",
|
|
886
|
+
grid_strategy=None,
|
|
887
|
+
bool inplace=False,
|
|
888
|
+
**filtration_grid_kwargs
|
|
889
|
+
)->SimplexTreeMulti_KFi32 | SimplexTreeMulti_KFi32:
|
|
858
890
|
"""
|
|
859
891
|
Fit the filtration of the simplextree to a grid.
|
|
860
892
|
|
|
@@ -865,26 +897,50 @@ cdef class SimplexTreeMulti_KFi32:
|
|
|
865
897
|
"""
|
|
866
898
|
if not force and self.is_squeezed:
|
|
867
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
|
+
|
|
868
911
|
#TODO : multi-critical
|
|
869
912
|
if filtration_grid is None:
|
|
870
|
-
filtration_grid = self.get_filtration_grid(grid_strategy=
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
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
|
|
874
920
|
if coordinate_values and inplace:
|
|
875
|
-
self.filtration_grid =
|
|
921
|
+
self.filtration_grid = filtration_grid
|
|
876
922
|
if inplace or not coordinate_values:
|
|
877
923
|
self.get_ptr().squeeze_filtration_inplace(c_filtration_grid, coordinate_values)
|
|
878
924
|
else:
|
|
879
925
|
out = SimplexTreeMulti_KFi32(num_parameters=self.num_parameters)
|
|
880
926
|
self.get_ptr().squeeze_filtration(out.thisptr, c_filtration_grid)
|
|
881
|
-
out.filtration_grid =
|
|
927
|
+
out.filtration_grid = filtration_grid
|
|
882
928
|
return out
|
|
883
929
|
return self
|
|
884
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
|
+
|
|
885
941
|
@property
|
|
886
942
|
def is_squeezed(self)->bool:
|
|
887
|
-
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
|
|
888
944
|
|
|
889
945
|
@property
|
|
890
946
|
def dtype(self)->type:
|
|
@@ -1177,15 +1233,15 @@ cdef class SimplexTreeMulti_Fi32:
|
|
|
1177
1233
|
"""
|
|
1178
1234
|
cdef public intptr_t thisptr
|
|
1179
1235
|
|
|
1180
|
-
cdef public
|
|
1181
|
-
cdef public bool _is_function_simplextree
|
|
1236
|
+
cdef public object filtration_grid
|
|
1237
|
+
cdef public bool _is_function_simplextree # TODO : deprecate
|
|
1182
1238
|
# Get the pointer casted as it should be
|
|
1183
1239
|
cdef Simplex_tree_multi_interface[Fi32, int32_t]* get_ptr(self) noexcept nogil:
|
|
1184
1240
|
return <Simplex_tree_multi_interface[Fi32, int32_t]*>(self.thisptr)
|
|
1185
1241
|
|
|
1186
1242
|
# cdef Simplex_tree_persistence_interface * pcohptr
|
|
1187
1243
|
# Fake constructor that does nothing but documenting the constructor
|
|
1188
|
-
def __init__(self, other = None, num_parameters:int
|
|
1244
|
+
def __init__(self, other = None, num_parameters:int=-1,default_values=[], safe_conversion=False):
|
|
1189
1245
|
"""SimplexTreeMulti constructor.
|
|
1190
1246
|
|
|
1191
1247
|
:param other: If `other` is `None` (default value), an empty `SimplexTreeMulti` is created.
|
|
@@ -1210,7 +1266,7 @@ cdef class SimplexTreeMulti_Fi32:
|
|
|
1210
1266
|
def is_kcritical(self)->bool:
|
|
1211
1267
|
return False
|
|
1212
1268
|
# The real cython constructor
|
|
1213
|
-
def __cinit__(self, other = None, int num_parameters
|
|
1269
|
+
def __cinit__(self, other = None, int num_parameters=-1,
|
|
1214
1270
|
default_values=np.asarray([SimplexTreeMulti_Fi32.T_minus_inf()]), # I'm not sure why `[]` does not work. Cython bug ?
|
|
1215
1271
|
bool safe_conversion=False,
|
|
1216
1272
|
): #TODO doc
|
|
@@ -1223,10 +1279,13 @@ cdef class SimplexTreeMulti_Fi32:
|
|
|
1223
1279
|
if isinstance(other, SimplexTreeMulti_Fi32):
|
|
1224
1280
|
other_ptr = other.thisptr
|
|
1225
1281
|
self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[Fi32, int32_t](dereference(<Simplex_tree_multi_interface[Fi32, int32_t]*>other_ptr))) ## prevents calling destructor of other
|
|
1226
|
-
num_parameters
|
|
1282
|
+
if num_parameters <=0:
|
|
1283
|
+
num_parameters = other.num_parameters
|
|
1227
1284
|
self.filtration_grid = other.filtration_grid
|
|
1228
1285
|
elif isinstance(other, SimplexTree): # Constructs a SimplexTreeMulti from a SimplexTree
|
|
1229
1286
|
self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[Fi32, int32_t]())
|
|
1287
|
+
if num_parameters <= 0:
|
|
1288
|
+
num_parameters = 1
|
|
1230
1289
|
if safe_conversion or SAFE_CONVERSION:
|
|
1231
1290
|
new_st_multi = _safe_simplextree_multify_Fi32(other, num_parameters = num_parameters, default_values=np.asarray(default_values))
|
|
1232
1291
|
self.thisptr, new_st_multi.thisptr = new_st_multi.thisptr, self.thisptr
|
|
@@ -1241,10 +1300,12 @@ cdef class SimplexTreeMulti_Fi32:
|
|
|
1241
1300
|
else:
|
|
1242
1301
|
raise TypeError("`other` argument requires to be of type `SimplexTree`, `SimplexTreeMulti`, or `None`.")
|
|
1243
1302
|
else:
|
|
1303
|
+
if num_parameters <=0:
|
|
1304
|
+
num_parameters = 2 # I don't know how dangerous this is, but this is mostly used.
|
|
1244
1305
|
self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[Fi32, int32_t]())
|
|
1245
|
-
self.
|
|
1306
|
+
self.set_num_parameter(num_parameters)
|
|
1246
1307
|
self._is_function_simplextree = False
|
|
1247
|
-
self.filtration_grid=[
|
|
1308
|
+
self.filtration_grid=[]
|
|
1248
1309
|
|
|
1249
1310
|
def __dealloc__(self):
|
|
1250
1311
|
cdef Simplex_tree_multi_interface[Fi32,int32_t]* ptr = self.get_ptr()
|
|
@@ -1254,6 +1315,9 @@ cdef class SimplexTreeMulti_Fi32:
|
|
|
1254
1315
|
|
|
1255
1316
|
def __repr__(self):
|
|
1256
1317
|
return f"SimplexTreeMulti[dtype={np.dtype(self.dtype).name},num_param={self.num_parameters},kcritical={self.is_kcritical},is_squeezed={self.is_squeezed},max_dim={self.dimension}]"
|
|
1318
|
+
def __len__(self):
|
|
1319
|
+
return self.num_simplices
|
|
1320
|
+
|
|
1257
1321
|
def __getstate__(self):
|
|
1258
1322
|
""":returns: Serialized (or flattened) SimplexTree data structure in order to pickle SimplexTree.
|
|
1259
1323
|
:rtype: numpy.array of shape (n,)
|
|
@@ -1918,7 +1982,16 @@ cdef class SimplexTreeMulti_Fi32:
|
|
|
1918
1982
|
out = self.get_ptr().get_edge_list()
|
|
1919
1983
|
return out
|
|
1920
1984
|
|
|
1921
|
-
def collapse_edges(
|
|
1985
|
+
def collapse_edges(
|
|
1986
|
+
self,
|
|
1987
|
+
int num=1,
|
|
1988
|
+
int max_dimension = 0,
|
|
1989
|
+
bool progress=False,
|
|
1990
|
+
bool strong=True,
|
|
1991
|
+
bool full=False,
|
|
1992
|
+
bool ignore_warning=False,
|
|
1993
|
+
bool auto_clean=True,
|
|
1994
|
+
)->SimplexTreeMulti_Fi32:
|
|
1922
1995
|
"""Edge collapse for 1-critical 2-parameter clique complex (see https://arxiv.org/abs/2211.05574).
|
|
1923
1996
|
It uses the code from the github repository https://github.com/aj-alonso/filtration_domination .
|
|
1924
1997
|
|
|
@@ -1968,6 +2041,8 @@ cdef class SimplexTreeMulti_Fi32:
|
|
|
1968
2041
|
edges = _collapse_edge_list(edges, num=num, full=full, strong=strong, progress=progress)
|
|
1969
2042
|
# Retrieves the collapsed simplicial complex
|
|
1970
2043
|
self._reconstruct_from_edge_list(edges, swap=True, expand_dimension=max_dimension)
|
|
2044
|
+
if self.is_squeezed and auto_clean:
|
|
2045
|
+
self._clean_filtration_grid()
|
|
1971
2046
|
return self
|
|
1972
2047
|
|
|
1973
2048
|
@cython.inline
|
|
@@ -2186,11 +2261,26 @@ cdef class SimplexTreeMulti_Fi32:
|
|
|
2186
2261
|
out = self.get_ptr().get_filtration_values(degrees)
|
|
2187
2262
|
filtrations_values = [np.asarray(filtration) for filtration in out]
|
|
2188
2263
|
# Removes infs
|
|
2189
|
-
if inf_to_nan:
|
|
2264
|
+
if inf_to_nan and np.dtype(filtrations_values[0].dtype).kind == 'f':
|
|
2190
2265
|
for i,f in enumerate(filtrations_values):
|
|
2191
2266
|
filtrations_values[i][f == np.inf] = np.nan
|
|
2192
2267
|
filtrations_values[i][f == - np.inf] = np.nan
|
|
2193
2268
|
return filtrations_values
|
|
2269
|
+
def _clean_filtration_grid(self):
|
|
2270
|
+
"""
|
|
2271
|
+
Removes the values in filtration_grid that are not linked to any splx.
|
|
2272
|
+
"""
|
|
2273
|
+
if not self.is_squeezed:
|
|
2274
|
+
raise ValueError("No grid to clean.")
|
|
2275
|
+
F = self.filtration_grid
|
|
2276
|
+
self.filtration_grid=None
|
|
2277
|
+
cleaned_coordinates = compute_grid(self)
|
|
2278
|
+
new_st = self.grid_squeeze(cleaned_coordinates)
|
|
2279
|
+
|
|
2280
|
+
self.thisptr, new_st.thisptr = new_st.thisptr, self.thisptr
|
|
2281
|
+
self.filtration_grid = tuple(f[g] for f,g in zip(F,cleaned_coordinates))
|
|
2282
|
+
return self
|
|
2283
|
+
|
|
2194
2284
|
|
|
2195
2285
|
|
|
2196
2286
|
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]:
|
|
@@ -2226,8 +2316,16 @@ cdef class SimplexTreeMulti_Fi32:
|
|
|
2226
2316
|
return compute_grid(filtrations_values, resolution=resolution,strategy=grid_strategy,drop_quantiles=drop_quantiles)
|
|
2227
2317
|
|
|
2228
2318
|
|
|
2229
|
-
|
|
2230
|
-
|
|
2319
|
+
def grid_squeeze(
|
|
2320
|
+
self,
|
|
2321
|
+
filtration_grid:np.ndarray|list|None=None,
|
|
2322
|
+
bool coordinate_values=True,
|
|
2323
|
+
bool force=False,
|
|
2324
|
+
str strategy:_available_strategies = "exact",
|
|
2325
|
+
grid_strategy=None,
|
|
2326
|
+
bool inplace=False,
|
|
2327
|
+
**filtration_grid_kwargs
|
|
2328
|
+
)->SimplexTreeMulti_Fi32 | SimplexTreeMulti_Fi32:
|
|
2231
2329
|
"""
|
|
2232
2330
|
Fit the filtration of the simplextree to a grid.
|
|
2233
2331
|
|
|
@@ -2238,26 +2336,50 @@ cdef class SimplexTreeMulti_Fi32:
|
|
|
2238
2336
|
"""
|
|
2239
2337
|
if not force and self.is_squeezed:
|
|
2240
2338
|
raise Exception("SimplexTree already squeezed, use `force=True` if that's really what you want to do.")
|
|
2339
|
+
|
|
2340
|
+
if grid_strategy is not None:
|
|
2341
|
+
warn("`grid_strategy` is deprecated, use `strategy` instead.",DeprecationWarning)
|
|
2342
|
+
strategy=grid_strategy
|
|
2343
|
+
|
|
2344
|
+
if self.is_squeezed:
|
|
2345
|
+
warn("(copy warning) Squeezing an already squeezed slicer.")
|
|
2346
|
+
temp = self.unsqueeze()
|
|
2347
|
+
subgrid = compute_grid(self.filtration_grid, strategy=strategy, resolution=resolution)
|
|
2348
|
+
return temp.grid_squeeze(subgrid, coordinates=coordinates, inplace=inplace)
|
|
2349
|
+
|
|
2241
2350
|
#TODO : multi-critical
|
|
2242
2351
|
if filtration_grid is None:
|
|
2243
|
-
filtration_grid = self.get_filtration_grid(grid_strategy=
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2352
|
+
filtration_grid = self.get_filtration_grid(grid_strategy=strategy, **filtration_grid_kwargs)
|
|
2353
|
+
else:
|
|
2354
|
+
filtration_grid = sanitize_grid(filtration_grid)
|
|
2355
|
+
if len(filtration_grid) != self.num_parameters:
|
|
2356
|
+
raise ValueError(f"Invalid grid to squeeze onto. Got {len(filtration_grid)=} != {self.num_parameters=}.")
|
|
2357
|
+
api = api_from_tensor(filtration_grid[0])
|
|
2358
|
+
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
|
|
2247
2359
|
if coordinate_values and inplace:
|
|
2248
|
-
self.filtration_grid =
|
|
2360
|
+
self.filtration_grid = filtration_grid
|
|
2249
2361
|
if inplace or not coordinate_values:
|
|
2250
2362
|
self.get_ptr().squeeze_filtration_inplace(c_filtration_grid, coordinate_values)
|
|
2251
2363
|
else:
|
|
2252
2364
|
out = SimplexTreeMulti_Fi32(num_parameters=self.num_parameters)
|
|
2253
2365
|
self.get_ptr().squeeze_filtration(out.thisptr, c_filtration_grid)
|
|
2254
|
-
out.filtration_grid =
|
|
2366
|
+
out.filtration_grid = filtration_grid
|
|
2255
2367
|
return out
|
|
2256
2368
|
return self
|
|
2257
2369
|
|
|
2370
|
+
def unsqueeze(self, grid=None)->SimplexTreeMulti_Ff64:
|
|
2371
|
+
from multipers.grids import sanitize_grid
|
|
2372
|
+
grid = self.filtration_grid if grid is None else grid
|
|
2373
|
+
|
|
2374
|
+
cdef vector[vector[double]] cgrid = sanitize_grid(grid, numpyfy=True)
|
|
2375
|
+
new_slicer = SimplexTreeMulti_Ff64()
|
|
2376
|
+
new_slicer.get_ptr().unsqueeze_filtration(self.thisptr, cgrid)
|
|
2377
|
+
|
|
2378
|
+
return new_slicer
|
|
2379
|
+
|
|
2258
2380
|
@property
|
|
2259
2381
|
def is_squeezed(self)->bool:
|
|
2260
|
-
return self.num_vertices > 0 and len(self.filtration_grid)>0 and len(self.filtration_grid[0]) > 0
|
|
2382
|
+
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
|
|
2261
2383
|
|
|
2262
2384
|
@property
|
|
2263
2385
|
def dtype(self)->type:
|
|
@@ -2548,15 +2670,15 @@ cdef class SimplexTreeMulti_KFi64:
|
|
|
2548
2670
|
"""
|
|
2549
2671
|
cdef public intptr_t thisptr
|
|
2550
2672
|
|
|
2551
|
-
cdef public
|
|
2552
|
-
cdef public bool _is_function_simplextree
|
|
2673
|
+
cdef public object filtration_grid
|
|
2674
|
+
cdef public bool _is_function_simplextree # TODO : deprecate
|
|
2553
2675
|
# Get the pointer casted as it should be
|
|
2554
2676
|
cdef Simplex_tree_multi_interface[KFi64, int64_t]* get_ptr(self) noexcept nogil:
|
|
2555
2677
|
return <Simplex_tree_multi_interface[KFi64, int64_t]*>(self.thisptr)
|
|
2556
2678
|
|
|
2557
2679
|
# cdef Simplex_tree_persistence_interface * pcohptr
|
|
2558
2680
|
# Fake constructor that does nothing but documenting the constructor
|
|
2559
|
-
def __init__(self, other = None, num_parameters:int
|
|
2681
|
+
def __init__(self, other = None, num_parameters:int=-1,default_values=[], safe_conversion=False):
|
|
2560
2682
|
"""SimplexTreeMulti constructor.
|
|
2561
2683
|
|
|
2562
2684
|
:param other: If `other` is `None` (default value), an empty `SimplexTreeMulti` is created.
|
|
@@ -2581,7 +2703,7 @@ cdef class SimplexTreeMulti_KFi64:
|
|
|
2581
2703
|
def is_kcritical(self)->bool:
|
|
2582
2704
|
return True
|
|
2583
2705
|
# The real cython constructor
|
|
2584
|
-
def __cinit__(self, other = None, int num_parameters
|
|
2706
|
+
def __cinit__(self, other = None, int num_parameters=-1,
|
|
2585
2707
|
default_values=np.asarray([SimplexTreeMulti_KFi64.T_minus_inf()]), # I'm not sure why `[]` does not work. Cython bug ?
|
|
2586
2708
|
bool safe_conversion=False,
|
|
2587
2709
|
): #TODO doc
|
|
@@ -2594,10 +2716,13 @@ cdef class SimplexTreeMulti_KFi64:
|
|
|
2594
2716
|
if isinstance(other, SimplexTreeMulti_KFi64):
|
|
2595
2717
|
other_ptr = other.thisptr
|
|
2596
2718
|
self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[KFi64, int64_t](dereference(<Simplex_tree_multi_interface[KFi64, int64_t]*>other_ptr))) ## prevents calling destructor of other
|
|
2597
|
-
num_parameters
|
|
2719
|
+
if num_parameters <=0:
|
|
2720
|
+
num_parameters = other.num_parameters
|
|
2598
2721
|
self.filtration_grid = other.filtration_grid
|
|
2599
2722
|
elif isinstance(other, SimplexTree): # Constructs a SimplexTreeMulti from a SimplexTree
|
|
2600
2723
|
self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[KFi64, int64_t]())
|
|
2724
|
+
if num_parameters <= 0:
|
|
2725
|
+
num_parameters = 1
|
|
2601
2726
|
if safe_conversion or SAFE_CONVERSION:
|
|
2602
2727
|
new_st_multi = _safe_simplextree_multify_KFi64(other, num_parameters = num_parameters, default_values=np.asarray(default_values))
|
|
2603
2728
|
self.thisptr, new_st_multi.thisptr = new_st_multi.thisptr, self.thisptr
|
|
@@ -2612,10 +2737,12 @@ cdef class SimplexTreeMulti_KFi64:
|
|
|
2612
2737
|
else:
|
|
2613
2738
|
raise TypeError("`other` argument requires to be of type `SimplexTree`, `SimplexTreeMulti`, or `None`.")
|
|
2614
2739
|
else:
|
|
2740
|
+
if num_parameters <=0:
|
|
2741
|
+
num_parameters = 2 # I don't know how dangerous this is, but this is mostly used.
|
|
2615
2742
|
self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[KFi64, int64_t]())
|
|
2616
|
-
self.
|
|
2743
|
+
self.set_num_parameter(num_parameters)
|
|
2617
2744
|
self._is_function_simplextree = False
|
|
2618
|
-
self.filtration_grid=[
|
|
2745
|
+
self.filtration_grid=[]
|
|
2619
2746
|
|
|
2620
2747
|
def __dealloc__(self):
|
|
2621
2748
|
cdef Simplex_tree_multi_interface[KFi64,int64_t]* ptr = self.get_ptr()
|
|
@@ -2625,6 +2752,9 @@ cdef class SimplexTreeMulti_KFi64:
|
|
|
2625
2752
|
|
|
2626
2753
|
def __repr__(self):
|
|
2627
2754
|
return f"SimplexTreeMulti[dtype={np.dtype(self.dtype).name},num_param={self.num_parameters},kcritical={self.is_kcritical},is_squeezed={self.is_squeezed},max_dim={self.dimension}]"
|
|
2755
|
+
def __len__(self):
|
|
2756
|
+
return self.num_simplices
|
|
2757
|
+
|
|
2628
2758
|
def __getstate__(self):
|
|
2629
2759
|
""":returns: Serialized (or flattened) SimplexTree data structure in order to pickle SimplexTree.
|
|
2630
2760
|
:rtype: numpy.array of shape (n,)
|
|
@@ -3280,11 +3410,26 @@ cdef class SimplexTreeMulti_KFi64:
|
|
|
3280
3410
|
out = self.get_ptr().get_filtration_values(degrees)
|
|
3281
3411
|
filtrations_values = [np.asarray(filtration) for filtration in out]
|
|
3282
3412
|
# Removes infs
|
|
3283
|
-
if inf_to_nan:
|
|
3413
|
+
if inf_to_nan and np.dtype(filtrations_values[0].dtype).kind == 'f':
|
|
3284
3414
|
for i,f in enumerate(filtrations_values):
|
|
3285
3415
|
filtrations_values[i][f == np.inf] = np.nan
|
|
3286
3416
|
filtrations_values[i][f == - np.inf] = np.nan
|
|
3287
3417
|
return filtrations_values
|
|
3418
|
+
def _clean_filtration_grid(self):
|
|
3419
|
+
"""
|
|
3420
|
+
Removes the values in filtration_grid that are not linked to any splx.
|
|
3421
|
+
"""
|
|
3422
|
+
if not self.is_squeezed:
|
|
3423
|
+
raise ValueError("No grid to clean.")
|
|
3424
|
+
F = self.filtration_grid
|
|
3425
|
+
self.filtration_grid=None
|
|
3426
|
+
cleaned_coordinates = compute_grid(self)
|
|
3427
|
+
new_st = self.grid_squeeze(cleaned_coordinates)
|
|
3428
|
+
|
|
3429
|
+
self.thisptr, new_st.thisptr = new_st.thisptr, self.thisptr
|
|
3430
|
+
self.filtration_grid = tuple(f[g] for f,g in zip(F,cleaned_coordinates))
|
|
3431
|
+
return self
|
|
3432
|
+
|
|
3288
3433
|
|
|
3289
3434
|
|
|
3290
3435
|
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]:
|
|
@@ -3320,8 +3465,16 @@ cdef class SimplexTreeMulti_KFi64:
|
|
|
3320
3465
|
return compute_grid(filtrations_values, resolution=resolution,strategy=grid_strategy,drop_quantiles=drop_quantiles)
|
|
3321
3466
|
|
|
3322
3467
|
|
|
3323
|
-
|
|
3324
|
-
|
|
3468
|
+
def grid_squeeze(
|
|
3469
|
+
self,
|
|
3470
|
+
filtration_grid:np.ndarray|list|None=None,
|
|
3471
|
+
bool coordinate_values=True,
|
|
3472
|
+
bool force=False,
|
|
3473
|
+
str strategy:_available_strategies = "exact",
|
|
3474
|
+
grid_strategy=None,
|
|
3475
|
+
bool inplace=False,
|
|
3476
|
+
**filtration_grid_kwargs
|
|
3477
|
+
)->SimplexTreeMulti_KFi32 | SimplexTreeMulti_KFi64:
|
|
3325
3478
|
"""
|
|
3326
3479
|
Fit the filtration of the simplextree to a grid.
|
|
3327
3480
|
|
|
@@ -3332,26 +3485,50 @@ cdef class SimplexTreeMulti_KFi64:
|
|
|
3332
3485
|
"""
|
|
3333
3486
|
if not force and self.is_squeezed:
|
|
3334
3487
|
raise Exception("SimplexTree already squeezed, use `force=True` if that's really what you want to do.")
|
|
3488
|
+
|
|
3489
|
+
if grid_strategy is not None:
|
|
3490
|
+
warn("`grid_strategy` is deprecated, use `strategy` instead.",DeprecationWarning)
|
|
3491
|
+
strategy=grid_strategy
|
|
3492
|
+
|
|
3493
|
+
if self.is_squeezed:
|
|
3494
|
+
warn("(copy warning) Squeezing an already squeezed slicer.")
|
|
3495
|
+
temp = self.unsqueeze()
|
|
3496
|
+
subgrid = compute_grid(self.filtration_grid, strategy=strategy, resolution=resolution)
|
|
3497
|
+
return temp.grid_squeeze(subgrid, coordinates=coordinates, inplace=inplace)
|
|
3498
|
+
|
|
3335
3499
|
#TODO : multi-critical
|
|
3336
3500
|
if filtration_grid is None:
|
|
3337
|
-
filtration_grid = self.get_filtration_grid(grid_strategy=
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3501
|
+
filtration_grid = self.get_filtration_grid(grid_strategy=strategy, **filtration_grid_kwargs)
|
|
3502
|
+
else:
|
|
3503
|
+
filtration_grid = sanitize_grid(filtration_grid)
|
|
3504
|
+
if len(filtration_grid) != self.num_parameters:
|
|
3505
|
+
raise ValueError(f"Invalid grid to squeeze onto. Got {len(filtration_grid)=} != {self.num_parameters=}.")
|
|
3506
|
+
api = api_from_tensor(filtration_grid[0])
|
|
3507
|
+
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
|
|
3341
3508
|
if coordinate_values and inplace:
|
|
3342
|
-
self.filtration_grid =
|
|
3509
|
+
self.filtration_grid = filtration_grid
|
|
3343
3510
|
if inplace or not coordinate_values:
|
|
3344
3511
|
self.get_ptr().squeeze_filtration_inplace(c_filtration_grid, coordinate_values)
|
|
3345
3512
|
else:
|
|
3346
3513
|
out = SimplexTreeMulti_KFi32(num_parameters=self.num_parameters)
|
|
3347
3514
|
self.get_ptr().squeeze_filtration(out.thisptr, c_filtration_grid)
|
|
3348
|
-
out.filtration_grid =
|
|
3515
|
+
out.filtration_grid = filtration_grid
|
|
3349
3516
|
return out
|
|
3350
3517
|
return self
|
|
3351
3518
|
|
|
3519
|
+
def unsqueeze(self, grid=None)->SimplexTreeMulti_KFf64:
|
|
3520
|
+
from multipers.grids import sanitize_grid
|
|
3521
|
+
grid = self.filtration_grid if grid is None else grid
|
|
3522
|
+
|
|
3523
|
+
cdef vector[vector[double]] cgrid = sanitize_grid(grid, numpyfy=True)
|
|
3524
|
+
new_slicer = SimplexTreeMulti_KFf64()
|
|
3525
|
+
new_slicer.get_ptr().unsqueeze_filtration(self.thisptr, cgrid)
|
|
3526
|
+
|
|
3527
|
+
return new_slicer
|
|
3528
|
+
|
|
3352
3529
|
@property
|
|
3353
3530
|
def is_squeezed(self)->bool:
|
|
3354
|
-
return self.num_vertices > 0 and len(self.filtration_grid)>0 and len(self.filtration_grid[0]) > 0
|
|
3531
|
+
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
|
|
3355
3532
|
|
|
3356
3533
|
@property
|
|
3357
3534
|
def dtype(self)->type:
|
|
@@ -3644,15 +3821,15 @@ cdef class SimplexTreeMulti_Fi64:
|
|
|
3644
3821
|
"""
|
|
3645
3822
|
cdef public intptr_t thisptr
|
|
3646
3823
|
|
|
3647
|
-
cdef public
|
|
3648
|
-
cdef public bool _is_function_simplextree
|
|
3824
|
+
cdef public object filtration_grid
|
|
3825
|
+
cdef public bool _is_function_simplextree # TODO : deprecate
|
|
3649
3826
|
# Get the pointer casted as it should be
|
|
3650
3827
|
cdef Simplex_tree_multi_interface[Fi64, int64_t]* get_ptr(self) noexcept nogil:
|
|
3651
3828
|
return <Simplex_tree_multi_interface[Fi64, int64_t]*>(self.thisptr)
|
|
3652
3829
|
|
|
3653
3830
|
# cdef Simplex_tree_persistence_interface * pcohptr
|
|
3654
3831
|
# Fake constructor that does nothing but documenting the constructor
|
|
3655
|
-
def __init__(self, other = None, num_parameters:int
|
|
3832
|
+
def __init__(self, other = None, num_parameters:int=-1,default_values=[], safe_conversion=False):
|
|
3656
3833
|
"""SimplexTreeMulti constructor.
|
|
3657
3834
|
|
|
3658
3835
|
:param other: If `other` is `None` (default value), an empty `SimplexTreeMulti` is created.
|
|
@@ -3677,7 +3854,7 @@ cdef class SimplexTreeMulti_Fi64:
|
|
|
3677
3854
|
def is_kcritical(self)->bool:
|
|
3678
3855
|
return False
|
|
3679
3856
|
# The real cython constructor
|
|
3680
|
-
def __cinit__(self, other = None, int num_parameters
|
|
3857
|
+
def __cinit__(self, other = None, int num_parameters=-1,
|
|
3681
3858
|
default_values=np.asarray([SimplexTreeMulti_Fi64.T_minus_inf()]), # I'm not sure why `[]` does not work. Cython bug ?
|
|
3682
3859
|
bool safe_conversion=False,
|
|
3683
3860
|
): #TODO doc
|
|
@@ -3690,10 +3867,13 @@ cdef class SimplexTreeMulti_Fi64:
|
|
|
3690
3867
|
if isinstance(other, SimplexTreeMulti_Fi64):
|
|
3691
3868
|
other_ptr = other.thisptr
|
|
3692
3869
|
self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[Fi64, int64_t](dereference(<Simplex_tree_multi_interface[Fi64, int64_t]*>other_ptr))) ## prevents calling destructor of other
|
|
3693
|
-
num_parameters
|
|
3870
|
+
if num_parameters <=0:
|
|
3871
|
+
num_parameters = other.num_parameters
|
|
3694
3872
|
self.filtration_grid = other.filtration_grid
|
|
3695
3873
|
elif isinstance(other, SimplexTree): # Constructs a SimplexTreeMulti from a SimplexTree
|
|
3696
3874
|
self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[Fi64, int64_t]())
|
|
3875
|
+
if num_parameters <= 0:
|
|
3876
|
+
num_parameters = 1
|
|
3697
3877
|
if safe_conversion or SAFE_CONVERSION:
|
|
3698
3878
|
new_st_multi = _safe_simplextree_multify_Fi64(other, num_parameters = num_parameters, default_values=np.asarray(default_values))
|
|
3699
3879
|
self.thisptr, new_st_multi.thisptr = new_st_multi.thisptr, self.thisptr
|
|
@@ -3708,10 +3888,12 @@ cdef class SimplexTreeMulti_Fi64:
|
|
|
3708
3888
|
else:
|
|
3709
3889
|
raise TypeError("`other` argument requires to be of type `SimplexTree`, `SimplexTreeMulti`, or `None`.")
|
|
3710
3890
|
else:
|
|
3891
|
+
if num_parameters <=0:
|
|
3892
|
+
num_parameters = 2 # I don't know how dangerous this is, but this is mostly used.
|
|
3711
3893
|
self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[Fi64, int64_t]())
|
|
3712
|
-
self.
|
|
3894
|
+
self.set_num_parameter(num_parameters)
|
|
3713
3895
|
self._is_function_simplextree = False
|
|
3714
|
-
self.filtration_grid=[
|
|
3896
|
+
self.filtration_grid=[]
|
|
3715
3897
|
|
|
3716
3898
|
def __dealloc__(self):
|
|
3717
3899
|
cdef Simplex_tree_multi_interface[Fi64,int64_t]* ptr = self.get_ptr()
|
|
@@ -3721,6 +3903,9 @@ cdef class SimplexTreeMulti_Fi64:
|
|
|
3721
3903
|
|
|
3722
3904
|
def __repr__(self):
|
|
3723
3905
|
return f"SimplexTreeMulti[dtype={np.dtype(self.dtype).name},num_param={self.num_parameters},kcritical={self.is_kcritical},is_squeezed={self.is_squeezed},max_dim={self.dimension}]"
|
|
3906
|
+
def __len__(self):
|
|
3907
|
+
return self.num_simplices
|
|
3908
|
+
|
|
3724
3909
|
def __getstate__(self):
|
|
3725
3910
|
""":returns: Serialized (or flattened) SimplexTree data structure in order to pickle SimplexTree.
|
|
3726
3911
|
:rtype: numpy.array of shape (n,)
|
|
@@ -4385,7 +4570,16 @@ cdef class SimplexTreeMulti_Fi64:
|
|
|
4385
4570
|
out = self.get_ptr().get_edge_list()
|
|
4386
4571
|
return out
|
|
4387
4572
|
|
|
4388
|
-
def collapse_edges(
|
|
4573
|
+
def collapse_edges(
|
|
4574
|
+
self,
|
|
4575
|
+
int num=1,
|
|
4576
|
+
int max_dimension = 0,
|
|
4577
|
+
bool progress=False,
|
|
4578
|
+
bool strong=True,
|
|
4579
|
+
bool full=False,
|
|
4580
|
+
bool ignore_warning=False,
|
|
4581
|
+
bool auto_clean=True,
|
|
4582
|
+
)->SimplexTreeMulti_Fi64:
|
|
4389
4583
|
"""Edge collapse for 1-critical 2-parameter clique complex (see https://arxiv.org/abs/2211.05574).
|
|
4390
4584
|
It uses the code from the github repository https://github.com/aj-alonso/filtration_domination .
|
|
4391
4585
|
|
|
@@ -4435,6 +4629,8 @@ cdef class SimplexTreeMulti_Fi64:
|
|
|
4435
4629
|
edges = _collapse_edge_list(edges, num=num, full=full, strong=strong, progress=progress)
|
|
4436
4630
|
# Retrieves the collapsed simplicial complex
|
|
4437
4631
|
self._reconstruct_from_edge_list(edges, swap=True, expand_dimension=max_dimension)
|
|
4632
|
+
if self.is_squeezed and auto_clean:
|
|
4633
|
+
self._clean_filtration_grid()
|
|
4438
4634
|
return self
|
|
4439
4635
|
|
|
4440
4636
|
@cython.inline
|
|
@@ -4653,11 +4849,26 @@ cdef class SimplexTreeMulti_Fi64:
|
|
|
4653
4849
|
out = self.get_ptr().get_filtration_values(degrees)
|
|
4654
4850
|
filtrations_values = [np.asarray(filtration) for filtration in out]
|
|
4655
4851
|
# Removes infs
|
|
4656
|
-
if inf_to_nan:
|
|
4852
|
+
if inf_to_nan and np.dtype(filtrations_values[0].dtype).kind == 'f':
|
|
4657
4853
|
for i,f in enumerate(filtrations_values):
|
|
4658
4854
|
filtrations_values[i][f == np.inf] = np.nan
|
|
4659
4855
|
filtrations_values[i][f == - np.inf] = np.nan
|
|
4660
4856
|
return filtrations_values
|
|
4857
|
+
def _clean_filtration_grid(self):
|
|
4858
|
+
"""
|
|
4859
|
+
Removes the values in filtration_grid that are not linked to any splx.
|
|
4860
|
+
"""
|
|
4861
|
+
if not self.is_squeezed:
|
|
4862
|
+
raise ValueError("No grid to clean.")
|
|
4863
|
+
F = self.filtration_grid
|
|
4864
|
+
self.filtration_grid=None
|
|
4865
|
+
cleaned_coordinates = compute_grid(self)
|
|
4866
|
+
new_st = self.grid_squeeze(cleaned_coordinates)
|
|
4867
|
+
|
|
4868
|
+
self.thisptr, new_st.thisptr = new_st.thisptr, self.thisptr
|
|
4869
|
+
self.filtration_grid = tuple(f[g] for f,g in zip(F,cleaned_coordinates))
|
|
4870
|
+
return self
|
|
4871
|
+
|
|
4661
4872
|
|
|
4662
4873
|
|
|
4663
4874
|
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]:
|
|
@@ -4693,8 +4904,16 @@ cdef class SimplexTreeMulti_Fi64:
|
|
|
4693
4904
|
return compute_grid(filtrations_values, resolution=resolution,strategy=grid_strategy,drop_quantiles=drop_quantiles)
|
|
4694
4905
|
|
|
4695
4906
|
|
|
4696
|
-
|
|
4697
|
-
|
|
4907
|
+
def grid_squeeze(
|
|
4908
|
+
self,
|
|
4909
|
+
filtration_grid:np.ndarray|list|None=None,
|
|
4910
|
+
bool coordinate_values=True,
|
|
4911
|
+
bool force=False,
|
|
4912
|
+
str strategy:_available_strategies = "exact",
|
|
4913
|
+
grid_strategy=None,
|
|
4914
|
+
bool inplace=False,
|
|
4915
|
+
**filtration_grid_kwargs
|
|
4916
|
+
)->SimplexTreeMulti_Fi32 | SimplexTreeMulti_Fi64:
|
|
4698
4917
|
"""
|
|
4699
4918
|
Fit the filtration of the simplextree to a grid.
|
|
4700
4919
|
|
|
@@ -4705,26 +4924,50 @@ cdef class SimplexTreeMulti_Fi64:
|
|
|
4705
4924
|
"""
|
|
4706
4925
|
if not force and self.is_squeezed:
|
|
4707
4926
|
raise Exception("SimplexTree already squeezed, use `force=True` if that's really what you want to do.")
|
|
4927
|
+
|
|
4928
|
+
if grid_strategy is not None:
|
|
4929
|
+
warn("`grid_strategy` is deprecated, use `strategy` instead.",DeprecationWarning)
|
|
4930
|
+
strategy=grid_strategy
|
|
4931
|
+
|
|
4932
|
+
if self.is_squeezed:
|
|
4933
|
+
warn("(copy warning) Squeezing an already squeezed slicer.")
|
|
4934
|
+
temp = self.unsqueeze()
|
|
4935
|
+
subgrid = compute_grid(self.filtration_grid, strategy=strategy, resolution=resolution)
|
|
4936
|
+
return temp.grid_squeeze(subgrid, coordinates=coordinates, inplace=inplace)
|
|
4937
|
+
|
|
4708
4938
|
#TODO : multi-critical
|
|
4709
4939
|
if filtration_grid is None:
|
|
4710
|
-
filtration_grid = self.get_filtration_grid(grid_strategy=
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4940
|
+
filtration_grid = self.get_filtration_grid(grid_strategy=strategy, **filtration_grid_kwargs)
|
|
4941
|
+
else:
|
|
4942
|
+
filtration_grid = sanitize_grid(filtration_grid)
|
|
4943
|
+
if len(filtration_grid) != self.num_parameters:
|
|
4944
|
+
raise ValueError(f"Invalid grid to squeeze onto. Got {len(filtration_grid)=} != {self.num_parameters=}.")
|
|
4945
|
+
api = api_from_tensor(filtration_grid[0])
|
|
4946
|
+
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
|
|
4714
4947
|
if coordinate_values and inplace:
|
|
4715
|
-
self.filtration_grid =
|
|
4948
|
+
self.filtration_grid = filtration_grid
|
|
4716
4949
|
if inplace or not coordinate_values:
|
|
4717
4950
|
self.get_ptr().squeeze_filtration_inplace(c_filtration_grid, coordinate_values)
|
|
4718
4951
|
else:
|
|
4719
4952
|
out = SimplexTreeMulti_Fi32(num_parameters=self.num_parameters)
|
|
4720
4953
|
self.get_ptr().squeeze_filtration(out.thisptr, c_filtration_grid)
|
|
4721
|
-
out.filtration_grid =
|
|
4954
|
+
out.filtration_grid = filtration_grid
|
|
4722
4955
|
return out
|
|
4723
4956
|
return self
|
|
4724
4957
|
|
|
4958
|
+
def unsqueeze(self, grid=None)->SimplexTreeMulti_Ff64:
|
|
4959
|
+
from multipers.grids import sanitize_grid
|
|
4960
|
+
grid = self.filtration_grid if grid is None else grid
|
|
4961
|
+
|
|
4962
|
+
cdef vector[vector[double]] cgrid = sanitize_grid(grid, numpyfy=True)
|
|
4963
|
+
new_slicer = SimplexTreeMulti_Ff64()
|
|
4964
|
+
new_slicer.get_ptr().unsqueeze_filtration(self.thisptr, cgrid)
|
|
4965
|
+
|
|
4966
|
+
return new_slicer
|
|
4967
|
+
|
|
4725
4968
|
@property
|
|
4726
4969
|
def is_squeezed(self)->bool:
|
|
4727
|
-
return self.num_vertices > 0 and len(self.filtration_grid)>0 and len(self.filtration_grid[0]) > 0
|
|
4970
|
+
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
|
|
4728
4971
|
|
|
4729
4972
|
@property
|
|
4730
4973
|
def dtype(self)->type:
|
|
@@ -5015,15 +5258,15 @@ cdef class SimplexTreeMulti_KFf32:
|
|
|
5015
5258
|
"""
|
|
5016
5259
|
cdef public intptr_t thisptr
|
|
5017
5260
|
|
|
5018
|
-
cdef public
|
|
5019
|
-
cdef public bool _is_function_simplextree
|
|
5261
|
+
cdef public object filtration_grid
|
|
5262
|
+
cdef public bool _is_function_simplextree # TODO : deprecate
|
|
5020
5263
|
# Get the pointer casted as it should be
|
|
5021
5264
|
cdef Simplex_tree_multi_interface[KFf32, float]* get_ptr(self) noexcept nogil:
|
|
5022
5265
|
return <Simplex_tree_multi_interface[KFf32, float]*>(self.thisptr)
|
|
5023
5266
|
|
|
5024
5267
|
# cdef Simplex_tree_persistence_interface * pcohptr
|
|
5025
5268
|
# Fake constructor that does nothing but documenting the constructor
|
|
5026
|
-
def __init__(self, other = None, num_parameters:int
|
|
5269
|
+
def __init__(self, other = None, num_parameters:int=-1,default_values=[], safe_conversion=False):
|
|
5027
5270
|
"""SimplexTreeMulti constructor.
|
|
5028
5271
|
|
|
5029
5272
|
:param other: If `other` is `None` (default value), an empty `SimplexTreeMulti` is created.
|
|
@@ -5048,7 +5291,7 @@ cdef class SimplexTreeMulti_KFf32:
|
|
|
5048
5291
|
def is_kcritical(self)->bool:
|
|
5049
5292
|
return True
|
|
5050
5293
|
# The real cython constructor
|
|
5051
|
-
def __cinit__(self, other = None, int num_parameters
|
|
5294
|
+
def __cinit__(self, other = None, int num_parameters=-1,
|
|
5052
5295
|
default_values=np.asarray([SimplexTreeMulti_KFf32.T_minus_inf()]), # I'm not sure why `[]` does not work. Cython bug ?
|
|
5053
5296
|
bool safe_conversion=False,
|
|
5054
5297
|
): #TODO doc
|
|
@@ -5061,10 +5304,13 @@ cdef class SimplexTreeMulti_KFf32:
|
|
|
5061
5304
|
if isinstance(other, SimplexTreeMulti_KFf32):
|
|
5062
5305
|
other_ptr = other.thisptr
|
|
5063
5306
|
self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[KFf32, float](dereference(<Simplex_tree_multi_interface[KFf32, float]*>other_ptr))) ## prevents calling destructor of other
|
|
5064
|
-
num_parameters
|
|
5307
|
+
if num_parameters <=0:
|
|
5308
|
+
num_parameters = other.num_parameters
|
|
5065
5309
|
self.filtration_grid = other.filtration_grid
|
|
5066
5310
|
elif isinstance(other, SimplexTree): # Constructs a SimplexTreeMulti from a SimplexTree
|
|
5067
5311
|
self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[KFf32, float]())
|
|
5312
|
+
if num_parameters <= 0:
|
|
5313
|
+
num_parameters = 1
|
|
5068
5314
|
if safe_conversion or SAFE_CONVERSION:
|
|
5069
5315
|
new_st_multi = _safe_simplextree_multify_KFf32(other, num_parameters = num_parameters, default_values=np.asarray(default_values))
|
|
5070
5316
|
self.thisptr, new_st_multi.thisptr = new_st_multi.thisptr, self.thisptr
|
|
@@ -5079,10 +5325,12 @@ cdef class SimplexTreeMulti_KFf32:
|
|
|
5079
5325
|
else:
|
|
5080
5326
|
raise TypeError("`other` argument requires to be of type `SimplexTree`, `SimplexTreeMulti`, or `None`.")
|
|
5081
5327
|
else:
|
|
5328
|
+
if num_parameters <=0:
|
|
5329
|
+
num_parameters = 2 # I don't know how dangerous this is, but this is mostly used.
|
|
5082
5330
|
self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[KFf32, float]())
|
|
5083
|
-
self.
|
|
5331
|
+
self.set_num_parameter(num_parameters)
|
|
5084
5332
|
self._is_function_simplextree = False
|
|
5085
|
-
self.filtration_grid=[
|
|
5333
|
+
self.filtration_grid=[]
|
|
5086
5334
|
|
|
5087
5335
|
def __dealloc__(self):
|
|
5088
5336
|
cdef Simplex_tree_multi_interface[KFf32,float]* ptr = self.get_ptr()
|
|
@@ -5092,6 +5340,9 @@ cdef class SimplexTreeMulti_KFf32:
|
|
|
5092
5340
|
|
|
5093
5341
|
def __repr__(self):
|
|
5094
5342
|
return f"SimplexTreeMulti[dtype={np.dtype(self.dtype).name},num_param={self.num_parameters},kcritical={self.is_kcritical},is_squeezed={self.is_squeezed},max_dim={self.dimension}]"
|
|
5343
|
+
def __len__(self):
|
|
5344
|
+
return self.num_simplices
|
|
5345
|
+
|
|
5095
5346
|
def __getstate__(self):
|
|
5096
5347
|
""":returns: Serialized (or flattened) SimplexTree data structure in order to pickle SimplexTree.
|
|
5097
5348
|
:rtype: numpy.array of shape (n,)
|
|
@@ -5747,11 +5998,26 @@ cdef class SimplexTreeMulti_KFf32:
|
|
|
5747
5998
|
out = self.get_ptr().get_filtration_values(degrees)
|
|
5748
5999
|
filtrations_values = [np.asarray(filtration) for filtration in out]
|
|
5749
6000
|
# Removes infs
|
|
5750
|
-
if inf_to_nan:
|
|
6001
|
+
if inf_to_nan and np.dtype(filtrations_values[0].dtype).kind == 'f':
|
|
5751
6002
|
for i,f in enumerate(filtrations_values):
|
|
5752
6003
|
filtrations_values[i][f == np.inf] = np.nan
|
|
5753
6004
|
filtrations_values[i][f == - np.inf] = np.nan
|
|
5754
6005
|
return filtrations_values
|
|
6006
|
+
def _clean_filtration_grid(self):
|
|
6007
|
+
"""
|
|
6008
|
+
Removes the values in filtration_grid that are not linked to any splx.
|
|
6009
|
+
"""
|
|
6010
|
+
if not self.is_squeezed:
|
|
6011
|
+
raise ValueError("No grid to clean.")
|
|
6012
|
+
F = self.filtration_grid
|
|
6013
|
+
self.filtration_grid=None
|
|
6014
|
+
cleaned_coordinates = compute_grid(self)
|
|
6015
|
+
new_st = self.grid_squeeze(cleaned_coordinates)
|
|
6016
|
+
|
|
6017
|
+
self.thisptr, new_st.thisptr = new_st.thisptr, self.thisptr
|
|
6018
|
+
self.filtration_grid = tuple(f[g] for f,g in zip(F,cleaned_coordinates))
|
|
6019
|
+
return self
|
|
6020
|
+
|
|
5755
6021
|
|
|
5756
6022
|
|
|
5757
6023
|
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]:
|
|
@@ -5787,8 +6053,16 @@ cdef class SimplexTreeMulti_KFf32:
|
|
|
5787
6053
|
return compute_grid(filtrations_values, resolution=resolution,strategy=grid_strategy,drop_quantiles=drop_quantiles)
|
|
5788
6054
|
|
|
5789
6055
|
|
|
5790
|
-
|
|
5791
|
-
|
|
6056
|
+
def grid_squeeze(
|
|
6057
|
+
self,
|
|
6058
|
+
filtration_grid:np.ndarray|list|None=None,
|
|
6059
|
+
bool coordinate_values=True,
|
|
6060
|
+
bool force=False,
|
|
6061
|
+
str strategy:_available_strategies = "exact",
|
|
6062
|
+
grid_strategy=None,
|
|
6063
|
+
bool inplace=False,
|
|
6064
|
+
**filtration_grid_kwargs
|
|
6065
|
+
)->SimplexTreeMulti_KFi32 | SimplexTreeMulti_KFf32:
|
|
5792
6066
|
"""
|
|
5793
6067
|
Fit the filtration of the simplextree to a grid.
|
|
5794
6068
|
|
|
@@ -5799,26 +6073,50 @@ cdef class SimplexTreeMulti_KFf32:
|
|
|
5799
6073
|
"""
|
|
5800
6074
|
if not force and self.is_squeezed:
|
|
5801
6075
|
raise Exception("SimplexTree already squeezed, use `force=True` if that's really what you want to do.")
|
|
6076
|
+
|
|
6077
|
+
if grid_strategy is not None:
|
|
6078
|
+
warn("`grid_strategy` is deprecated, use `strategy` instead.",DeprecationWarning)
|
|
6079
|
+
strategy=grid_strategy
|
|
6080
|
+
|
|
6081
|
+
if self.is_squeezed:
|
|
6082
|
+
warn("(copy warning) Squeezing an already squeezed slicer.")
|
|
6083
|
+
temp = self.unsqueeze()
|
|
6084
|
+
subgrid = compute_grid(self.filtration_grid, strategy=strategy, resolution=resolution)
|
|
6085
|
+
return temp.grid_squeeze(subgrid, coordinates=coordinates, inplace=inplace)
|
|
6086
|
+
|
|
5802
6087
|
#TODO : multi-critical
|
|
5803
6088
|
if filtration_grid is None:
|
|
5804
|
-
filtration_grid = self.get_filtration_grid(grid_strategy=
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
6089
|
+
filtration_grid = self.get_filtration_grid(grid_strategy=strategy, **filtration_grid_kwargs)
|
|
6090
|
+
else:
|
|
6091
|
+
filtration_grid = sanitize_grid(filtration_grid)
|
|
6092
|
+
if len(filtration_grid) != self.num_parameters:
|
|
6093
|
+
raise ValueError(f"Invalid grid to squeeze onto. Got {len(filtration_grid)=} != {self.num_parameters=}.")
|
|
6094
|
+
api = api_from_tensor(filtration_grid[0])
|
|
6095
|
+
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
|
|
5808
6096
|
if coordinate_values and inplace:
|
|
5809
|
-
self.filtration_grid =
|
|
6097
|
+
self.filtration_grid = filtration_grid
|
|
5810
6098
|
if inplace or not coordinate_values:
|
|
5811
6099
|
self.get_ptr().squeeze_filtration_inplace(c_filtration_grid, coordinate_values)
|
|
5812
6100
|
else:
|
|
5813
6101
|
out = SimplexTreeMulti_KFi32(num_parameters=self.num_parameters)
|
|
5814
6102
|
self.get_ptr().squeeze_filtration(out.thisptr, c_filtration_grid)
|
|
5815
|
-
out.filtration_grid =
|
|
6103
|
+
out.filtration_grid = filtration_grid
|
|
5816
6104
|
return out
|
|
5817
6105
|
return self
|
|
5818
6106
|
|
|
6107
|
+
def unsqueeze(self, grid=None)->SimplexTreeMulti_KFf64:
|
|
6108
|
+
from multipers.grids import sanitize_grid
|
|
6109
|
+
grid = self.filtration_grid if grid is None else grid
|
|
6110
|
+
|
|
6111
|
+
cdef vector[vector[double]] cgrid = sanitize_grid(grid, numpyfy=True)
|
|
6112
|
+
new_slicer = SimplexTreeMulti_KFf64()
|
|
6113
|
+
new_slicer.get_ptr().unsqueeze_filtration(self.thisptr, cgrid)
|
|
6114
|
+
|
|
6115
|
+
return new_slicer
|
|
6116
|
+
|
|
5819
6117
|
@property
|
|
5820
6118
|
def is_squeezed(self)->bool:
|
|
5821
|
-
return self.num_vertices > 0 and len(self.filtration_grid)>0 and len(self.filtration_grid[0]) > 0
|
|
6119
|
+
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
|
|
5822
6120
|
|
|
5823
6121
|
@property
|
|
5824
6122
|
def dtype(self)->type:
|
|
@@ -6111,15 +6409,15 @@ cdef class SimplexTreeMulti_Ff32:
|
|
|
6111
6409
|
"""
|
|
6112
6410
|
cdef public intptr_t thisptr
|
|
6113
6411
|
|
|
6114
|
-
cdef public
|
|
6115
|
-
cdef public bool _is_function_simplextree
|
|
6412
|
+
cdef public object filtration_grid
|
|
6413
|
+
cdef public bool _is_function_simplextree # TODO : deprecate
|
|
6116
6414
|
# Get the pointer casted as it should be
|
|
6117
6415
|
cdef Simplex_tree_multi_interface[Ff32, float]* get_ptr(self) noexcept nogil:
|
|
6118
6416
|
return <Simplex_tree_multi_interface[Ff32, float]*>(self.thisptr)
|
|
6119
6417
|
|
|
6120
6418
|
# cdef Simplex_tree_persistence_interface * pcohptr
|
|
6121
6419
|
# Fake constructor that does nothing but documenting the constructor
|
|
6122
|
-
def __init__(self, other = None, num_parameters:int
|
|
6420
|
+
def __init__(self, other = None, num_parameters:int=-1,default_values=[], safe_conversion=False):
|
|
6123
6421
|
"""SimplexTreeMulti constructor.
|
|
6124
6422
|
|
|
6125
6423
|
:param other: If `other` is `None` (default value), an empty `SimplexTreeMulti` is created.
|
|
@@ -6144,7 +6442,7 @@ cdef class SimplexTreeMulti_Ff32:
|
|
|
6144
6442
|
def is_kcritical(self)->bool:
|
|
6145
6443
|
return False
|
|
6146
6444
|
# The real cython constructor
|
|
6147
|
-
def __cinit__(self, other = None, int num_parameters
|
|
6445
|
+
def __cinit__(self, other = None, int num_parameters=-1,
|
|
6148
6446
|
default_values=np.asarray([SimplexTreeMulti_Ff32.T_minus_inf()]), # I'm not sure why `[]` does not work. Cython bug ?
|
|
6149
6447
|
bool safe_conversion=False,
|
|
6150
6448
|
): #TODO doc
|
|
@@ -6157,10 +6455,13 @@ cdef class SimplexTreeMulti_Ff32:
|
|
|
6157
6455
|
if isinstance(other, SimplexTreeMulti_Ff32):
|
|
6158
6456
|
other_ptr = other.thisptr
|
|
6159
6457
|
self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[Ff32, float](dereference(<Simplex_tree_multi_interface[Ff32, float]*>other_ptr))) ## prevents calling destructor of other
|
|
6160
|
-
num_parameters
|
|
6458
|
+
if num_parameters <=0:
|
|
6459
|
+
num_parameters = other.num_parameters
|
|
6161
6460
|
self.filtration_grid = other.filtration_grid
|
|
6162
6461
|
elif isinstance(other, SimplexTree): # Constructs a SimplexTreeMulti from a SimplexTree
|
|
6163
6462
|
self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[Ff32, float]())
|
|
6463
|
+
if num_parameters <= 0:
|
|
6464
|
+
num_parameters = 1
|
|
6164
6465
|
if safe_conversion or SAFE_CONVERSION:
|
|
6165
6466
|
new_st_multi = _safe_simplextree_multify_Ff32(other, num_parameters = num_parameters, default_values=np.asarray(default_values))
|
|
6166
6467
|
self.thisptr, new_st_multi.thisptr = new_st_multi.thisptr, self.thisptr
|
|
@@ -6175,10 +6476,12 @@ cdef class SimplexTreeMulti_Ff32:
|
|
|
6175
6476
|
else:
|
|
6176
6477
|
raise TypeError("`other` argument requires to be of type `SimplexTree`, `SimplexTreeMulti`, or `None`.")
|
|
6177
6478
|
else:
|
|
6479
|
+
if num_parameters <=0:
|
|
6480
|
+
num_parameters = 2 # I don't know how dangerous this is, but this is mostly used.
|
|
6178
6481
|
self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[Ff32, float]())
|
|
6179
|
-
self.
|
|
6482
|
+
self.set_num_parameter(num_parameters)
|
|
6180
6483
|
self._is_function_simplextree = False
|
|
6181
|
-
self.filtration_grid=[
|
|
6484
|
+
self.filtration_grid=[]
|
|
6182
6485
|
|
|
6183
6486
|
def __dealloc__(self):
|
|
6184
6487
|
cdef Simplex_tree_multi_interface[Ff32,float]* ptr = self.get_ptr()
|
|
@@ -6188,6 +6491,9 @@ cdef class SimplexTreeMulti_Ff32:
|
|
|
6188
6491
|
|
|
6189
6492
|
def __repr__(self):
|
|
6190
6493
|
return f"SimplexTreeMulti[dtype={np.dtype(self.dtype).name},num_param={self.num_parameters},kcritical={self.is_kcritical},is_squeezed={self.is_squeezed},max_dim={self.dimension}]"
|
|
6494
|
+
def __len__(self):
|
|
6495
|
+
return self.num_simplices
|
|
6496
|
+
|
|
6191
6497
|
def __getstate__(self):
|
|
6192
6498
|
""":returns: Serialized (or flattened) SimplexTree data structure in order to pickle SimplexTree.
|
|
6193
6499
|
:rtype: numpy.array of shape (n,)
|
|
@@ -6852,7 +7158,16 @@ cdef class SimplexTreeMulti_Ff32:
|
|
|
6852
7158
|
out = self.get_ptr().get_edge_list()
|
|
6853
7159
|
return out
|
|
6854
7160
|
|
|
6855
|
-
def collapse_edges(
|
|
7161
|
+
def collapse_edges(
|
|
7162
|
+
self,
|
|
7163
|
+
int num=1,
|
|
7164
|
+
int max_dimension = 0,
|
|
7165
|
+
bool progress=False,
|
|
7166
|
+
bool strong=True,
|
|
7167
|
+
bool full=False,
|
|
7168
|
+
bool ignore_warning=False,
|
|
7169
|
+
bool auto_clean=True,
|
|
7170
|
+
)->SimplexTreeMulti_Ff32:
|
|
6856
7171
|
"""Edge collapse for 1-critical 2-parameter clique complex (see https://arxiv.org/abs/2211.05574).
|
|
6857
7172
|
It uses the code from the github repository https://github.com/aj-alonso/filtration_domination .
|
|
6858
7173
|
|
|
@@ -6902,6 +7217,8 @@ cdef class SimplexTreeMulti_Ff32:
|
|
|
6902
7217
|
edges = _collapse_edge_list(edges, num=num, full=full, strong=strong, progress=progress)
|
|
6903
7218
|
# Retrieves the collapsed simplicial complex
|
|
6904
7219
|
self._reconstruct_from_edge_list(edges, swap=True, expand_dimension=max_dimension)
|
|
7220
|
+
if self.is_squeezed and auto_clean:
|
|
7221
|
+
self._clean_filtration_grid()
|
|
6905
7222
|
return self
|
|
6906
7223
|
|
|
6907
7224
|
@cython.inline
|
|
@@ -7120,11 +7437,26 @@ cdef class SimplexTreeMulti_Ff32:
|
|
|
7120
7437
|
out = self.get_ptr().get_filtration_values(degrees)
|
|
7121
7438
|
filtrations_values = [np.asarray(filtration) for filtration in out]
|
|
7122
7439
|
# Removes infs
|
|
7123
|
-
if inf_to_nan:
|
|
7440
|
+
if inf_to_nan and np.dtype(filtrations_values[0].dtype).kind == 'f':
|
|
7124
7441
|
for i,f in enumerate(filtrations_values):
|
|
7125
7442
|
filtrations_values[i][f == np.inf] = np.nan
|
|
7126
7443
|
filtrations_values[i][f == - np.inf] = np.nan
|
|
7127
7444
|
return filtrations_values
|
|
7445
|
+
def _clean_filtration_grid(self):
|
|
7446
|
+
"""
|
|
7447
|
+
Removes the values in filtration_grid that are not linked to any splx.
|
|
7448
|
+
"""
|
|
7449
|
+
if not self.is_squeezed:
|
|
7450
|
+
raise ValueError("No grid to clean.")
|
|
7451
|
+
F = self.filtration_grid
|
|
7452
|
+
self.filtration_grid=None
|
|
7453
|
+
cleaned_coordinates = compute_grid(self)
|
|
7454
|
+
new_st = self.grid_squeeze(cleaned_coordinates)
|
|
7455
|
+
|
|
7456
|
+
self.thisptr, new_st.thisptr = new_st.thisptr, self.thisptr
|
|
7457
|
+
self.filtration_grid = tuple(f[g] for f,g in zip(F,cleaned_coordinates))
|
|
7458
|
+
return self
|
|
7459
|
+
|
|
7128
7460
|
|
|
7129
7461
|
|
|
7130
7462
|
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]:
|
|
@@ -7160,8 +7492,16 @@ cdef class SimplexTreeMulti_Ff32:
|
|
|
7160
7492
|
return compute_grid(filtrations_values, resolution=resolution,strategy=grid_strategy,drop_quantiles=drop_quantiles)
|
|
7161
7493
|
|
|
7162
7494
|
|
|
7163
|
-
|
|
7164
|
-
|
|
7495
|
+
def grid_squeeze(
|
|
7496
|
+
self,
|
|
7497
|
+
filtration_grid:np.ndarray|list|None=None,
|
|
7498
|
+
bool coordinate_values=True,
|
|
7499
|
+
bool force=False,
|
|
7500
|
+
str strategy:_available_strategies = "exact",
|
|
7501
|
+
grid_strategy=None,
|
|
7502
|
+
bool inplace=False,
|
|
7503
|
+
**filtration_grid_kwargs
|
|
7504
|
+
)->SimplexTreeMulti_Fi32 | SimplexTreeMulti_Ff32:
|
|
7165
7505
|
"""
|
|
7166
7506
|
Fit the filtration of the simplextree to a grid.
|
|
7167
7507
|
|
|
@@ -7172,26 +7512,50 @@ cdef class SimplexTreeMulti_Ff32:
|
|
|
7172
7512
|
"""
|
|
7173
7513
|
if not force and self.is_squeezed:
|
|
7174
7514
|
raise Exception("SimplexTree already squeezed, use `force=True` if that's really what you want to do.")
|
|
7515
|
+
|
|
7516
|
+
if grid_strategy is not None:
|
|
7517
|
+
warn("`grid_strategy` is deprecated, use `strategy` instead.",DeprecationWarning)
|
|
7518
|
+
strategy=grid_strategy
|
|
7519
|
+
|
|
7520
|
+
if self.is_squeezed:
|
|
7521
|
+
warn("(copy warning) Squeezing an already squeezed slicer.")
|
|
7522
|
+
temp = self.unsqueeze()
|
|
7523
|
+
subgrid = compute_grid(self.filtration_grid, strategy=strategy, resolution=resolution)
|
|
7524
|
+
return temp.grid_squeeze(subgrid, coordinates=coordinates, inplace=inplace)
|
|
7525
|
+
|
|
7175
7526
|
#TODO : multi-critical
|
|
7176
7527
|
if filtration_grid is None:
|
|
7177
|
-
filtration_grid = self.get_filtration_grid(grid_strategy=
|
|
7178
|
-
|
|
7179
|
-
|
|
7180
|
-
|
|
7528
|
+
filtration_grid = self.get_filtration_grid(grid_strategy=strategy, **filtration_grid_kwargs)
|
|
7529
|
+
else:
|
|
7530
|
+
filtration_grid = sanitize_grid(filtration_grid)
|
|
7531
|
+
if len(filtration_grid) != self.num_parameters:
|
|
7532
|
+
raise ValueError(f"Invalid grid to squeeze onto. Got {len(filtration_grid)=} != {self.num_parameters=}.")
|
|
7533
|
+
api = api_from_tensor(filtration_grid[0])
|
|
7534
|
+
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
|
|
7181
7535
|
if coordinate_values and inplace:
|
|
7182
|
-
self.filtration_grid =
|
|
7536
|
+
self.filtration_grid = filtration_grid
|
|
7183
7537
|
if inplace or not coordinate_values:
|
|
7184
7538
|
self.get_ptr().squeeze_filtration_inplace(c_filtration_grid, coordinate_values)
|
|
7185
7539
|
else:
|
|
7186
7540
|
out = SimplexTreeMulti_Fi32(num_parameters=self.num_parameters)
|
|
7187
7541
|
self.get_ptr().squeeze_filtration(out.thisptr, c_filtration_grid)
|
|
7188
|
-
out.filtration_grid =
|
|
7542
|
+
out.filtration_grid = filtration_grid
|
|
7189
7543
|
return out
|
|
7190
7544
|
return self
|
|
7191
7545
|
|
|
7546
|
+
def unsqueeze(self, grid=None)->SimplexTreeMulti_Ff64:
|
|
7547
|
+
from multipers.grids import sanitize_grid
|
|
7548
|
+
grid = self.filtration_grid if grid is None else grid
|
|
7549
|
+
|
|
7550
|
+
cdef vector[vector[double]] cgrid = sanitize_grid(grid, numpyfy=True)
|
|
7551
|
+
new_slicer = SimplexTreeMulti_Ff64()
|
|
7552
|
+
new_slicer.get_ptr().unsqueeze_filtration(self.thisptr, cgrid)
|
|
7553
|
+
|
|
7554
|
+
return new_slicer
|
|
7555
|
+
|
|
7192
7556
|
@property
|
|
7193
7557
|
def is_squeezed(self)->bool:
|
|
7194
|
-
return self.num_vertices > 0 and len(self.filtration_grid)>0 and len(self.filtration_grid[0]) > 0
|
|
7558
|
+
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
|
|
7195
7559
|
|
|
7196
7560
|
@property
|
|
7197
7561
|
def dtype(self)->type:
|
|
@@ -7482,15 +7846,15 @@ cdef class SimplexTreeMulti_KFf64:
|
|
|
7482
7846
|
"""
|
|
7483
7847
|
cdef public intptr_t thisptr
|
|
7484
7848
|
|
|
7485
|
-
cdef public
|
|
7486
|
-
cdef public bool _is_function_simplextree
|
|
7849
|
+
cdef public object filtration_grid
|
|
7850
|
+
cdef public bool _is_function_simplextree # TODO : deprecate
|
|
7487
7851
|
# Get the pointer casted as it should be
|
|
7488
7852
|
cdef Simplex_tree_multi_interface[KFf64, double]* get_ptr(self) noexcept nogil:
|
|
7489
7853
|
return <Simplex_tree_multi_interface[KFf64, double]*>(self.thisptr)
|
|
7490
7854
|
|
|
7491
7855
|
# cdef Simplex_tree_persistence_interface * pcohptr
|
|
7492
7856
|
# Fake constructor that does nothing but documenting the constructor
|
|
7493
|
-
def __init__(self, other = None, num_parameters:int
|
|
7857
|
+
def __init__(self, other = None, num_parameters:int=-1,default_values=[], safe_conversion=False):
|
|
7494
7858
|
"""SimplexTreeMulti constructor.
|
|
7495
7859
|
|
|
7496
7860
|
:param other: If `other` is `None` (default value), an empty `SimplexTreeMulti` is created.
|
|
@@ -7515,7 +7879,7 @@ cdef class SimplexTreeMulti_KFf64:
|
|
|
7515
7879
|
def is_kcritical(self)->bool:
|
|
7516
7880
|
return True
|
|
7517
7881
|
# The real cython constructor
|
|
7518
|
-
def __cinit__(self, other = None, int num_parameters
|
|
7882
|
+
def __cinit__(self, other = None, int num_parameters=-1,
|
|
7519
7883
|
default_values=np.asarray([SimplexTreeMulti_KFf64.T_minus_inf()]), # I'm not sure why `[]` does not work. Cython bug ?
|
|
7520
7884
|
bool safe_conversion=False,
|
|
7521
7885
|
): #TODO doc
|
|
@@ -7528,10 +7892,13 @@ cdef class SimplexTreeMulti_KFf64:
|
|
|
7528
7892
|
if isinstance(other, SimplexTreeMulti_KFf64):
|
|
7529
7893
|
other_ptr = other.thisptr
|
|
7530
7894
|
self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[KFf64, double](dereference(<Simplex_tree_multi_interface[KFf64, double]*>other_ptr))) ## prevents calling destructor of other
|
|
7531
|
-
num_parameters
|
|
7895
|
+
if num_parameters <=0:
|
|
7896
|
+
num_parameters = other.num_parameters
|
|
7532
7897
|
self.filtration_grid = other.filtration_grid
|
|
7533
7898
|
elif isinstance(other, SimplexTree): # Constructs a SimplexTreeMulti from a SimplexTree
|
|
7534
7899
|
self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[KFf64, double]())
|
|
7900
|
+
if num_parameters <= 0:
|
|
7901
|
+
num_parameters = 1
|
|
7535
7902
|
if safe_conversion or SAFE_CONVERSION:
|
|
7536
7903
|
new_st_multi = _safe_simplextree_multify_KFf64(other, num_parameters = num_parameters, default_values=np.asarray(default_values))
|
|
7537
7904
|
self.thisptr, new_st_multi.thisptr = new_st_multi.thisptr, self.thisptr
|
|
@@ -7546,10 +7913,12 @@ cdef class SimplexTreeMulti_KFf64:
|
|
|
7546
7913
|
else:
|
|
7547
7914
|
raise TypeError("`other` argument requires to be of type `SimplexTree`, `SimplexTreeMulti`, or `None`.")
|
|
7548
7915
|
else:
|
|
7916
|
+
if num_parameters <=0:
|
|
7917
|
+
num_parameters = 2 # I don't know how dangerous this is, but this is mostly used.
|
|
7549
7918
|
self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[KFf64, double]())
|
|
7550
|
-
self.
|
|
7919
|
+
self.set_num_parameter(num_parameters)
|
|
7551
7920
|
self._is_function_simplextree = False
|
|
7552
|
-
self.filtration_grid=[
|
|
7921
|
+
self.filtration_grid=[]
|
|
7553
7922
|
|
|
7554
7923
|
def __dealloc__(self):
|
|
7555
7924
|
cdef Simplex_tree_multi_interface[KFf64,double]* ptr = self.get_ptr()
|
|
@@ -7559,6 +7928,9 @@ cdef class SimplexTreeMulti_KFf64:
|
|
|
7559
7928
|
|
|
7560
7929
|
def __repr__(self):
|
|
7561
7930
|
return f"SimplexTreeMulti[dtype={np.dtype(self.dtype).name},num_param={self.num_parameters},kcritical={self.is_kcritical},is_squeezed={self.is_squeezed},max_dim={self.dimension}]"
|
|
7931
|
+
def __len__(self):
|
|
7932
|
+
return self.num_simplices
|
|
7933
|
+
|
|
7562
7934
|
def __getstate__(self):
|
|
7563
7935
|
""":returns: Serialized (or flattened) SimplexTree data structure in order to pickle SimplexTree.
|
|
7564
7936
|
:rtype: numpy.array of shape (n,)
|
|
@@ -8214,11 +8586,26 @@ cdef class SimplexTreeMulti_KFf64:
|
|
|
8214
8586
|
out = self.get_ptr().get_filtration_values(degrees)
|
|
8215
8587
|
filtrations_values = [np.asarray(filtration) for filtration in out]
|
|
8216
8588
|
# Removes infs
|
|
8217
|
-
if inf_to_nan:
|
|
8589
|
+
if inf_to_nan and np.dtype(filtrations_values[0].dtype).kind == 'f':
|
|
8218
8590
|
for i,f in enumerate(filtrations_values):
|
|
8219
8591
|
filtrations_values[i][f == np.inf] = np.nan
|
|
8220
8592
|
filtrations_values[i][f == - np.inf] = np.nan
|
|
8221
8593
|
return filtrations_values
|
|
8594
|
+
def _clean_filtration_grid(self):
|
|
8595
|
+
"""
|
|
8596
|
+
Removes the values in filtration_grid that are not linked to any splx.
|
|
8597
|
+
"""
|
|
8598
|
+
if not self.is_squeezed:
|
|
8599
|
+
raise ValueError("No grid to clean.")
|
|
8600
|
+
F = self.filtration_grid
|
|
8601
|
+
self.filtration_grid=None
|
|
8602
|
+
cleaned_coordinates = compute_grid(self)
|
|
8603
|
+
new_st = self.grid_squeeze(cleaned_coordinates)
|
|
8604
|
+
|
|
8605
|
+
self.thisptr, new_st.thisptr = new_st.thisptr, self.thisptr
|
|
8606
|
+
self.filtration_grid = tuple(f[g] for f,g in zip(F,cleaned_coordinates))
|
|
8607
|
+
return self
|
|
8608
|
+
|
|
8222
8609
|
|
|
8223
8610
|
|
|
8224
8611
|
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]:
|
|
@@ -8254,8 +8641,16 @@ cdef class SimplexTreeMulti_KFf64:
|
|
|
8254
8641
|
return compute_grid(filtrations_values, resolution=resolution,strategy=grid_strategy,drop_quantiles=drop_quantiles)
|
|
8255
8642
|
|
|
8256
8643
|
|
|
8257
|
-
|
|
8258
|
-
|
|
8644
|
+
def grid_squeeze(
|
|
8645
|
+
self,
|
|
8646
|
+
filtration_grid:np.ndarray|list|None=None,
|
|
8647
|
+
bool coordinate_values=True,
|
|
8648
|
+
bool force=False,
|
|
8649
|
+
str strategy:_available_strategies = "exact",
|
|
8650
|
+
grid_strategy=None,
|
|
8651
|
+
bool inplace=False,
|
|
8652
|
+
**filtration_grid_kwargs
|
|
8653
|
+
)->SimplexTreeMulti_KFi32 | SimplexTreeMulti_KFf64:
|
|
8259
8654
|
"""
|
|
8260
8655
|
Fit the filtration of the simplextree to a grid.
|
|
8261
8656
|
|
|
@@ -8266,26 +8661,50 @@ cdef class SimplexTreeMulti_KFf64:
|
|
|
8266
8661
|
"""
|
|
8267
8662
|
if not force and self.is_squeezed:
|
|
8268
8663
|
raise Exception("SimplexTree already squeezed, use `force=True` if that's really what you want to do.")
|
|
8664
|
+
|
|
8665
|
+
if grid_strategy is not None:
|
|
8666
|
+
warn("`grid_strategy` is deprecated, use `strategy` instead.",DeprecationWarning)
|
|
8667
|
+
strategy=grid_strategy
|
|
8668
|
+
|
|
8669
|
+
if self.is_squeezed:
|
|
8670
|
+
warn("(copy warning) Squeezing an already squeezed slicer.")
|
|
8671
|
+
temp = self.unsqueeze()
|
|
8672
|
+
subgrid = compute_grid(self.filtration_grid, strategy=strategy, resolution=resolution)
|
|
8673
|
+
return temp.grid_squeeze(subgrid, coordinates=coordinates, inplace=inplace)
|
|
8674
|
+
|
|
8269
8675
|
#TODO : multi-critical
|
|
8270
8676
|
if filtration_grid is None:
|
|
8271
|
-
filtration_grid = self.get_filtration_grid(grid_strategy=
|
|
8272
|
-
|
|
8273
|
-
|
|
8274
|
-
|
|
8677
|
+
filtration_grid = self.get_filtration_grid(grid_strategy=strategy, **filtration_grid_kwargs)
|
|
8678
|
+
else:
|
|
8679
|
+
filtration_grid = sanitize_grid(filtration_grid)
|
|
8680
|
+
if len(filtration_grid) != self.num_parameters:
|
|
8681
|
+
raise ValueError(f"Invalid grid to squeeze onto. Got {len(filtration_grid)=} != {self.num_parameters=}.")
|
|
8682
|
+
api = api_from_tensor(filtration_grid[0])
|
|
8683
|
+
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
|
|
8275
8684
|
if coordinate_values and inplace:
|
|
8276
|
-
self.filtration_grid =
|
|
8685
|
+
self.filtration_grid = filtration_grid
|
|
8277
8686
|
if inplace or not coordinate_values:
|
|
8278
8687
|
self.get_ptr().squeeze_filtration_inplace(c_filtration_grid, coordinate_values)
|
|
8279
8688
|
else:
|
|
8280
8689
|
out = SimplexTreeMulti_KFi32(num_parameters=self.num_parameters)
|
|
8281
8690
|
self.get_ptr().squeeze_filtration(out.thisptr, c_filtration_grid)
|
|
8282
|
-
out.filtration_grid =
|
|
8691
|
+
out.filtration_grid = filtration_grid
|
|
8283
8692
|
return out
|
|
8284
8693
|
return self
|
|
8285
8694
|
|
|
8695
|
+
def unsqueeze(self, grid=None)->SimplexTreeMulti_KFf64:
|
|
8696
|
+
from multipers.grids import sanitize_grid
|
|
8697
|
+
grid = self.filtration_grid if grid is None else grid
|
|
8698
|
+
|
|
8699
|
+
cdef vector[vector[double]] cgrid = sanitize_grid(grid, numpyfy=True)
|
|
8700
|
+
new_slicer = SimplexTreeMulti_KFf64()
|
|
8701
|
+
new_slicer.get_ptr().unsqueeze_filtration(self.thisptr, cgrid)
|
|
8702
|
+
|
|
8703
|
+
return new_slicer
|
|
8704
|
+
|
|
8286
8705
|
@property
|
|
8287
8706
|
def is_squeezed(self)->bool:
|
|
8288
|
-
return self.num_vertices > 0 and len(self.filtration_grid)>0 and len(self.filtration_grid[0]) > 0
|
|
8707
|
+
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
|
|
8289
8708
|
|
|
8290
8709
|
@property
|
|
8291
8710
|
def dtype(self)->type:
|
|
@@ -8578,15 +8997,15 @@ cdef class SimplexTreeMulti_Ff64:
|
|
|
8578
8997
|
"""
|
|
8579
8998
|
cdef public intptr_t thisptr
|
|
8580
8999
|
|
|
8581
|
-
cdef public
|
|
8582
|
-
cdef public bool _is_function_simplextree
|
|
9000
|
+
cdef public object filtration_grid
|
|
9001
|
+
cdef public bool _is_function_simplextree # TODO : deprecate
|
|
8583
9002
|
# Get the pointer casted as it should be
|
|
8584
9003
|
cdef Simplex_tree_multi_interface[Ff64, double]* get_ptr(self) noexcept nogil:
|
|
8585
9004
|
return <Simplex_tree_multi_interface[Ff64, double]*>(self.thisptr)
|
|
8586
9005
|
|
|
8587
9006
|
# cdef Simplex_tree_persistence_interface * pcohptr
|
|
8588
9007
|
# Fake constructor that does nothing but documenting the constructor
|
|
8589
|
-
def __init__(self, other = None, num_parameters:int
|
|
9008
|
+
def __init__(self, other = None, num_parameters:int=-1,default_values=[], safe_conversion=False):
|
|
8590
9009
|
"""SimplexTreeMulti constructor.
|
|
8591
9010
|
|
|
8592
9011
|
:param other: If `other` is `None` (default value), an empty `SimplexTreeMulti` is created.
|
|
@@ -8611,7 +9030,7 @@ cdef class SimplexTreeMulti_Ff64:
|
|
|
8611
9030
|
def is_kcritical(self)->bool:
|
|
8612
9031
|
return False
|
|
8613
9032
|
# The real cython constructor
|
|
8614
|
-
def __cinit__(self, other = None, int num_parameters
|
|
9033
|
+
def __cinit__(self, other = None, int num_parameters=-1,
|
|
8615
9034
|
default_values=np.asarray([SimplexTreeMulti_Ff64.T_minus_inf()]), # I'm not sure why `[]` does not work. Cython bug ?
|
|
8616
9035
|
bool safe_conversion=False,
|
|
8617
9036
|
): #TODO doc
|
|
@@ -8624,10 +9043,13 @@ cdef class SimplexTreeMulti_Ff64:
|
|
|
8624
9043
|
if isinstance(other, SimplexTreeMulti_Ff64):
|
|
8625
9044
|
other_ptr = other.thisptr
|
|
8626
9045
|
self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[Ff64, double](dereference(<Simplex_tree_multi_interface[Ff64, double]*>other_ptr))) ## prevents calling destructor of other
|
|
8627
|
-
num_parameters
|
|
9046
|
+
if num_parameters <=0:
|
|
9047
|
+
num_parameters = other.num_parameters
|
|
8628
9048
|
self.filtration_grid = other.filtration_grid
|
|
8629
9049
|
elif isinstance(other, SimplexTree): # Constructs a SimplexTreeMulti from a SimplexTree
|
|
8630
9050
|
self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[Ff64, double]())
|
|
9051
|
+
if num_parameters <= 0:
|
|
9052
|
+
num_parameters = 1
|
|
8631
9053
|
if safe_conversion or SAFE_CONVERSION:
|
|
8632
9054
|
new_st_multi = _safe_simplextree_multify_Ff64(other, num_parameters = num_parameters, default_values=np.asarray(default_values))
|
|
8633
9055
|
self.thisptr, new_st_multi.thisptr = new_st_multi.thisptr, self.thisptr
|
|
@@ -8642,10 +9064,12 @@ cdef class SimplexTreeMulti_Ff64:
|
|
|
8642
9064
|
else:
|
|
8643
9065
|
raise TypeError("`other` argument requires to be of type `SimplexTree`, `SimplexTreeMulti`, or `None`.")
|
|
8644
9066
|
else:
|
|
9067
|
+
if num_parameters <=0:
|
|
9068
|
+
num_parameters = 2 # I don't know how dangerous this is, but this is mostly used.
|
|
8645
9069
|
self.thisptr = <intptr_t>(new Simplex_tree_multi_interface[Ff64, double]())
|
|
8646
|
-
self.
|
|
9070
|
+
self.set_num_parameter(num_parameters)
|
|
8647
9071
|
self._is_function_simplextree = False
|
|
8648
|
-
self.filtration_grid=[
|
|
9072
|
+
self.filtration_grid=[]
|
|
8649
9073
|
|
|
8650
9074
|
def __dealloc__(self):
|
|
8651
9075
|
cdef Simplex_tree_multi_interface[Ff64,double]* ptr = self.get_ptr()
|
|
@@ -8655,6 +9079,9 @@ cdef class SimplexTreeMulti_Ff64:
|
|
|
8655
9079
|
|
|
8656
9080
|
def __repr__(self):
|
|
8657
9081
|
return f"SimplexTreeMulti[dtype={np.dtype(self.dtype).name},num_param={self.num_parameters},kcritical={self.is_kcritical},is_squeezed={self.is_squeezed},max_dim={self.dimension}]"
|
|
9082
|
+
def __len__(self):
|
|
9083
|
+
return self.num_simplices
|
|
9084
|
+
|
|
8658
9085
|
def __getstate__(self):
|
|
8659
9086
|
""":returns: Serialized (or flattened) SimplexTree data structure in order to pickle SimplexTree.
|
|
8660
9087
|
:rtype: numpy.array of shape (n,)
|
|
@@ -9319,7 +9746,16 @@ cdef class SimplexTreeMulti_Ff64:
|
|
|
9319
9746
|
out = self.get_ptr().get_edge_list()
|
|
9320
9747
|
return out
|
|
9321
9748
|
|
|
9322
|
-
def collapse_edges(
|
|
9749
|
+
def collapse_edges(
|
|
9750
|
+
self,
|
|
9751
|
+
int num=1,
|
|
9752
|
+
int max_dimension = 0,
|
|
9753
|
+
bool progress=False,
|
|
9754
|
+
bool strong=True,
|
|
9755
|
+
bool full=False,
|
|
9756
|
+
bool ignore_warning=False,
|
|
9757
|
+
bool auto_clean=True,
|
|
9758
|
+
)->SimplexTreeMulti_Ff64:
|
|
9323
9759
|
"""Edge collapse for 1-critical 2-parameter clique complex (see https://arxiv.org/abs/2211.05574).
|
|
9324
9760
|
It uses the code from the github repository https://github.com/aj-alonso/filtration_domination .
|
|
9325
9761
|
|
|
@@ -9369,6 +9805,8 @@ cdef class SimplexTreeMulti_Ff64:
|
|
|
9369
9805
|
edges = _collapse_edge_list(edges, num=num, full=full, strong=strong, progress=progress)
|
|
9370
9806
|
# Retrieves the collapsed simplicial complex
|
|
9371
9807
|
self._reconstruct_from_edge_list(edges, swap=True, expand_dimension=max_dimension)
|
|
9808
|
+
if self.is_squeezed and auto_clean:
|
|
9809
|
+
self._clean_filtration_grid()
|
|
9372
9810
|
return self
|
|
9373
9811
|
|
|
9374
9812
|
@cython.inline
|
|
@@ -9587,11 +10025,26 @@ cdef class SimplexTreeMulti_Ff64:
|
|
|
9587
10025
|
out = self.get_ptr().get_filtration_values(degrees)
|
|
9588
10026
|
filtrations_values = [np.asarray(filtration) for filtration in out]
|
|
9589
10027
|
# Removes infs
|
|
9590
|
-
if inf_to_nan:
|
|
10028
|
+
if inf_to_nan and np.dtype(filtrations_values[0].dtype).kind == 'f':
|
|
9591
10029
|
for i,f in enumerate(filtrations_values):
|
|
9592
10030
|
filtrations_values[i][f == np.inf] = np.nan
|
|
9593
10031
|
filtrations_values[i][f == - np.inf] = np.nan
|
|
9594
10032
|
return filtrations_values
|
|
10033
|
+
def _clean_filtration_grid(self):
|
|
10034
|
+
"""
|
|
10035
|
+
Removes the values in filtration_grid that are not linked to any splx.
|
|
10036
|
+
"""
|
|
10037
|
+
if not self.is_squeezed:
|
|
10038
|
+
raise ValueError("No grid to clean.")
|
|
10039
|
+
F = self.filtration_grid
|
|
10040
|
+
self.filtration_grid=None
|
|
10041
|
+
cleaned_coordinates = compute_grid(self)
|
|
10042
|
+
new_st = self.grid_squeeze(cleaned_coordinates)
|
|
10043
|
+
|
|
10044
|
+
self.thisptr, new_st.thisptr = new_st.thisptr, self.thisptr
|
|
10045
|
+
self.filtration_grid = tuple(f[g] for f,g in zip(F,cleaned_coordinates))
|
|
10046
|
+
return self
|
|
10047
|
+
|
|
9595
10048
|
|
|
9596
10049
|
|
|
9597
10050
|
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]:
|
|
@@ -9627,8 +10080,16 @@ cdef class SimplexTreeMulti_Ff64:
|
|
|
9627
10080
|
return compute_grid(filtrations_values, resolution=resolution,strategy=grid_strategy,drop_quantiles=drop_quantiles)
|
|
9628
10081
|
|
|
9629
10082
|
|
|
9630
|
-
|
|
9631
|
-
|
|
10083
|
+
def grid_squeeze(
|
|
10084
|
+
self,
|
|
10085
|
+
filtration_grid:np.ndarray|list|None=None,
|
|
10086
|
+
bool coordinate_values=True,
|
|
10087
|
+
bool force=False,
|
|
10088
|
+
str strategy:_available_strategies = "exact",
|
|
10089
|
+
grid_strategy=None,
|
|
10090
|
+
bool inplace=False,
|
|
10091
|
+
**filtration_grid_kwargs
|
|
10092
|
+
)->SimplexTreeMulti_Fi32 | SimplexTreeMulti_Ff64:
|
|
9632
10093
|
"""
|
|
9633
10094
|
Fit the filtration of the simplextree to a grid.
|
|
9634
10095
|
|
|
@@ -9639,26 +10100,50 @@ cdef class SimplexTreeMulti_Ff64:
|
|
|
9639
10100
|
"""
|
|
9640
10101
|
if not force and self.is_squeezed:
|
|
9641
10102
|
raise Exception("SimplexTree already squeezed, use `force=True` if that's really what you want to do.")
|
|
10103
|
+
|
|
10104
|
+
if grid_strategy is not None:
|
|
10105
|
+
warn("`grid_strategy` is deprecated, use `strategy` instead.",DeprecationWarning)
|
|
10106
|
+
strategy=grid_strategy
|
|
10107
|
+
|
|
10108
|
+
if self.is_squeezed:
|
|
10109
|
+
warn("(copy warning) Squeezing an already squeezed slicer.")
|
|
10110
|
+
temp = self.unsqueeze()
|
|
10111
|
+
subgrid = compute_grid(self.filtration_grid, strategy=strategy, resolution=resolution)
|
|
10112
|
+
return temp.grid_squeeze(subgrid, coordinates=coordinates, inplace=inplace)
|
|
10113
|
+
|
|
9642
10114
|
#TODO : multi-critical
|
|
9643
10115
|
if filtration_grid is None:
|
|
9644
|
-
filtration_grid = self.get_filtration_grid(grid_strategy=
|
|
9645
|
-
|
|
9646
|
-
|
|
9647
|
-
|
|
10116
|
+
filtration_grid = self.get_filtration_grid(grid_strategy=strategy, **filtration_grid_kwargs)
|
|
10117
|
+
else:
|
|
10118
|
+
filtration_grid = sanitize_grid(filtration_grid)
|
|
10119
|
+
if len(filtration_grid) != self.num_parameters:
|
|
10120
|
+
raise ValueError(f"Invalid grid to squeeze onto. Got {len(filtration_grid)=} != {self.num_parameters=}.")
|
|
10121
|
+
api = api_from_tensor(filtration_grid[0])
|
|
10122
|
+
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
|
|
9648
10123
|
if coordinate_values and inplace:
|
|
9649
|
-
self.filtration_grid =
|
|
10124
|
+
self.filtration_grid = filtration_grid
|
|
9650
10125
|
if inplace or not coordinate_values:
|
|
9651
10126
|
self.get_ptr().squeeze_filtration_inplace(c_filtration_grid, coordinate_values)
|
|
9652
10127
|
else:
|
|
9653
10128
|
out = SimplexTreeMulti_Fi32(num_parameters=self.num_parameters)
|
|
9654
10129
|
self.get_ptr().squeeze_filtration(out.thisptr, c_filtration_grid)
|
|
9655
|
-
out.filtration_grid =
|
|
10130
|
+
out.filtration_grid = filtration_grid
|
|
9656
10131
|
return out
|
|
9657
10132
|
return self
|
|
9658
10133
|
|
|
10134
|
+
def unsqueeze(self, grid=None)->SimplexTreeMulti_Ff64:
|
|
10135
|
+
from multipers.grids import sanitize_grid
|
|
10136
|
+
grid = self.filtration_grid if grid is None else grid
|
|
10137
|
+
|
|
10138
|
+
cdef vector[vector[double]] cgrid = sanitize_grid(grid, numpyfy=True)
|
|
10139
|
+
new_slicer = SimplexTreeMulti_Ff64()
|
|
10140
|
+
new_slicer.get_ptr().unsqueeze_filtration(self.thisptr, cgrid)
|
|
10141
|
+
|
|
10142
|
+
return new_slicer
|
|
10143
|
+
|
|
9659
10144
|
@property
|
|
9660
10145
|
def is_squeezed(self)->bool:
|
|
9661
|
-
return self.num_vertices > 0 and len(self.filtration_grid)>0 and len(self.filtration_grid[0]) > 0
|
|
10146
|
+
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
|
|
9662
10147
|
|
|
9663
10148
|
@property
|
|
9664
10149
|
def dtype(self)->type:
|
|
@@ -9968,7 +10453,7 @@ def is_simplextree_multi(input)->bool:
|
|
|
9968
10453
|
|
|
9969
10454
|
|
|
9970
10455
|
|
|
9971
|
-
def SimplexTreeMulti(input=None, int num_parameters
|
|
10456
|
+
def SimplexTreeMulti(input=None, int num_parameters=-1, dtype:type = np.float64, bool kcritical = False,**kwargs) -> SimplexTreeMulti_type:
|
|
9972
10457
|
"""SimplexTreeMulti constructor.
|
|
9973
10458
|
|
|
9974
10459
|
:param other: If `other` is `None` (default value), an empty `SimplexTreeMulti` is created.
|
|
@@ -10179,7 +10664,8 @@ def _euler_signed_measure(simplextree, mass_default=None, bool verbose=False):
|
|
|
10179
10664
|
`[signed_measure_of_degree for degree in degrees]`
|
|
10180
10665
|
with `signed_measure_of_degree` of the form `(dirac location, dirac weights)`.
|
|
10181
10666
|
"""
|
|
10182
|
-
|
|
10667
|
+
if not simplextree.is_squeezed:
|
|
10668
|
+
raise ValueError("Squeeze grid first.")
|
|
10183
10669
|
cdef bool zero_pad = mass_default is not None
|
|
10184
10670
|
# assert simplextree.num_parameters == 2
|
|
10185
10671
|
grid_shape = np.array([len(f) for f in simplextree.filtration_grid])
|
|
@@ -10297,9 +10783,9 @@ def _rank_signed_measure(simplextree, vector[indices_type] degrees, mass_default
|
|
|
10297
10783
|
else:
|
|
10298
10784
|
mass_default = np.asarray(mass_default)
|
|
10299
10785
|
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,)"
|
|
10300
|
-
if zero_pad:
|
|
10301
|
-
|
|
10302
|
-
|
|
10786
|
+
# if zero_pad:
|
|
10787
|
+
# for i, _ in enumerate(grid_shape):
|
|
10788
|
+
# grid_shape[i] += 1 # adds a 0
|
|
10303
10789
|
# grid_conversion = tuple(np.concatenate([f, [mass_default[i]]]) for i,f in enumerate(grid_conversion))
|
|
10304
10790
|
|
|
10305
10791
|
assert len(grid_shape) == simplextree.num_parameters, "Grid shape size has to be the number of parameters."
|