diff --git a/gallery/experiments/experimental_abinitio_pipeline_10081.py b/gallery/experiments/experimental_abinitio_pipeline_10081.py index be27bc6e43..838b2c2d5a 100644 --- a/gallery/experiments/experimental_abinitio_pipeline_10081.py +++ b/gallery/experiments/experimental_abinitio_pipeline_10081.py @@ -59,7 +59,11 @@ # Create a source object for the experimental images src = RelionSource( - starfile_in, pixel_size=pixel_size, max_rows=n_imgs, data_folder=data_folder + starfile_in, + pixel_size=pixel_size, + max_rows=n_imgs, + data_folder=data_folder, + symmetry_group="C4", ) # Downsample the images @@ -115,12 +119,13 @@ # Volume Reconstruction # ---------------------- # -# Using the oriented source, attempt to reconstruct a volume. -# Since this is a Cn symmetric molecule, as indicated by -# ``symmetry="C4"`` above, the ``avgs`` images set will be repeated -# for each of the 3 additional rotations during the back-projection -# step. This boosts the effective number of images used in the -# reconstruction from ``n_classes`` to ``4*n_classes``. +# Using the oriented source, attempt to reconstruct a volume. Since +# this is a Cn symmetric molecule, as specified by ``RelionSource(..., +# symmetry_group="C4", ...)``, the ``symmetry_group`` source attribute +# will flow through the pipeline to ``avgs``. Then each image will be +# repeated for each of the 3 additional rotations during +# back-projection. This boosts the effective number of images used in +# the reconstruction from ``n_classes`` to ``4*n_classes``. logger.info("Begin Volume reconstruction") diff --git a/src/aspire/abinitio/commonline_c3_c4.py b/src/aspire/abinitio/commonline_c3_c4.py index 8e9652258a..68469618dd 100644 --- a/src/aspire/abinitio/commonline_c3_c4.py +++ b/src/aspire/abinitio/commonline_c3_c4.py @@ -47,7 +47,7 @@ def __init__( n_theta=None, max_shift=0.15, shift_step=1, - epsilon=1e-3, + epsilon=1e-2, max_iters=1000, degree_res=1, seed=None, @@ -691,7 +691,8 @@ def _J_sync_power_method(self, vijs): ) while itr < max_iters and residual > epsilon: itr += 1 - vec_new = self._signs_times_v(vijs, vec) + # Note, this appears to need double precision for accuracy in the following division. + vec_new = self._signs_times_v(vijs, vec).astype(np.float64, copy=False) vec_new = vec_new / norm(vec_new) residual = norm(vec_new - vec) vec = vec_new diff --git a/src/aspire/denoising/class_avg.py b/src/aspire/denoising/class_avg.py index 586e0a08a9..20c3694dbf 100644 --- a/src/aspire/denoising/class_avg.py +++ b/src/aspire/denoising/class_avg.py @@ -76,6 +76,7 @@ def __init__( L=self.averager.src.L, n=self.averager.src.n, dtype=self.averager.src.dtype, + symmetry_group=self.src.symmetry_group, ) # Any further operations should not mutate this instance. diff --git a/src/aspire/image/image.py b/src/aspire/image/image.py index ff2353d333..fd160d1644 100644 --- a/src/aspire/image/image.py +++ b/src/aspire/image/image.py @@ -517,6 +517,8 @@ def backproject(self, rot_matrices, symmetry_group=None): # Get symmetry rotations from SymmetryGroup. symmetry_rots = SymmetryGroup.parse(symmetry_group, dtype=self.dtype).matrices + if len(symmetry_rots) > 1: + logger.info(f"Boosting with {len(symmetry_rots)} rotational symmetries.") # Compute Fourier transform of images. im_f = xp.asnumpy(fft.centered_fft2(xp.asarray(self._data))) / (L**2) diff --git a/tests/test_class_src.py b/tests/test_class_src.py index 0c169621cd..e395aecc8e 100644 --- a/tests/test_class_src.py +++ b/tests/test_class_src.py @@ -128,7 +128,14 @@ def class_sim_fixture(dtype, img_size): # Note using a single volume via C=1 is critical to matching # alignment without the complexity of remapping via states etc. src = Simulation( - L=img_size, n=n, vols=v, offsets=0, amplitudes=1, C=1, angles=true_rots.angles + L=img_size, + n=n, + vols=v, + offsets=0, + amplitudes=1, + C=1, + angles=true_rots.angles, + symmetry_group="C4", # For testing symmetry_group pass-through. ) # Prefetch all the images src = src.cache() @@ -193,6 +200,9 @@ class averages. k = len(src2.class_indices) np.testing.assert_equal(src2.class_indices, test_src.class_indices[::3][:k]) + # Check symmetry_group pass-through. + assert test_src.symmetry_group == class_sim_fixture.symmetry_group + # Test the _HeapItem helper class def test_heap_helper(): diff --git a/tests/test_indexed_source.py b/tests/test_indexed_source.py index 30a23ee16b..9ce35c7052 100644 --- a/tests/test_indexed_source.py +++ b/tests/test_indexed_source.py @@ -13,7 +13,7 @@ def sim_fixture(): """ Generate a very small simulation and slice it. """ - sim = Simulation(L=8, n=10, C=1) + sim = Simulation(L=8, n=10, C=1, symmetry_group="D3") sim2 = sim[0::2] # Slice the evens return sim, sim2 @@ -31,6 +31,9 @@ def test_remapping(sim_fixture): # Check meta is served correctly. assert np.all(sim.get_metadata(indices=sim2.index_map) == sim2.get_metadata()) + # Check symmetry_group pass-through. + assert sim.symmetry_group == sim2.symmetry_group + def test_repr(sim_fixture): sim, sim2 = sim_fixture