pytme 0.2.0b0__cp311-cp311-macosx_14_0_arm64.whl → 0.2.2__cp311-cp311-macosx_14_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. pytme-0.2.2.data/scripts/match_template.py +1187 -0
  2. {pytme-0.2.0b0.data → pytme-0.2.2.data}/scripts/postprocess.py +170 -71
  3. {pytme-0.2.0b0.data → pytme-0.2.2.data}/scripts/preprocessor_gui.py +179 -86
  4. pytme-0.2.2.dist-info/METADATA +91 -0
  5. pytme-0.2.2.dist-info/RECORD +74 -0
  6. {pytme-0.2.0b0.dist-info → pytme-0.2.2.dist-info}/WHEEL +1 -1
  7. scripts/extract_candidates.py +126 -87
  8. scripts/match_template.py +596 -209
  9. scripts/match_template_filters.py +571 -223
  10. scripts/postprocess.py +170 -71
  11. scripts/preprocessor_gui.py +179 -86
  12. scripts/refine_matches.py +567 -159
  13. tme/__init__.py +0 -1
  14. tme/__version__.py +1 -1
  15. tme/analyzer.py +627 -855
  16. tme/backends/__init__.py +41 -11
  17. tme/backends/_jax_utils.py +185 -0
  18. tme/backends/cupy_backend.py +120 -225
  19. tme/backends/jax_backend.py +282 -0
  20. tme/backends/matching_backend.py +464 -388
  21. tme/backends/mlx_backend.py +45 -68
  22. tme/backends/npfftw_backend.py +256 -514
  23. tme/backends/pytorch_backend.py +41 -154
  24. tme/density.py +312 -421
  25. tme/extensions.cpython-311-darwin.so +0 -0
  26. tme/matching_data.py +366 -303
  27. tme/matching_exhaustive.py +279 -1521
  28. tme/matching_optimization.py +234 -129
  29. tme/matching_scores.py +884 -0
  30. tme/matching_utils.py +281 -387
  31. tme/memory.py +377 -0
  32. tme/orientations.py +226 -66
  33. tme/parser.py +3 -4
  34. tme/preprocessing/__init__.py +2 -0
  35. tme/preprocessing/_utils.py +217 -0
  36. tme/preprocessing/composable_filter.py +31 -0
  37. tme/preprocessing/compose.py +55 -0
  38. tme/preprocessing/frequency_filters.py +388 -0
  39. tme/preprocessing/tilt_series.py +1011 -0
  40. tme/preprocessor.py +574 -530
  41. tme/structure.py +495 -189
  42. tme/types.py +5 -3
  43. pytme-0.2.0b0.data/scripts/match_template.py +0 -800
  44. pytme-0.2.0b0.dist-info/METADATA +0 -73
  45. pytme-0.2.0b0.dist-info/RECORD +0 -66
  46. tme/helpers.py +0 -881
  47. tme/matching_constrained.py +0 -195
  48. {pytme-0.2.0b0.data → pytme-0.2.2.data}/scripts/estimate_ram_usage.py +0 -0
  49. {pytme-0.2.0b0.data → pytme-0.2.2.data}/scripts/preprocess.py +0 -0
  50. {pytme-0.2.0b0.dist-info → pytme-0.2.2.dist-info}/LICENSE +0 -0
  51. {pytme-0.2.0b0.dist-info → pytme-0.2.2.dist-info}/entry_points.txt +0 -0
  52. {pytme-0.2.0b0.dist-info → pytme-0.2.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,31 @@
1
+ """ Defines a specification for filters that can be used with
2
+ :py:class:`tme.preprocessing.compose.Compose`.
3
+
4
+ Copyright (c) 2024 European Molecular Biology Laboratory
5
+
6
+ Author: Valentin Maurer <valentin.maurer@embl-hamburg.de>
7
+ """
8
+ from typing import Dict
9
+ from abc import ABC, abstractmethod
10
+
11
+
12
+ class ComposableFilter(ABC):
13
+ """
14
+ Strategy class for composable filters.
15
+ """
16
+
17
+ @abstractmethod
18
+ def __call__(self, *args, **kwargs) -> Dict:
19
+ """
20
+ Parameters:
21
+ -----------
22
+ *args : tuple
23
+ Variable length argument list.
24
+ **kwargs : dict
25
+ Arbitrary keyword arguments.
26
+
27
+ Returns:
28
+ --------
29
+ Dict
30
+ A dictionary representing the result of the filtering operation.
31
+ """
@@ -0,0 +1,55 @@
1
+ """ Combine filters using an interface analogous to pytorch's Compose.
2
+
3
+ Copyright (c) 2024 European Molecular Biology Laboratory
4
+
5
+ Author: Valentin Maurer <valentin.maurer@embl-hamburg.de>
6
+ """
7
+
8
+ from typing import Tuple, Dict
9
+
10
+ from tme.backends import backend as be
11
+
12
+
13
+ class Compose:
14
+ """
15
+ Compose a series of transformations.
16
+
17
+ This class allows composing multiple transformations together. Each transformation
18
+ is expected to be a callable that accepts keyword arguments and returns metadata.
19
+
20
+ Parameters:
21
+ -----------
22
+ transforms : Tuple[object]
23
+ A tuple containing transformation objects.
24
+
25
+ Returns:
26
+ --------
27
+ Dict
28
+ Metadata resulting from the composed transformations.
29
+
30
+ """
31
+
32
+ def __init__(self, transforms: Tuple[object]):
33
+ self.transforms = transforms
34
+
35
+ def __call__(self, **kwargs: Dict) -> Dict:
36
+ meta = {}
37
+ if not len(self.transforms):
38
+ return meta
39
+
40
+ meta = self.transforms[0](**kwargs)
41
+ for transform in self.transforms[1:]:
42
+ kwargs.update(meta)
43
+ ret = transform(**kwargs)
44
+
45
+ if "data" not in ret:
46
+ continue
47
+
48
+ if ret.get("is_multiplicative_filter", False):
49
+ prev_data = meta.pop("data")
50
+ ret["data"] = be.multiply(ret["data"], prev_data, out=ret["data"])
51
+ ret["merge"], prev_data = None, None
52
+
53
+ meta = ret
54
+
55
+ return meta
@@ -0,0 +1,388 @@
1
+ """ Defines Fourier frequency filters.
2
+
3
+ Copyright (c) 2024 European Molecular Biology Laboratory
4
+
5
+ Author: Valentin Maurer <valentin.maurer@embl-hamburg.de>
6
+ """
7
+ from math import log, sqrt
8
+ from typing import Tuple, Dict
9
+
10
+ import numpy as np
11
+ from scipy.ndimage import mean as ndimean
12
+ from scipy.ndimage import map_coordinates
13
+
14
+ from ..types import BackendArray
15
+ from ..backends import backend as be
16
+ from ._utils import fftfreqn, crop_real_fourier, shift_fourier, compute_fourier_shape
17
+
18
+
19
+ class BandPassFilter:
20
+ """
21
+ This class provides methods to generate bandpass filters in Fourier space,
22
+ either by directly specifying the frequency cutoffs (discrete_bandpass) or
23
+ by using Gaussian functions (gaussian_bandpass).
24
+
25
+ Parameters:
26
+ -----------
27
+ lowpass : float, optional
28
+ The lowpass cutoff, defaults to None.
29
+ highpass : float, optional
30
+ The highpass cutoff, defaults to None.
31
+ sampling_rate : Tuple[float], optional
32
+ The sampling r_position_to_molmapate in Fourier space, defaults to 1.
33
+ use_gaussian : bool, optional
34
+ Whether to use Gaussian bandpass filter, defaults to True.
35
+ return_real_fourier : bool, optional
36
+ Whether to return only the real Fourier space, defaults to False.
37
+ shape_is_real_fourier : bool, optional
38
+ Whether the shape represents the real Fourier space, defaults to False.
39
+ """
40
+
41
+ def __init__(
42
+ self,
43
+ lowpass: float = None,
44
+ highpass: float = None,
45
+ sampling_rate: Tuple[float] = 1,
46
+ use_gaussian: bool = True,
47
+ return_real_fourier: bool = False,
48
+ shape_is_real_fourier: bool = False,
49
+ ):
50
+ self.lowpass = lowpass
51
+ self.highpass = highpass
52
+ self.use_gaussian = use_gaussian
53
+ self.return_real_fourier = return_real_fourier
54
+ self.shape_is_real_fourier = shape_is_real_fourier
55
+ self.sampling_rate = sampling_rate
56
+
57
+ @staticmethod
58
+ def discrete_bandpass(
59
+ shape: Tuple[int],
60
+ lowpass: float,
61
+ highpass: float,
62
+ sampling_rate: Tuple[float],
63
+ return_real_fourier: bool = False,
64
+ shape_is_real_fourier: bool = False,
65
+ **kwargs,
66
+ ) -> BackendArray:
67
+ """
68
+ Generate a bandpass filter using discrete frequency cutoffs.
69
+
70
+ Parameters:
71
+ -----------
72
+ shape : tuple of int
73
+ The shape of the bandpass filter.
74
+ lowpass : float
75
+ The lowpass cutoff in units of sampling rate.
76
+ highpass : float
77
+ The highpass cutoff in units of sampling rate.
78
+ return_real_fourier : bool, optional
79
+ Whether to return only the real Fourier space, defaults to False.
80
+ sampling_rate : float
81
+ The sampling rate in Fourier space.
82
+ shape_is_real_fourier : bool, optional
83
+ Whether the shape represents the real Fourier space, defaults to False.
84
+ **kwargs : dict
85
+ Additional keyword arguments.
86
+
87
+ Returns:
88
+ --------
89
+ BackendArray
90
+ The bandpass filter in Fourier space.
91
+ """
92
+ if shape_is_real_fourier:
93
+ return_real_fourier = False
94
+
95
+ grid = fftfreqn(
96
+ shape=shape,
97
+ sampling_rate=0.5,
98
+ shape_is_real_fourier=shape_is_real_fourier,
99
+ compute_euclidean_norm=True,
100
+ )
101
+
102
+ lowpass = 0 if lowpass is None else lowpass
103
+ highpass = 1e10 if highpass is None else highpass
104
+
105
+ highcut = grid.max()
106
+ if lowpass > 0:
107
+ highcut = np.max(2 * sampling_rate / lowpass)
108
+ lowcut = np.max(2 * sampling_rate / highpass)
109
+
110
+ bandpass_filter = ((grid <= highcut) & (grid >= lowcut)) * 1.0
111
+
112
+ bandpass_filter = shift_fourier(
113
+ data=bandpass_filter, shape_is_real_fourier=shape_is_real_fourier
114
+ )
115
+
116
+ if return_real_fourier:
117
+ bandpass_filter = crop_real_fourier(bandpass_filter)
118
+
119
+ return bandpass_filter
120
+
121
+ @staticmethod
122
+ def gaussian_bandpass(
123
+ shape: Tuple[int],
124
+ lowpass: float,
125
+ highpass: float,
126
+ sampling_rate: float,
127
+ return_real_fourier: bool = False,
128
+ shape_is_real_fourier: bool = False,
129
+ **kwargs,
130
+ ) -> BackendArray:
131
+ """
132
+ Generate a bandpass filter using Gaussian functions.
133
+
134
+ Parameters:
135
+ -----------
136
+ shape : tuple of int
137
+ The shape of the bandpass filter.
138
+ lowpass : float
139
+ The lowpass cutoff in units of sampling rate.
140
+ highpass : float
141
+ The highpass cutoff in units of sampling rate.
142
+ sampling_rate : float
143
+ The sampling rate in Fourier space.
144
+ return_real_fourier : bool, optional
145
+ Whether to return only the real Fourier space, defaults to False.
146
+ shape_is_real_fourier : bool, optional
147
+ Whether the shape represents the real Fourier space, defaults to False.
148
+ **kwargs : dict
149
+ Additional keyword arguments.
150
+
151
+ Returns:
152
+ --------
153
+ BackendArray
154
+ The bandpass filter in Fourier space.
155
+ """
156
+ if shape_is_real_fourier:
157
+ return_real_fourier = False
158
+
159
+ grid = fftfreqn(
160
+ shape=shape,
161
+ sampling_rate=0.5,
162
+ shape_is_real_fourier=shape_is_real_fourier,
163
+ compute_euclidean_norm=True,
164
+ )
165
+ grid = be.to_backend_array(grid)
166
+ grid = -be.square(grid)
167
+
168
+ lowpass_filter, highpass_filter = 1, 1
169
+ norm = float(sqrt(2 * log(2)))
170
+ upper_sampling = float(
171
+ be.max(be.multiply(2, be.to_backend_array(sampling_rate)))
172
+ )
173
+
174
+ if lowpass is not None:
175
+ lowpass = float(lowpass)
176
+ lowpass = be.maximum(lowpass, be.eps(be._float_dtype))
177
+ if highpass is not None:
178
+ highpass = float(highpass)
179
+ highpass = be.maximum(highpass, be.eps(be._float_dtype))
180
+
181
+ if lowpass is not None:
182
+ lowpass = upper_sampling / (lowpass * norm)
183
+ lowpass = be.multiply(2, be.square(lowpass))
184
+ lowpass_filter = be.exp(be.divide(grid, lowpass))
185
+ if highpass is not None:
186
+ highpass = upper_sampling / (highpass * norm)
187
+ highpass = be.multiply(2, be.square(highpass))
188
+ highpass_filter = 1 - be.exp(be.divide(grid, highpass))
189
+
190
+ bandpass_filter = be.multiply(lowpass_filter, highpass_filter)
191
+ bandpass_filter = shift_fourier(
192
+ data=bandpass_filter, shape_is_real_fourier=shape_is_real_fourier
193
+ )
194
+
195
+ if return_real_fourier:
196
+ bandpass_filter = crop_real_fourier(bandpass_filter)
197
+
198
+ return bandpass_filter
199
+
200
+ def __call__(self, **kwargs):
201
+ func_args = vars(self)
202
+ func_args.update(kwargs)
203
+
204
+ func = self.discrete_bandpass
205
+ if func_args.get("use_gaussian"):
206
+ func = self.gaussian_bandpass
207
+
208
+ mask = func(**func_args)
209
+
210
+ return {
211
+ "data": be.to_backend_array(mask),
212
+ "sampling_rate": func_args.get("sampling_rate", 1),
213
+ "is_multiplicative_filter": True,
214
+ }
215
+
216
+
217
+ class LinearWhiteningFilter:
218
+ """
219
+ This class provides methods to compute the spectrum of the input data and
220
+ apply linear whitening to the Fourier coefficients.
221
+
222
+ Parameters:
223
+ -----------
224
+ **kwargs : Dict, optional
225
+ Additional keyword arguments.
226
+
227
+
228
+ References
229
+ ----------
230
+ .. [1] de Teresa-Trueba, I.; Goetz, S. K.; Mattausch, A.; Stojanovska, F.; Zimmerli, C. E.;
231
+ Toro-Nahuelpan, M.; Cheng, D. W. C.; Tollervey, F.; Pape, C.; Beck, M.; Diz-Munoz,
232
+ A.; Kreshuk, A.; Mahamid, J.; Zaugg, J. B. Nat. Methods 2023, 20, 284–294.
233
+ .. [2] M. L. Chaillet, G. van der Schot, I. Gubins, S. Roet,
234
+ R. C. Veltkamp, and F. Förster, Int. J. Mol. Sci. 24,
235
+ 13375 (2023)
236
+ """
237
+
238
+ def __init__(self, **kwargs):
239
+ pass
240
+
241
+ @staticmethod
242
+ def _compute_spectrum(
243
+ data_rfft: BackendArray, n_bins: int = None, batch_dimension: int = None
244
+ ) -> Tuple[BackendArray, BackendArray]:
245
+ """
246
+ Compute the spectrum of the input data.
247
+
248
+ Parameters:
249
+ -----------
250
+ data_rfft : BackendArray
251
+ The Fourier transform of the input data.
252
+ n_bins : int, optional
253
+ The number of bins for computing the spectrum, defaults to None.
254
+ batch_dimension : int, optional
255
+ Batch dimension to average over.
256
+
257
+ Returns:
258
+ --------
259
+ bins : BackendArray
260
+ Array containing the bin indices for the spectrum.
261
+ radial_averages : BackendArray
262
+ Array containing the radial averages of the spectrum.
263
+ """
264
+ shape = tuple(x for i, x in enumerate(data_rfft.shape) if i != batch_dimension)
265
+
266
+ max_bins = max(max(shape[:-1]) // 2 + 1, shape[-1])
267
+ n_bins = max_bins if n_bins is None else n_bins
268
+ n_bins = int(min(n_bins, max_bins))
269
+
270
+ bins = fftfreqn(
271
+ shape=shape,
272
+ sampling_rate=0.5,
273
+ shape_is_real_fourier=True,
274
+ compute_euclidean_norm=True,
275
+ )
276
+ bins = be.to_numpy_array(bins)
277
+
278
+ # Implicit lowpass to nyquist
279
+ bins = np.floor(bins * (n_bins - 1) + 0.5).astype(int)
280
+ fft_shift_axes = tuple(
281
+ i for i in range(data_rfft.ndim - 1) if i != batch_dimension
282
+ )
283
+ fourier_spectrum = np.fft.fftshift(data_rfft, axes=fft_shift_axes)
284
+ fourier_spectrum = np.abs(fourier_spectrum)
285
+ np.square(fourier_spectrum, out=fourier_spectrum)
286
+
287
+ radial_averages = ndimean(
288
+ fourier_spectrum, labels=bins, index=np.arange(n_bins)
289
+ )
290
+ np.sqrt(radial_averages, out=radial_averages)
291
+ np.reciprocal(radial_averages, out=radial_averages)
292
+ np.divide(radial_averages, radial_averages.max(), out=radial_averages)
293
+
294
+ return bins, radial_averages
295
+
296
+ @staticmethod
297
+ def _interpolate_spectrum(
298
+ spectrum: BackendArray,
299
+ shape: Tuple[int],
300
+ shape_is_real_fourier: bool = True,
301
+ order: int = 1,
302
+ ) -> BackendArray:
303
+ """
304
+ References
305
+ ----------
306
+ .. [1] M. L. Chaillet, G. van der Schot, I. Gubins, S. Roet,
307
+ R. C. Veltkamp, and F. Förster, Int. J. Mol. Sci. 24,
308
+ 13375 (2023)
309
+ """
310
+ grid = fftfreqn(
311
+ shape=shape,
312
+ sampling_rate=0.5,
313
+ shape_is_real_fourier=shape_is_real_fourier,
314
+ compute_euclidean_norm=True,
315
+ )
316
+ grid = be.to_numpy_array(grid)
317
+ np.multiply(grid, (spectrum.shape[0] - 1), out=grid) + 0.5
318
+ spectrum = map_coordinates(spectrum, grid.reshape(1, -1), order=order)
319
+ return spectrum.reshape(grid.shape)
320
+
321
+ def __call__(
322
+ self,
323
+ data: BackendArray = None,
324
+ data_rfft: BackendArray = None,
325
+ n_bins: int = None,
326
+ batch_dimension: int = None,
327
+ order: int = 1,
328
+ **kwargs: Dict,
329
+ ) -> Dict:
330
+ """
331
+ Apply linear whitening to the data and return the result.
332
+
333
+ Parameters:
334
+ -----------
335
+ data : BackendArray, optional
336
+ The input data, defaults to None.
337
+ data_rfft : BackendArray, optional
338
+ The Fourier transform of the input data, defaults to None.
339
+ n_bins : int, optional
340
+ The number of bins for computing the spectrum, defaults to None.
341
+ batch_dimension : int, optional
342
+ Batch dimension to average over.
343
+ order : int, optional
344
+ Interpolation order to use.
345
+ **kwargs : Dict
346
+ Additional keyword arguments.
347
+
348
+ Returns:
349
+ --------
350
+ Dict
351
+ Filter data and associated parameters.
352
+ """
353
+ if data_rfft is None:
354
+ data_rfft = np.fft.rfftn(be.to_numpy_array(data))
355
+
356
+ data_rfft = be.to_numpy_array(data_rfft)
357
+
358
+ bins, radial_averages = self._compute_spectrum(
359
+ data_rfft, n_bins, batch_dimension
360
+ )
361
+
362
+ if order is None:
363
+ cutoff = bins < radial_averages.size
364
+ filter_mask = np.zeros(bins.shape, radial_averages.dtype)
365
+ filter_mask[cutoff] = radial_averages[bins[cutoff]]
366
+ else:
367
+ shape = bins.shape
368
+ if kwargs.get("shape", False):
369
+ shape = compute_fourier_shape(
370
+ shape=kwargs.get("shape"),
371
+ shape_is_real_fourier=kwargs.get("shape_is_real_fourier", False),
372
+ )
373
+
374
+ filter_mask = self._interpolate_spectrum(
375
+ spectrum=radial_averages,
376
+ shape=shape,
377
+ shape_is_real_fourier=True,
378
+ )
379
+
380
+ filter_mask = np.fft.ifftshift(
381
+ filter_mask,
382
+ axes=tuple(i for i in range(data_rfft.ndim - 1) if i != batch_dimension),
383
+ )
384
+
385
+ return {
386
+ "data": be.to_backend_array(filter_mask),
387
+ "is_multiplicative_filter": True,
388
+ }