pytme 0.2.0__cp311-cp311-macosx_14_0_arm64.whl → 0.2.1__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 (40) hide show
  1. {pytme-0.2.0.data → pytme-0.2.1.data}/scripts/match_template.py +183 -69
  2. {pytme-0.2.0.data → pytme-0.2.1.data}/scripts/postprocess.py +107 -49
  3. {pytme-0.2.0.data → pytme-0.2.1.data}/scripts/preprocessor_gui.py +4 -1
  4. {pytme-0.2.0.dist-info → pytme-0.2.1.dist-info}/METADATA +1 -1
  5. pytme-0.2.1.dist-info/RECORD +73 -0
  6. scripts/extract_candidates.py +117 -85
  7. scripts/match_template.py +183 -69
  8. scripts/match_template_filters.py +193 -71
  9. scripts/postprocess.py +107 -49
  10. scripts/preprocessor_gui.py +4 -1
  11. scripts/refine_matches.py +364 -160
  12. tme/__version__.py +1 -1
  13. tme/analyzer.py +259 -117
  14. tme/backends/__init__.py +1 -0
  15. tme/backends/cupy_backend.py +20 -13
  16. tme/backends/jax_backend.py +218 -0
  17. tme/backends/matching_backend.py +25 -10
  18. tme/backends/mlx_backend.py +13 -9
  19. tme/backends/npfftw_backend.py +20 -8
  20. tme/backends/pytorch_backend.py +20 -9
  21. tme/density.py +79 -60
  22. tme/extensions.cpython-311-darwin.so +0 -0
  23. tme/matching_data.py +85 -61
  24. tme/matching_exhaustive.py +222 -129
  25. tme/matching_optimization.py +117 -76
  26. tme/orientations.py +175 -55
  27. tme/preprocessing/_utils.py +17 -5
  28. tme/preprocessing/composable_filter.py +2 -1
  29. tme/preprocessing/compose.py +1 -2
  30. tme/preprocessing/frequency_filters.py +97 -41
  31. tme/preprocessing/tilt_series.py +137 -87
  32. tme/preprocessor.py +3 -0
  33. tme/structure.py +4 -1
  34. pytme-0.2.0.dist-info/RECORD +0 -72
  35. {pytme-0.2.0.data → pytme-0.2.1.data}/scripts/estimate_ram_usage.py +0 -0
  36. {pytme-0.2.0.data → pytme-0.2.1.data}/scripts/preprocess.py +0 -0
  37. {pytme-0.2.0.dist-info → pytme-0.2.1.dist-info}/LICENSE +0 -0
  38. {pytme-0.2.0.dist-info → pytme-0.2.1.dist-info}/WHEEL +0 -0
  39. {pytme-0.2.0.dist-info → pytme-0.2.1.dist-info}/entry_points.txt +0 -0
  40. {pytme-0.2.0.dist-info → pytme-0.2.1.dist-info}/top_level.txt +0 -0
@@ -4,13 +4,15 @@
4
4
 
5
5
  Author: Valentin Maurer <valentin.maurer@embl-hamburg.de>
6
6
  """
7
+ from math import log, sqrt
7
8
  from typing import Tuple, Dict
8
9
 
9
10
  import numpy as np
10
11
  from numpy.typing import NDArray
11
12
  from scipy.ndimage import mean as ndimean
13
+ from scipy.ndimage import map_coordinates
12
14
 
13
- from ._utils import fftfreqn, crop_real_fourier
15
+ from ._utils import fftfreqn, crop_real_fourier, shift_fourier
14
16
  from ..backends import backend
15
17
 
16
18
 
@@ -87,6 +89,9 @@ class BandPassFilter:
87
89
  NDArray
88
90
  The bandpass filter in Fourier space.
89
91
  """
92
+ if shape_is_real_fourier:
93
+ return_real_fourier = False
94
+
90
95
  grid = fftfreqn(
91
96
  shape=shape,
92
97
  sampling_rate=0.5,
@@ -103,15 +108,9 @@ class BandPassFilter:
103
108
  lowcut = np.max(2 * sampling_rate / highpass)
104
109
 
105
110
  bandpass_filter = ((grid <= highcut) & (grid >= lowcut)) * 1.0
106
- shift = backend.add(
107
- backend.astype(backend.divide(bandpass_filter.shape, 2), int),
108
- backend.mod(bandpass_filter.shape, 2),
109
- )
110
- if shape_is_real_fourier:
111
- shift[-1] = 0
112
111
 
113
- bandpass_filter = backend.roll(
114
- bandpass_filter, shift, tuple(i for i in range(len(shift)))
112
+ bandpass_filter = shift_fourier(
113
+ data=bandpass_filter, shape_is_real_fourier=shape_is_real_fourier
115
114
  )
116
115
 
117
116
  if return_real_fourier:
@@ -166,15 +165,15 @@ class BandPassFilter:
166
165
  grid = -backend.square(grid)
167
166
 
168
167
  lowpass_filter, highpass_filter = 1, 1
169
- norm = float(backend.sqrt(2 * backend.log(2)))
168
+ norm = float(sqrt(2 * log(2)))
170
169
  upper_sampling = float(backend.max(backend.multiply(2, sampling_rate)))
171
170
 
172
171
  if lowpass is not None:
173
172
  lowpass = float(lowpass)
174
- lowpass = backend.maximum(lowpass, backend.eps(lowpass))
173
+ lowpass = backend.maximum(lowpass, backend.eps(backend._float_dtype))
175
174
  if highpass is not None:
176
175
  highpass = float(highpass)
177
- highpass = backend.maximum(highpass, backend.eps(highpass))
176
+ highpass = backend.maximum(highpass, backend.eps(backend._float_dtype))
178
177
 
179
178
  if lowpass is not None:
180
179
  lowpass = upper_sampling / (lowpass * norm)
@@ -185,22 +184,15 @@ class BandPassFilter:
185
184
  highpass = backend.multiply(2, backend.square(highpass))
186
185
  highpass_filter = 1 - backend.exp(backend.divide(grid, highpass))
187
186
 
188
- lowpass_filter = backend.multiply(lowpass_filter, highpass_filter)
189
- shift = backend.add(
190
- backend.astype(backend.divide(lowpass_filter.shape, 2), int),
191
- backend.mod(lowpass_filter.shape, 2),
192
- )
193
- if shape_is_real_fourier:
194
- shift[-1] = 0
195
-
196
- lowpass_filter = backend.roll(
197
- lowpass_filter, shift, tuple(i for i in range(len(shift)))
187
+ bandpass_filter = backend.multiply(lowpass_filter, highpass_filter)
188
+ bandpass_filter = shift_fourier(
189
+ data=bandpass_filter, shape_is_real_fourier=shape_is_real_fourier
198
190
  )
199
191
 
200
192
  if return_real_fourier:
201
- lowpass_filter = crop_real_fourier(lowpass_filter)
193
+ bandpass_filter = crop_real_fourier(bandpass_filter)
202
194
 
203
- return lowpass_filter
195
+ return bandpass_filter
204
196
 
205
197
  def __call__(self, **kwargs):
206
198
  func_args = vars(self)
@@ -228,6 +220,16 @@ class LinearWhiteningFilter:
228
220
  -----------
229
221
  **kwargs : Dict, optional
230
222
  Additional keyword arguments.
223
+
224
+
225
+ References
226
+ ----------
227
+ .. [1] de Teresa-Trueba, I.; Goetz, S. K.; Mattausch, A.; Stojanovska, F.; Zimmerli, C. E.;
228
+ Toro-Nahuelpan, M.; Cheng, D. W. C.; Tollervey, F.; Pape, C.; Beck, M.; Diz-Munoz,
229
+ A.; Kreshuk, A.; Mahamid, J.; Zaugg, J. B. Nat. Methods 2023, 20, 284–294.
230
+ .. [2] M. L. Chaillet, G. van der Schot, I. Gubins, S. Roet,
231
+ R. C. Veltkamp, and F. Förster, Int. J. Mol. Sci. 24,
232
+ 13375 (2023)
231
233
  """
232
234
 
233
235
  def __init__(self, **kwargs):
@@ -235,7 +237,7 @@ class LinearWhiteningFilter:
235
237
 
236
238
  @staticmethod
237
239
  def _compute_spectrum(
238
- data_rfft: NDArray, n_bins: int = None
240
+ data_rfft: NDArray, n_bins: int = None, batch_dimension: int = None
239
241
  ) -> Tuple[NDArray, NDArray]:
240
242
  """
241
243
  Compute the spectrum of the input data.
@@ -246,6 +248,8 @@ class LinearWhiteningFilter:
246
248
  The Fourier transform of the input data.
247
249
  n_bins : int, optional
248
250
  The number of bins for computing the spectrum, defaults to None.
251
+ batch_dimension : int, optional
252
+ Batch dimension to average over.
249
253
 
250
254
  Returns:
251
255
  --------
@@ -254,35 +258,70 @@ class LinearWhiteningFilter:
254
258
  radial_averages : NDArray
255
259
  Array containing the radial averages of the spectrum.
256
260
  """
257
- max_bins = max(max(data_rfft.shape[:-1]) // 2 + 1, data_rfft.shape[-1])
261
+ shape = tuple(x for i, x in enumerate(data_rfft.shape) if i != batch_dimension)
262
+
263
+ max_bins = max(max(shape[:-1]) // 2 + 1, shape[-1])
258
264
  n_bins = max_bins if n_bins is None else n_bins
259
265
  n_bins = int(min(n_bins, max_bins))
260
266
 
261
- grid = fftfreqn(
262
- shape=data_rfft.shape,
263
- sampling_rate=None,
267
+ bins = fftfreqn(
268
+ shape=shape,
269
+ sampling_rate=0.5,
264
270
  shape_is_real_fourier=True,
265
271
  compute_euclidean_norm=True,
266
272
  )
267
- _, bin_edges = np.histogram(grid, bins=n_bins - 1)
268
- bins = np.digitize(grid, bins=bin_edges, right=True)
273
+ bins = backend.to_numpy_array(bins)
269
274
 
270
- fft_shift_axes = tuple(range(data_rfft.ndim - 1))
271
- fourier_transform = np.fft.fftshift(data_rfft, axes=fft_shift_axes)
272
- fourier_spectrum = np.square(np.abs(fourier_transform))
273
- radial_averages = ndimean(fourier_spectrum, labels=bins, index=np.unique(bins))
275
+ # Implicit lowpass to nyquist
276
+ bins = np.floor(bins * (n_bins - 1) + 0.5).astype(int)
277
+ fft_shift_axes = tuple(
278
+ i for i in range(data_rfft.ndim - 1) if i != batch_dimension
279
+ )
280
+ fourier_spectrum = np.fft.fftshift(data_rfft, axes=fft_shift_axes)
281
+ fourier_spectrum = np.abs(fourier_spectrum)
282
+ np.square(fourier_spectrum, out=fourier_spectrum)
274
283
 
284
+ radial_averages = ndimean(
285
+ fourier_spectrum, labels=bins, index=np.arange(n_bins)
286
+ )
275
287
  np.sqrt(radial_averages, out=radial_averages)
276
288
  np.reciprocal(radial_averages, out=radial_averages)
277
289
  np.divide(radial_averages, radial_averages.max(), out=radial_averages)
278
290
 
279
291
  return bins, radial_averages
280
292
 
293
+ @staticmethod
294
+ def _interpolate_spectrum(
295
+ spectrum: NDArray,
296
+ shape: Tuple[int],
297
+ shape_is_real_fourier: bool = True,
298
+ order: int = 1,
299
+ ) -> NDArray:
300
+ """
301
+ References
302
+ ----------
303
+ .. [1] M. L. Chaillet, G. van der Schot, I. Gubins, S. Roet,
304
+ R. C. Veltkamp, and F. Förster, Int. J. Mol. Sci. 24,
305
+ 13375 (2023)
306
+ """
307
+ grid = fftfreqn(
308
+ shape=shape,
309
+ sampling_rate=.5,
310
+ shape_is_real_fourier=shape_is_real_fourier,
311
+ compute_euclidean_norm=True,
312
+ )
313
+ grid = backend.to_numpy_array(grid)
314
+ np.multiply(grid, (spectrum.shape[0] - 1), out = grid) + 0.5
315
+ spectrum = map_coordinates(spectrum, grid.reshape(1, -1), order=order)
316
+ return spectrum.reshape(grid.shape)
317
+
281
318
  def __call__(
282
319
  self,
283
320
  data: NDArray = None,
284
321
  data_rfft: NDArray = None,
285
322
  n_bins: int = None,
323
+ batch_dimension: int = None,
324
+ order: int = 1,
286
325
  **kwargs: Dict,
287
326
  ) -> Dict:
288
327
  """
@@ -296,27 +335,44 @@ class LinearWhiteningFilter:
296
335
  The Fourier transform of the input data, defaults to None.
297
336
  n_bins : int, optional
298
337
  The number of bins for computing the spectrum, defaults to None.
338
+ batch_dimension : int, optional
339
+ Batch dimension to average over.
340
+ order : int, optional
341
+ Interpolation order to use.
299
342
  **kwargs : Dict
300
343
  Additional keyword arguments.
301
344
 
302
345
  Returns:
303
346
  --------
304
347
  Dict
305
- A dictionary containing the whitened data and information
306
- about the filter being a multiplicative filter.
348
+ Filter data and associated parameters.
307
349
  """
308
350
  if data_rfft is None:
309
351
  data_rfft = np.fft.rfftn(backend.to_numpy_array(data))
310
352
 
311
353
  data_rfft = backend.to_numpy_array(data_rfft)
312
354
 
313
- bins, radial_averages = self._compute_spectrum(data_rfft, n_bins)
355
+ bins, radial_averages = self._compute_spectrum(
356
+ data_rfft, n_bins, batch_dimension
357
+ )
314
358
 
315
- radial_averages = np.fft.ifftshift(
316
- radial_averages[bins], axes=tuple(range(data_rfft.ndim - 1))
359
+ if order is None:
360
+ cutoff = bins < radial_averages.size
361
+ filter_mask = np.zeros(data_rfft.shape, radial_averages.dtype)
362
+ filter_mask[cutoff] = radial_averages[bins[cutoff]]
363
+ else:
364
+ filter_mask = self._interpolate_spectrum(
365
+ spectrum=radial_averages,
366
+ shape=data_rfft.shape,
367
+ shape_is_real_fourier=True,
368
+ )
369
+
370
+ filter_mask = np.fft.ifftshift(
371
+ filter_mask,
372
+ axes=tuple(i for i in range(data_rfft.ndim - 1) if i != batch_dimension)
317
373
  )
318
374
 
319
375
  return {
320
- "data": backend.to_backend_array(radial_averages),
376
+ "data": backend.to_backend_array(filter_mask),
321
377
  "is_multiplicative_filter": True,
322
378
  }