pytme 0.1.8__cp311-cp311-macosx_14_0_arm64.whl → 0.2.0__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 (42) hide show
  1. pytme-0.2.0.data/scripts/match_template.py +1019 -0
  2. pytme-0.2.0.data/scripts/postprocess.py +570 -0
  3. {pytme-0.1.8.data → pytme-0.2.0.data}/scripts/preprocessor_gui.py +244 -60
  4. {pytme-0.1.8.dist-info → pytme-0.2.0.dist-info}/METADATA +3 -1
  5. pytme-0.2.0.dist-info/RECORD +72 -0
  6. {pytme-0.1.8.dist-info → pytme-0.2.0.dist-info}/WHEEL +1 -1
  7. scripts/extract_candidates.py +218 -0
  8. scripts/match_template.py +459 -218
  9. pytme-0.1.8.data/scripts/match_template.py → scripts/match_template_filters.py +459 -218
  10. scripts/postprocess.py +380 -435
  11. scripts/preprocessor_gui.py +244 -60
  12. scripts/refine_matches.py +218 -0
  13. tme/__init__.py +2 -1
  14. tme/__version__.py +1 -1
  15. tme/analyzer.py +533 -78
  16. tme/backends/cupy_backend.py +80 -15
  17. tme/backends/npfftw_backend.py +35 -6
  18. tme/backends/pytorch_backend.py +15 -7
  19. tme/density.py +173 -78
  20. tme/extensions.cpython-311-darwin.so +0 -0
  21. tme/matching_constrained.py +195 -0
  22. tme/matching_data.py +78 -32
  23. tme/matching_exhaustive.py +369 -221
  24. tme/matching_memory.py +1 -0
  25. tme/matching_optimization.py +753 -649
  26. tme/matching_utils.py +152 -8
  27. tme/orientations.py +561 -0
  28. tme/preprocessing/__init__.py +2 -0
  29. tme/preprocessing/_utils.py +176 -0
  30. tme/preprocessing/composable_filter.py +30 -0
  31. tme/preprocessing/compose.py +52 -0
  32. tme/preprocessing/frequency_filters.py +322 -0
  33. tme/preprocessing/tilt_series.py +967 -0
  34. tme/preprocessor.py +35 -25
  35. tme/structure.py +2 -37
  36. pytme-0.1.8.data/scripts/postprocess.py +0 -625
  37. pytme-0.1.8.dist-info/RECORD +0 -61
  38. {pytme-0.1.8.data → pytme-0.2.0.data}/scripts/estimate_ram_usage.py +0 -0
  39. {pytme-0.1.8.data → pytme-0.2.0.data}/scripts/preprocess.py +0 -0
  40. {pytme-0.1.8.dist-info → pytme-0.2.0.dist-info}/LICENSE +0 -0
  41. {pytme-0.1.8.dist-info → pytme-0.2.0.dist-info}/entry_points.txt +0 -0
  42. {pytme-0.1.8.dist-info → pytme-0.2.0.dist-info}/top_level.txt +0 -0
tme/matching_utils.py CHANGED
@@ -229,6 +229,7 @@ def compute_parallelization_schedule(
229
229
  max_cores: int,
230
230
  max_ram: int,
231
231
  matching_method: str,
232
+ split_axes: Tuple[int] = None,
232
233
  backend: str = None,
233
234
  split_only_outer: bool = False,
234
235
  shape1_padding: NDArray = None,
@@ -259,6 +260,8 @@ def compute_parallelization_schedule(
259
260
  The maximum amount of memory that can be used.
260
261
  matching_method : str
261
262
  The metric used for scoring the computations.
263
+ split_axes : tuple
264
+ Axes that can be used for splitting. By default all are considered.
262
265
  backend : str, optional
263
266
  Backend used for computations.
264
267
  split_only_outer : bool, optional
@@ -303,6 +306,13 @@ def compute_parallelization_schedule(
303
306
  core_assignments = [(1, max_cores)]
304
307
 
305
308
  possible_params, split_axis = [], np.argmax(shape1)
309
+
310
+ split_axis_index = split_axis
311
+ if split_axes is not None:
312
+ split_axis, split_axis_index = split_axes[0], 0
313
+ else:
314
+ split_axes = tuple(i for i in range(len(shape1)))
315
+
306
316
  split_factor, n_splits = [1 for _ in range(len(shape1))], 0
307
317
  while n_splits <= max_splits:
308
318
  splits = {k: split_factor[k] for k in range(len(split_factor))}
@@ -341,9 +351,11 @@ def compute_parallelization_schedule(
341
351
  (*split_factor, outer_cores, inner_cores, n_splits, inits)
342
352
  )
343
353
  split_factor[split_axis] += 1
344
- split_axis += 1
345
- if split_axis == shape1.size:
346
- split_axis = 0
354
+
355
+ split_axis_index += 1
356
+ if split_axis_index == len(split_axes):
357
+ split_axis_index = 0
358
+ split_axis = split_axes[split_axis_index]
347
359
 
348
360
  possible_params = np.array(possible_params)
349
361
  if not len(possible_params):
@@ -609,6 +621,91 @@ def get_rotation_matrices(
609
621
  return ret
610
622
 
611
623
 
624
+ def get_rotations_around_vector(
625
+ cone_angle: float,
626
+ cone_sampling: float,
627
+ axis_angle: float = 360.0,
628
+ axis_sampling: float = None,
629
+ vector: Tuple[float] = (1, 0, 0),
630
+ n_symmetry: int = 1,
631
+ convention: str = None,
632
+ ) -> NDArray:
633
+ """
634
+ Generate rotations describing the possible placements of a vector in a cone.
635
+
636
+ Parameters
637
+ ----------
638
+ cone_angle : float
639
+ The half-angle of the cone in degrees.
640
+ cone_sampling : float
641
+ Angular increment used for sampling points on the cone in degrees.
642
+ axis_angle : float, optional
643
+ The total angle of rotation around the vector axis in degrees (default is 360.0).
644
+ axis_sampling : float, optional
645
+ Angular increment used for sampling points around the vector axis in degrees.
646
+ If None, it takes the value of `cone_sampling`.
647
+ vector : Tuple[float], optional
648
+ Cartesian coordinates in zyx convention.
649
+ n_symmetry : int, optional
650
+ Number of symmetry axis around the vector axis.
651
+ convention : str, optional
652
+ Convention for angles. By default returns rotation matrices.
653
+
654
+ Returns
655
+ -------
656
+ NDArray
657
+ An array of rotation angles represented as Euler angles (phi, theta, psi) in degrees.
658
+ The shape of the array is (n, 3), where `n` is the total number of rotation angles.
659
+ Each row represents a set of rotation angles.
660
+
661
+ References
662
+ ----------
663
+ .. [1] https://stackoverflow.com/questions/9600801/evenly-distributing-n-points-on-a-sphere
664
+
665
+ """
666
+ if axis_sampling is None:
667
+ axis_sampling = cone_sampling
668
+
669
+ # Heuristic to estimate necessary number of points on sphere
670
+ theta = np.linspace(0, cone_angle, round(cone_angle / cone_sampling) + 1)
671
+ number_of_points = np.ceil(
672
+ 360 * np.divide(np.sin(np.radians(theta)), cone_sampling),
673
+ )
674
+ number_of_points = int(np.sum(number_of_points + 1) + 2)
675
+
676
+ # Golden Spiral
677
+ indices = np.arange(0, number_of_points, dtype=float) + 0.5
678
+ radius = cone_angle * np.sqrt(indices / number_of_points)
679
+ theta = np.pi * (1 + np.sqrt(5)) * indices
680
+
681
+ angles_vector = Rotation.from_euler(
682
+ angles=rotation_aligning_vectors([1, 0, 0], vector, convention="zyx"),
683
+ seq="zyx",
684
+ degrees=True,
685
+ )
686
+
687
+ # phi, theta, psi
688
+ axis_angle /= n_symmetry
689
+ phi_steps = np.maximum(np.round(axis_angle / axis_sampling), 1).astype(int)
690
+ phi = np.linspace(0, axis_angle, phi_steps + 1)[:-1]
691
+ np.add(phi, angles_vector.as_euler("zyx", degrees=True)[0], out=phi)
692
+ angles = np.stack(
693
+ [radius * np.cos(theta), radius * np.sin(theta), np.zeros_like(radius)], axis=1
694
+ )
695
+ angles = np.repeat(angles, phi_steps, axis=0)
696
+ angles[:, 2] = np.tile(phi, radius.size)
697
+
698
+ angles = Rotation.from_euler(angles=angles, seq="zyx", degrees=True)
699
+ angles = angles_vector * angles
700
+
701
+ if convention is None:
702
+ rotation_angles = angles.as_matrix()
703
+ else:
704
+ rotation_angles = angles.as_euler(seq=convention, degrees=True)
705
+
706
+ return rotation_angles
707
+
708
+
612
709
  def minimum_enclosing_box(
613
710
  coordinates: NDArray,
614
711
  margin: NDArray = None,
@@ -749,7 +846,7 @@ def crop_input(
749
846
  return reference_fit
750
847
 
751
848
 
752
- def euler_to_rotationmatrix(angles: Tuple[float]) -> NDArray:
849
+ def euler_to_rotationmatrix(angles: Tuple[float], convention: str = "zyx") -> NDArray:
753
850
  """
754
851
  Convert Euler angles to a rotation matrix.
755
852
 
@@ -757,6 +854,8 @@ def euler_to_rotationmatrix(angles: Tuple[float]) -> NDArray:
757
854
  ----------
758
855
  angles : tuple
759
856
  A tuple representing the Euler angles in degrees.
857
+ convention : str, optional
858
+ Euler angle convention.
760
859
 
761
860
  Returns
762
861
  -------
@@ -764,7 +863,7 @@ def euler_to_rotationmatrix(angles: Tuple[float]) -> NDArray:
764
863
  The generated rotation matrix.
765
864
  """
766
865
  n_angles = len(angles)
767
- angle_convention = "zyx"[:n_angles]
866
+ angle_convention = convention[:n_angles]
768
867
  if n_angles == 1:
769
868
  angles = (angles, 0, 0)
770
869
  rotation_matrix = (
@@ -775,7 +874,9 @@ def euler_to_rotationmatrix(angles: Tuple[float]) -> NDArray:
775
874
  return rotation_matrix
776
875
 
777
876
 
778
- def euler_from_rotationmatrix(rotation_matrix: NDArray) -> Tuple:
877
+ def euler_from_rotationmatrix(
878
+ rotation_matrix: NDArray, convention: str = "zyx"
879
+ ) -> Tuple:
779
880
  """
780
881
  Convert a rotation matrix to euler angles.
781
882
 
@@ -783,7 +884,8 @@ def euler_from_rotationmatrix(rotation_matrix: NDArray) -> Tuple:
783
884
  ----------
784
885
  rotation_matrix : NDArray
785
886
  A 2 x 2 or 3 x 3 rotation matrix in z y x form.
786
-
887
+ convention : str, optional
888
+ Euler angle convention.
787
889
  Returns
788
890
  -------
789
891
  Tuple
@@ -795,12 +897,54 @@ def euler_from_rotationmatrix(rotation_matrix: NDArray) -> Tuple:
795
897
  rotation_matrix = temp_matrix
796
898
  euler_angles = (
797
899
  Rotation.from_matrix(rotation_matrix)
798
- .as_euler("zyx", degrees=True)
900
+ .as_euler(convention, degrees=True)
799
901
  .astype(np.float32)
800
902
  )
801
903
  return euler_angles
802
904
 
803
905
 
906
+ def rotation_aligning_vectors(
907
+ initial_vector: NDArray, target_vector: NDArray = [1, 0, 0], convention: str = None
908
+ ):
909
+ """
910
+ Compute the rotation matrix or Euler angles required to align an initial vector with a target vector.
911
+
912
+ Parameters
913
+ ----------
914
+ initial_vector : NDArray
915
+ The initial vector to be rotated.
916
+ target_vector : NDArray, optional
917
+ The target vector to align the initial vector with. Default is [1, 0, 0].
918
+ convention : str, optional
919
+ The generate euler angles in degrees. If None returns a rotation matrix instead.
920
+
921
+ Returns
922
+ -------
923
+ rotation_matrix_or_angles : NDArray or tuple
924
+ Rotation matrix if convention is None else tuple of euler angles.
925
+ """
926
+ initial_vector = np.asarray(initial_vector, dtype=np.float32)
927
+ target_vector = np.asarray(target_vector, dtype=np.float32)
928
+ initial_vector /= np.linalg.norm(initial_vector)
929
+ target_vector /= np.linalg.norm(target_vector)
930
+
931
+ rotation_matrix = np.eye(len(initial_vector))
932
+ if not np.allclose(initial_vector, target_vector):
933
+ rotation_axis = np.cross(initial_vector, target_vector)
934
+ rotation_angle = np.arccos(np.dot(initial_vector, target_vector))
935
+ k = rotation_axis / np.linalg.norm(rotation_axis)
936
+ K = np.array([[0, -k[2], k[1]], [k[2], 0, -k[0]], [-k[1], k[0], 0]])
937
+ rotation_matrix = np.eye(3)
938
+ rotation_matrix += np.sin(rotation_angle) * K
939
+ rotation_matrix += (1 - np.cos(rotation_angle)) * np.dot(K, K)
940
+
941
+ if convention is None:
942
+ return rotation_matrix
943
+
944
+ angles = euler_from_rotationmatrix(rotation_matrix, convention=convention)
945
+ return angles
946
+
947
+
804
948
  def rigid_transform(
805
949
  coordinates: NDArray,
806
950
  rotation_matrix: NDArray,