From a2629daa3f2c59be2b1c4fc1b9122ca75150e37b Mon Sep 17 00:00:00 2001 From: jed Date: Sat, 19 Mar 2022 17:09:42 -0700 Subject: [PATCH 001/148] Init --- .../expectation_maximization.py | 357 ++++++++++++++++++ tests/test_expectation_maximization.py | 9 + 2 files changed, 366 insertions(+) create mode 100644 iterative_refinement/expectation_maximization.py create mode 100644 tests/test_expectation_maximization.py diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py new file mode 100644 index 0000000..4f1164a --- /dev/null +++ b/iterative_refinement/expectation_maximization.py @@ -0,0 +1,357 @@ +""" +References +1. Nelson, P. C. (2019). Chapter 12 : Single Particle Reconstruction in Cryo-electron Microscopy. + In Physical Models of Living Systems (pp. 305–325). + https://repository.upenn.edu/cgi/viewcontent.cgi?article=1665&context=physics_papers +2. Scheres, S. H. W. (2012). RELION: Implementation of a Bayesian approach to cryo-EM structure determination. + Journal of Structural Biology, 180(3), 519–530. + http://doi.org/10.1016/j.jsb.2012.09.006 +3. Sigworth, F. J., Doerschuk, P. C., Carazo, J.-M., & Scheres, S. H. W. (2010). + An Introduction to Maximum-Likelihood Methods in Cryo-EM. + In Methods in Enzymology (1st ed., Vol. 482, pp. 263–294). Elsevier Inc. + http://doi.org/10.1016/S0076-6879(10)82011-7 + +Note that functions are defined in place for readability. code should be filled out and refactored for performance +""" + +import numpy as np +from compSPI.transforms import do_fft, do_ifft # currently only 2D ffts in compSPI.transforms. can use torch.fft for 3d fft and convert back to numpy array + + +def do_iterative_refinement(map_3d_init, particles, ctf_info): + """ + Performs interative refimenent in a Bayesean expectation maximization setting, + i.e. maximum a posteriori estimation. + + Input + _________ + map_3d_init + initial estimate + input map + shape (n_pix,n_pix,n_pix) + particles + particles to be reconstructed + shape (n_pix,n_pix) + + + Returns + _________ + + map_3d_final + shape (n_pix,n_pix,n_pix) + + map_3d_r_final + final updated map + shape (n_pix,n_pix,n_pix) + half_map_3d_r_1 + half map 1 + half_map_3d_r_2 + half map 2 + fsc_1d + final 1d fsc + shape (n_pix//2,) + + """ + + # split particles up into two half sets for statistical validation + + def do_split(arr): + idx_half = arr.shape[0] // 2 + arr_1, arr_2 = arr[:idx_half], arr[idx_half:] + assert arr_1.shape[0] == arr_2.shape[0] + return parr_1, arr_2 + + particles_1, particles_2 = do_split(particles) + + def do_build_ctf(ctf_params): + """ + Build 2D array of ctf from ctf params + + Input + ___ + Params of ctfs (defocus, etc) + Suggest list of dicts, one for each particle. + + Returns + ___ + ctfs + type np.ndarray + shape (n_ctfs,n_pix,n_pix) + + """ + n_ctfs = len(ctf_params) + # TODO: see simSPI.transfer + # https://github.com/compSPI/simSPI/blob/master/simSPI/transfer.py#L57 + ctfs = np.ones((n_ctfs,n_pix,n_pix)) + + return ctfs + + ctfs = do_build_ctf(ctf_info) + ctfs_1, ctfs_2 = do_split(ctfs) + + # work in Fourier space. so particles can stay in Fourier space the whole time. + # they are experimental measurements and are fixed in the algorithm + particles_f_1 = do_fft(particles_1) + particles_f_2 = do_fft(particles_2) + + n_pix = map_3d_init.shape[0] + # suggest 32 or 64 to start with. real data will be more like 128 or 256. + # can have issues with ctf at small pixels and need to zero pad to avoid artefacts + # artefacts from ctf not going to zero at edges, and sinusoidal ctf rippling too fast + # can zero pad when do Fourier convolution (fft is on zero paded and larger sized array) + + max_n_iters = 7 # in practice programs using 3-10 iterations. + + half_map_3d_r_1,half_map_3d_r_2 = map_3d_init, map_3d_init.copy() + # should diverge because different particles averaging in + + for iteration in range(max_n_iters): + + half_map_3d_f_1 = do_fft(half_map_3d_r_1,d=3) + half_map_3d_f_2 = do_fft(half_map_3d_r_2,d=3) + + + # align particles to 3D volume + # decide on granularity of rotations + # i.e. how finely the rotational SO(3) space is sampled in a grid search. + # smarter method is branch and bound... + # perhaps can make grid up front of slices, and then only compute norms on finer grid later. so re-use slices + + + # def do_adaptive_grid_search(particle, map_3d): + # # a la branch and bound + # # not sure exactly how you decide how finely gridded to make it. + # # perhaps heuristics based on how well the signal agrees in half_map_1, half_map_2 (Fourier frequency) + + + def grid_SO3_uniform(n_rotations): + """ + uniformly grid (not sample) SO(3) + can use some intermediate encoding of SO(3) like quaternions, axis angle, Euler + final output 3x3 rotations + + """ + # TODO: sample over the sphere at given granularity. + # easy: draw uniform samples of rotations on sphere. lots of code for this all over the internet. quick solution in geomstats + # harder: draw samples around some rotation using ProjectedNormal distribution (ask Geoff) + # unknown difficulty: make a regular grid of SO(3) at given granularity. Khanh says non-trivial. + rots = np.ones((n_rotations,3,3)) + return rots + + n_rotations = 1000 + rots = grid_SO3(n_rotations) + + def do_xy0_plane(n_pix): + """ + generate xy0 plane + xy values are over the xy plane + all z values are 0 + see how meshgrid and generate coordinates functions used in https://github.com/geoffwoollard/compSPI/blob/stash_simulate/src/simulate.py#L96 + + """ + + ### methgrid + xy0_plane = np.ones(n_pix**2,3) + return xy0 + + xy0_plane = do_xy0_plane(n_pix): + + + def do_slices(map_3d_f,rots): + """ + generates slice coordinates by rotating xy0 plane + interpolate values from map_3d_f onto 3D coordinates + see how scipy map_values used to interpolate in https://github.com/geoffwoollard/compSPI/blob/stash_simulate/src/simulate.py#L111 + + Returns + ___ + slices + slice of map_3d_f + by Fourier slice theorem corresponds to Fourier transform of projection of rotated map_3d_f + + """ + n_rotations = rots.shape[0] + ### TODO: map_values interpolation + xyz_rotated = np.ones_like(xy0_plane) + slices = np.random.normal(size=n_rotations*n_pix**2).reshape(n_rotations,n_pix,n_pix) + return slices, xyz_rotated + + slices_1, xyz_rotated = do_slices(half_map_3d_1_f,rots) # Here rots are the same for the half maps, but could be different in general + slices_2, xyz_rotated = do_slices(half_map_3d_2_f,rots) + + + def do_conv_ctf(projection_f, ctf): + """ + Apply CTF to projection + """ + + # TODO: vectorize and have shape match + projection_f_conv_ctf = ctf*projection_f + return + + + + def do_bayesean_weights(particle, slices): + """ + compute bayesean weights of particle to slice + under gaussian white noise model + + Input + ____ + slices + shape (n_slices, n_pix,n_pix) + dtype complex32 or complex64 + + Returns + ___ + bayesean_weights + shape (n_slices,) + dtyle float32 or float64 + + """ + n_slices = slices.shape[0] + particle_l2 = np.linalg.norm(particle, ord='fro')**2 + slices_l2 = np.linalg.norm(slices,axis=(1,2),ord='fro')**2 # TODO: check right axis. should n_slices l2 norms, one for each slice + # can precompute slices_l2 and keep for all particles if slices the same for different particles + + corr = np.zeros(slices) + a_slice_corr = particle.dot(slices) # |particle|^2 - particle.dot(a_slice) + |a_slice|^2 + ### see Sigrowth et al and Nelson for how to get bayes factors + bayes_factors = np.random.normal(n_slices) # TODO: replace placeholder with right shape + return bayes_factors + + + + # initialize + map_3d_f_updated_1 = np.zeros_like(half_map_3d_f_1) # complex + map_3d_f_updated_2 = np.zeros_like(half_map_3d_f_2) # complex + counts_3d_updated_1 = np.zeros_like(half_map_3d_r_1) # float/real + counts_3d_updated_2 = np.zeros_like(half_map_3d_r_2) # float/real + + for particle_idx in range(particles_1_f.shape[0]): + ctf_1 = ctfs_1[particle_idx] + ctf_2 = ctfs_2[particle_idx] + particle_f_1 = particles_f_1[particle_idx] + particle_f_2 = particles_f_2[particle_idx] + + def do_wiener_filter(projection, ctf, small_number): + wfilter = ctf/(ctf*ctf+small_number) + projection_wfilter_f = projection*w_filter + return projection_wfilter_f + + + particle_f_deconv_1 = do_wiener_filter(particles_f_1, ctf_1) + particle_f_deconv_1 = do_wiener_filter(particles_f_1, ctf_1) + + slices_conv_ctfs_1 = do_conv_ctf(slices_1, ctf_1) # all slices get convolved with the ctf for the particle + slices_conv_ctfs_2 = do_conv_ctf(slices_2, ctf_2) + + bayes_factors_1 = do_bayesean_weights(particles_1_f[particle_idx], slices_conv_ctfs_1) + bayes_factors_2 = do_bayesean_weights(particles_2_f[particle_idx], slices_conv_ctfs_2) + + def do_insert_slice(slice_real,xyz,n_pix): + """ + Update map_3d_f_updated with values from slice. Requires interpolation of off grid + see "Insert Fourier slices" in https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/fourier_slice_2D_3D_with_trilinear.ipynb + # TODO: vectorize so can take in many slices; + # i.e. do the compoutation in a vetorized way and return inserted_slices_3d, counts_3d of shape (n_slice,n_pix,n_pix,n_pix) + + Input + ___ + + slice + type array of shape (n_pix,n_pix) + dtype float32 or float64. real since imaginary and real part done separately + xyz + type array of shape (n_pix**2,3) + volume_3d_shape: float n_pix + + + + + Return + ___ + inserted_slice_3d + count_3d + + """ + + volume_3d = np.zeros((n_pix,n_pix,n_pix)) + # TODO: write insertion code. use linear interpolation (order of interpolation kernel) so not expensive. + # nearest neightbors cheaper, but we can afford to do better than that + + return inserted_slice_3d, count_3d + + + for one_slice_idx in range(bayes_factors_1.shape[0]): + xyz = xyz_rotated[one_slice_idx] + inserted_slice_3d_r, count_3d_r = do_insert_slice(particle_f_deconv_1.real,xyz,volume_3d) # if this can be vectorized, can avoid loop over slices + inserted_slice_3d_i, count_3d_i = do_insert_slice(particle_f_deconv_1.imag,xyz,volume_3d) # if this can be vectorized, can avoid loop over slices + map_3d_f_updated_1 += inserted_slice_3d_r + 1j*inserted_slice_3d_i + counts_3d_updated_1 += count_3d_r + count_3d_i + + for one_slice_idx in range(bayes_factors_2.shape[0]): + xyz = xyz_rotated[one_slice_idx] + inserted_slice_3d_r, count_3d_r = do_insert_slice(particle_f_deconv_2.real,xyz,volume_3d) # if this can be vectorized, can avoid loop over slices + inserted_slice_3d_i, count_3d_i = do_insert_slice(particle_f_deconv_2.imag,xyz,volume_3d) # if this can be vectorized, can avoid loop over slices + map_3d_f_updated_2 += inserted_slice_3d_r + 1j*inserted_slice_3d_i + counts_3d_updated_2 += count_3d_r + count_3d_i + + + # apply noise model + # half_map_1, half_map_2 come from doing the above independently + # filter by noise estimate (e.g. multiply both half maps by FSC) + + def do_fsc(map_3d_f_1,map_3d_f_2): + """ + Estimate noise from half maps + for now do noise estimate as FSC between half maps + + """ + # TODO: write fast vectorized fsc from code snippets in + # https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/fsc.ipynb + # https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/mFSC.ipynb + # https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/guinier_fsc_sharpen.ipynb + n_pix = map_3d_f_1.shape[0] + fsc_1d = np.ones(n_pix//2) + return noise_estimate + + + fsc_1d = do_estimate_noise(map_3d_f_updated_1,map_3d_f_updated_2) + + def do_expand_1d_3d(arr_1d): + n_pix = arr_1d.shape[0]*2 + arr_3d = np.ones((n_pix,n_pix,n_pix)) + # TODO: arr_1d fsc_1d to 3d (spherical shells) + return arr_3d + + fsc_3d = do_expand_1d_3d(fsc_1d) + + # multiplicative filter on maps with fsc + # The FSC is 1D, one number per spherical shells + # it can be expanded back to a multiplicative filter of the same shape as the maps + map_3d_f_filtered_1 = map_3d_f_updated_1*fsc_3d + map_3d_f_filtered_2 = map_3d_f_updated_2*fsc_3d + + # update iteration + half_map_3d_f_1 = map_3d_f_filtered_1 + half_map_3d_f_2 = map_3d_f_filtered_2 + + # final map + fsc_1d = do_estimate_noise(half_map_3d_f_1,half_map_3d_f_2) + fsc_3d = do_expand_1d_3d(fsc_1d) + map_3d_f_final = (half_map_3d_f_1 + half_map_3d_f_2 / 2)*fsc_3d + map_3d_r_final = do_ifft(map_3d_f_final) + half_map_3d_r_1 = do_ifft(half_map_3d_f_1) + half_map_3d_r_2 = do_ifft(half_map_3d_f_2) + + return map_3d_r_final, half_map_3d_r_1, half_map_3d_r_2, fsc_1d + + + + + + + + + diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py new file mode 100644 index 0000000..6ce316a --- /dev/null +++ b/tests/test_expectation_maximization.py @@ -0,0 +1,9 @@ +def test_do_iterative_refinement(): + n_pix = 64 + map_3d_init = np.random.normal(size=n_pix**3).reshape(n_pix,n_pix,n_pix) + particles = np.random.normal(size=n_pix**2).reshape(n_pix,n_pix) + map_3d_r_final, half_map_3d_r_1, half_map_3d_r_2, fsc_1d = iterative_refinement(map_3d_init, particles) + assert map_3d_r_final.shape == (n_pix,n_pix,n_pix) + assert fsc_1d.dtype = np.float32 + assert half_map_3d_r_1.dtype = np.float32 + assert half_map_3d_r_2.dtype = np.float32 \ No newline at end of file From ccc4359697dde3aa62b5865646b26c9975fb12f4 Mon Sep 17 00:00:00 2001 From: jed Date: Sun, 20 Mar 2022 14:42:45 -0700 Subject: [PATCH 002/148] precommit yaml --- iterative_refinement/expectation_maximization.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index 4f1164a..486c125 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -11,7 +11,7 @@ In Methods in Enzymology (1st ed., Vol. 482, pp. 263–294). Elsevier Inc. http://doi.org/10.1016/S0076-6879(10)82011-7 -Note that functions are defined in place for readability. code should be filled out and refactored for performance +Iterative refinement in Bayesian expection maximization setting. """ import numpy as np @@ -20,11 +20,11 @@ def do_iterative_refinement(map_3d_init, particles, ctf_info): """ - Performs interative refimenent in a Bayesean expectation maximization setting, + Performs interative refimenent in a Bayesian expectation maximization setting, i.e. maximum a posteriori estimation. Input - _________ + ----- map_3d_init initial estimate input map @@ -35,7 +35,7 @@ def do_iterative_refinement(map_3d_init, particles, ctf_info): Returns - _________ + ------- map_3d_final shape (n_pix,n_pix,n_pix) From 186fddc7176b37db0aa4fbf6710086dff1b6fd63 Mon Sep 17 00:00:00 2001 From: jed Date: Sun, 20 Mar 2022 14:42:56 -0700 Subject: [PATCH 003/148] remove ref --- iterative_refinement/expectation_maximization.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index 486c125..8f54012 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -1,21 +1,10 @@ """ -References -1. Nelson, P. C. (2019). Chapter 12 : Single Particle Reconstruction in Cryo-electron Microscopy. - In Physical Models of Living Systems (pp. 305–325). - https://repository.upenn.edu/cgi/viewcontent.cgi?article=1665&context=physics_papers -2. Scheres, S. H. W. (2012). RELION: Implementation of a Bayesian approach to cryo-EM structure determination. - Journal of Structural Biology, 180(3), 519–530. - http://doi.org/10.1016/j.jsb.2012.09.006 -3. Sigworth, F. J., Doerschuk, P. C., Carazo, J.-M., & Scheres, S. H. W. (2010). - An Introduction to Maximum-Likelihood Methods in Cryo-EM. - In Methods in Enzymology (1st ed., Vol. 482, pp. 263–294). Elsevier Inc. - http://doi.org/10.1016/S0076-6879(10)82011-7 - Iterative refinement in Bayesian expection maximization setting. """ import numpy as np -from compSPI.transforms import do_fft, do_ifft # currently only 2D ffts in compSPI.transforms. can use torch.fft for 3d fft and convert back to numpy array +from compSPI.transforms import do_fft, do_ifft +# currently only 2D ffts in compSPI.transforms. can use torch.fft for 3d fft and convert back to numpy array def do_iterative_refinement(map_3d_init, particles, ctf_info): From 62b9f42613ebbc4783d6d7cadd5f83a9f673ab2d Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Sun, 20 Mar 2022 21:43:52 +0000 Subject: [PATCH 004/148] Format code with black and isort --- iterative_refinement/expectation_maximization.py | 1 + 1 file changed, 1 insertion(+) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index 8f54012..826a0ef 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -4,6 +4,7 @@ import numpy as np from compSPI.transforms import do_fft, do_ifft + # currently only 2D ffts in compSPI.transforms. can use torch.fft for 3d fft and convert back to numpy array From ee5adab3702379f2a073bad87b20b5f261c44b42 Mon Sep 17 00:00:00 2001 From: jed Date: Sun, 20 Mar 2022 16:34:21 -0700 Subject: [PATCH 005/148] Docstrings, precommit broken? --- .../expectation_maximization.py | 502 ++++++++++-------- tests/test_expectation_maximization.py | 2 +- 2 files changed, 288 insertions(+), 216 deletions(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index 826a0ef..5380c3e 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -1,347 +1,419 @@ """ -Iterative refinement in Bayesian expection maximization setting. +Iterative refinement in Bayesian expection maximization setting +for reconstruction of particles. """ import numpy as np from compSPI.transforms import do_fft, do_ifft -# currently only 2D ffts in compSPI.transforms. can use torch.fft for 3d fft and convert back to numpy array - def do_iterative_refinement(map_3d_init, particles, ctf_info): """ - Performs interative refimenent in a Bayesian expectation maximization setting, - i.e. maximum a posteriori estimation. - - Input - ----- - map_3d_init - initial estimate - input map - shape (n_pix,n_pix,n_pix) - particles - particles to be reconstructed - shape (n_pix,n_pix) - - + Performs interative refimenent in a Bayesian expectation + maximization setting, i.e. maximum a posteriori estimation. + + Parameters + ---------- + map_3d_init : arr + Initial particle map. + Shape (n_pix,n_pix,n_pix) + particles : arr + Particles to be reconstructed. + Shape (n_pix,n_pix) + Returns ------- - - map_3d_final - shape (n_pix,n_pix,n_pix) - - map_3d_r_final - final updated map - shape (n_pix,n_pix,n_pix) - half_map_3d_r_1 - half map 1 - half_map_3d_r_2 - half map 2 - fsc_1d - final 1d fsc - shape (n_pix//2,) - + map_3d_update : arr + Current iteration of map. + Shape (n_pix, n_pix, n_pix) + + map_3d_final : arr + Final updated map. + Shape (n_pix,n_pix,n_pix) + half_map_3d_final_1 : arr + Shape (n_pix,n_pix,n_pix) + half_map_3d_final_2 : arr + Shape (n_pix,n_pix,n_pix) + fsc_1d : arr + Final one dimensional fourier shell correlation. + Shape (n_pix//2,) """ - # split particles up into two half sets for statistical validation - def do_split(arr): + """ + Split distribution of particles into two distinct sets for + statistical validation purposes. + + Parameters + ---------- + arr : arr + Shape (n_pix,n_pix,n_pix) + + Returns + ------- + arr1 : arr + Shape (n_pix // 2,n_pix,n_pix) + arr2: arr + Shape (n_pix // 2,n_pix,n_pix) + """ idx_half = arr.shape[0] // 2 arr_1, arr_2 = arr[:idx_half], arr[idx_half:] assert arr_1.shape[0] == arr_2.shape[0] - return parr_1, arr_2 + return arr_1, arr_2 particles_1, particles_2 = do_split(particles) def do_build_ctf(ctf_params): """ - Build 2D array of ctf from ctf params + Build 2D array of CTF from contrast transfer + function parameters. - Input - ___ - Params of ctfs (defocus, etc) - Suggest list of dicts, one for each particle. + Parameters + ---------- + ctf_params : list of dicts + Each dict contains CTF k,v pairs per particle. + Shape (n_pix,) Returns - ___ - ctfs - type np.ndarray - shape (n_ctfs,n_pix,n_pix) - + ------- + ctfs : arr + Shape (n_ctfs,n_pix,n_pix) """ n_ctfs = len(ctf_params) # TODO: see simSPI.transfer # https://github.com/compSPI/simSPI/blob/master/simSPI/transfer.py#L57 - ctfs = np.ones((n_ctfs,n_pix,n_pix)) + ctfs = np.ones((n_ctfs, n_pix, n_pix)) return ctfs ctfs = do_build_ctf(ctf_info) ctfs_1, ctfs_2 = do_split(ctfs) - # work in Fourier space. so particles can stay in Fourier space the whole time. + # work in Fourier space. as particles stay in Fourier space the whole time. # they are experimental measurements and are fixed in the algorithm particles_f_1 = do_fft(particles_1) particles_f_2 = do_fft(particles_2) - n_pix = map_3d_init.shape[0] - # suggest 32 or 64 to start with. real data will be more like 128 or 256. - # can have issues with ctf at small pixels and need to zero pad to avoid artefacts - # artefacts from ctf not going to zero at edges, and sinusoidal ctf rippling too fast - # can zero pad when do Fourier convolution (fft is on zero paded and larger sized array) + n_pix = map_3d_init.shape[0] - max_n_iters = 7 # in practice programs using 3-10 iterations. + max_n_iters = 7 # in practice programs using 3-10 iterations. - half_map_3d_r_1,half_map_3d_r_2 = map_3d_init, map_3d_init.copy() - # should diverge because different particles averaging in + half_map_3d_r_1, half_map_3d_r_2 = map_3d_init, map_3d_init.copy() for iteration in range(max_n_iters): - half_map_3d_f_1 = do_fft(half_map_3d_r_1,d=3) - half_map_3d_f_2 = do_fft(half_map_3d_r_2,d=3) + half_map_3d_f_1 = do_fft(half_map_3d_r_1, d=3) + half_map_3d_f_2 = do_fft(half_map_3d_r_2, d=3) + def grid_SO3_uniform(n_rotations): + """ + Uniformly gridded SO(3) rotations. - # align particles to 3D volume - # decide on granularity of rotations - # i.e. how finely the rotational SO(3) space is sampled in a grid search. - # smarter method is branch and bound... - # perhaps can make grid up front of slices, and then only compute norms on finer grid later. so re-use slices - + Parameters + ---------- + n_rotations : int + Number of rotations - # def do_adaptive_grid_search(particle, map_3d): - # # a la branch and bound - # # not sure exactly how you decide how finely gridded to make it. - # # perhaps heuristics based on how well the signal agrees in half_map_1, half_map_2 (Fourier frequency) - + Returns + ------- + rots : arr + List of rotations. + Shape (n_rotations, 3, 3) - def grid_SO3_uniform(n_rotations): """ - uniformly grid (not sample) SO(3) - can use some intermediate encoding of SO(3) like quaternions, axis angle, Euler - final output 3x3 rotations - - """ - # TODO: sample over the sphere at given granularity. - # easy: draw uniform samples of rotations on sphere. lots of code for this all over the internet. quick solution in geomstats - # harder: draw samples around some rotation using ProjectedNormal distribution (ask Geoff) - # unknown difficulty: make a regular grid of SO(3) at given granularity. Khanh says non-trivial. - rots = np.ones((n_rotations,3,3)) + # TODO: sample over the sphere at given granularity. + # easy: draw uniform samples of rotations on sphere. lots of code for this all over the internet. quick solution in geomstats + # harder: draw samples around some rotation using ProjectedNormal distribution (ask Geoff) + # unknown difficulty: make a regular grid of SO(3) at given granularity. Khanh says non-trivial. + rots = np.ones((n_rotations, 3, 3)) return rots n_rotations = 1000 - rots = grid_SO3(n_rotations) + rots = grid_SO3_uniform(n_rotations) - def do_xy0_plane(n_pix): + def generate_xy_plane(n_pix): """ - generate xy0 plane - xy values are over the xy plane - all z values are 0 - see how meshgrid and generate coordinates functions used in https://github.com/geoffwoollard/compSPI/blob/stash_simulate/src/simulate.py#L96 - + Generate xy plane. + + Parameters + ---------- + n_pix : int + Number of pixels + + Returns + xy_plane : arr + Array describing xy plane in space. + Shape (n_pix**2, 3) """ - ### methgrid - xy0_plane = np.ones(n_pix**2,3) - return xy0 + # See how meshgrid and generate coordinates functions used + # TODO: + # https://github.com/geoffwoollard/compSPI/blob/stash_simulate/src/simulate.py#L96 - xy0_plane = do_xy0_plane(n_pix): + xy_plane = np.ones((n_pix ** 2, 3)) + return xy_plane + xy_plane = generate_xy_plane(n_pix) - def do_slices(map_3d_f,rots): + def do_slices(map_3d_f, rots): """ - generates slice coordinates by rotating xy0 plane - interpolate values from map_3d_f onto 3D coordinates - see how scipy map_values used to interpolate in https://github.com/geoffwoollard/compSPI/blob/stash_simulate/src/simulate.py#L111 + Generates slice coordinates by rotating xy plane. + Interpolate values from map_3d_f onto 3D coordinates. + TODO: See how scipy map_values used to interpolate in + https://github.com/geoffwoollard/compSPI/blob/stash_simulate/src/simulate.py#L111 + + Parameters + ---------- + map_3d_f : arr + Shape (n_pix,n_pix,n_pix) + rots : arr + List of rotations. + Shape (n_rotations, 3, 3) Returns - ___ - slices - slice of map_3d_f - by Fourier slice theorem corresponds to Fourier transform of projection of rotated map_3d_f - + ------- + slices : arr + Slice of map_3d_f. Corresponds to Fourier transform + of projection of rotated map_3d_f. + Shape (n_rotations, n_pix, n_pix) + xyz_rotated : arr + Rotated xy plane. + Shape (n_pix**2, 3) """ n_rotations = rots.shape[0] - ### TODO: map_values interpolation - xyz_rotated = np.ones_like(xy0_plane) - slices = np.random.normal(size=n_rotations*n_pix**2).reshape(n_rotations,n_pix,n_pix) + # TODO: map_values interpolation, calculate from map, rots + xyz_rotated = np.ones_like(xy_plane) + size = n_rotations * n_pix ** 2 + slices = np.random.normal(size=size) + slices.reshape(n_rotations, n_pix, n_pix) return slices, xyz_rotated - slices_1, xyz_rotated = do_slices(half_map_3d_1_f,rots) # Here rots are the same for the half maps, but could be different in general - slices_2, xyz_rotated = do_slices(half_map_3d_2_f,rots) - + slices_1, xyz_rotated = do_slices(half_map_3d_f_1, rots) + slices_2, xyz_rotated = do_slices(half_map_3d_f_2, rots) - def do_conv_ctf(projection_f, ctf): + def do_conv_ctf(slice, ctf): """ Apply CTF to projection + slice : arr + Slice of map_3d_f. Corresponds to Fourier transform + of projection of rotated map_3d_f. + Shape (n_pix, n_pix) + ctf : arr + CTF parameters for particle. + Shape (n_pix,n_pix) """ # TODO: vectorize and have shape match - projection_f_conv_ctf = ctf*projection_f - return - + projection_f_conv_ctf = ctf * slice + return projection_f_conv_ctf - - def do_bayesean_weights(particle, slices): + def do_bayesian_weights(particle, slices): """ - compute bayesean weights of particle to slice - under gaussian white noise model + Compute Bayesian weights of particle to slice + under Gaussian white noise model. + + Parameters + ---------- + particle : arr + Shape (n_pix // 2,n_pix,n_pix) - Input - ____ - slices - shape (n_slices, n_pix,n_pix) - dtype complex32 or complex64 + slices : complex64 arr + Shape (n_slices, n_pix, n_pix) Returns - ___ - bayesean_weights - shape (n_slices,) - dtyle float32 or float64 - + ------- + bayesian_weights : float64 arr + Shape (n_slices,) """ n_slices = slices.shape[0] - particle_l2 = np.linalg.norm(particle, ord='fro')**2 - slices_l2 = np.linalg.norm(slices,axis=(1,2),ord='fro')**2 # TODO: check right axis. should n_slices l2 norms, one for each slice - # can precompute slices_l2 and keep for all particles if slices the same for different particles - - corr = np.zeros(slices) - a_slice_corr = particle.dot(slices) # |particle|^2 - particle.dot(a_slice) + |a_slice|^2 - ### see Sigrowth et al and Nelson for how to get bayes factors - bayes_factors = np.random.normal(n_slices) # TODO: replace placeholder with right shape + # particle_l2 = np.linalg.norm(particle, ord='fro')**2 + # slices_l2 = np.linalg.norm(slices, axis=(1, 2), ord='fro')**2 + + # TODO: check right ax. should n_slices l2 norms, 1 for each slice + # can precompute slices_l2 and keep for all particles + # if slices the same for different particles + + # corr = np.zeros(slices) + # a_slice_corr = particle.dot(slices) + # |particle|^2 - particle.dot(a_slice) + |a_slice|^2 + # see Sigrowth et al and Nelson for how to get bayes factors + bayes_factors = np.random.normal(n_slices) return bayes_factors + # Initialize + map_3d_f_updated_1 = np.zeros_like(half_map_3d_f_1) # complex + map_3d_f_updated_2 = np.zeros_like(half_map_3d_f_2) # complex + counts_3d_updated_1 = np.zeros_like(half_map_3d_r_1) # float/real + counts_3d_updated_2 = np.zeros_like(half_map_3d_r_2) # float/real - - # initialize - map_3d_f_updated_1 = np.zeros_like(half_map_3d_f_1) # complex - map_3d_f_updated_2 = np.zeros_like(half_map_3d_f_2) # complex - counts_3d_updated_1 = np.zeros_like(half_map_3d_r_1) # float/real - counts_3d_updated_2 = np.zeros_like(half_map_3d_r_2) # float/real - - for particle_idx in range(particles_1_f.shape[0]): + for particle_idx in range(particles_f_1.shape[0]): ctf_1 = ctfs_1[particle_idx] ctf_2 = ctfs_2[particle_idx] - particle_f_1 = particles_f_1[particle_idx] - particle_f_2 = particles_f_2[particle_idx] + # particle_f_1 = particles_f_1[particle_idx] + # particle_f_2 = particles_f_2[particle_idx] def do_wiener_filter(projection, ctf, small_number): - wfilter = ctf/(ctf*ctf+small_number) - projection_wfilter_f = projection*w_filter + wfilter = ctf / (ctf * ctf + small_number) + projection_wfilter_f = projection * wfilter return projection_wfilter_f - - particle_f_deconv_1 = do_wiener_filter(particles_f_1, ctf_1) particle_f_deconv_1 = do_wiener_filter(particles_f_1, ctf_1) + particle_f_deconv_2 = do_wiener_filter(particles_f_1, ctf_1) - slices_conv_ctfs_1 = do_conv_ctf(slices_1, ctf_1) # all slices get convolved with the ctf for the particle + # all slices get convolved with the ctf for the particle + slices_conv_ctfs_1 = do_conv_ctf(slices_1, ctf_1) slices_conv_ctfs_2 = do_conv_ctf(slices_2, ctf_2) - bayes_factors_1 = do_bayesean_weights(particles_1_f[particle_idx], slices_conv_ctfs_1) - bayes_factors_2 = do_bayesean_weights(particles_2_f[particle_idx], slices_conv_ctfs_2) + bayes_factors_1 = do_bayesian_weights( + particles_f_1[particle_idx], slices_conv_ctfs_1 + ) + bayes_factors_2 = do_bayesian_weights( + particles_f_2[particle_idx], slices_conv_ctfs_2 + ) - def do_insert_slice(slice_real,xyz,n_pix): + def do_insert_slice(slice_real, xyz, n_pix): """ - Update map_3d_f_updated with values from slice. Requires interpolation of off grid - see "Insert Fourier slices" in https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/fourier_slice_2D_3D_with_trilinear.ipynb - # TODO: vectorize so can take in many slices; - # i.e. do the compoutation in a vetorized way and return inserted_slices_3d, counts_3d of shape (n_slice,n_pix,n_pix,n_pix) - - Input - ___ - - slice - type array of shape (n_pix,n_pix) - dtype float32 or float64. real since imaginary and real part done separately - xyz - type array of shape (n_pix**2,3) - volume_3d_shape: float n_pix - - - - - Return - ___ - inserted_slice_3d - count_3d + Update map_3d_f_updated with values from slice. + Requires interpolation of off grid + see "Insert Fourier slices" in + https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/fourier_slice_2D_3D_with_trilinear.ipynb + # TODO: vectorize so can take in many slices; + # i.e. do the compoutation in a vetorized way and + # return inserted_slices_3d, counts_3d of shape + # (n_slice,n_pix,n_pix,n_pix) + + Parameters + ---------- + slice_real : float64 arr + Shape (n_pix, n_pix) + xyz : arr + Shape (n_pix**2, 3) + n_pix : int + Number of pixels + + Returns + ------- + inserted_slice_3d : float64 arr + Slice to insert. + Shape (n_pix, n_pix) + count_3d : arr + shape (n_pix?, n_pix, n_pix) """ - volume_3d = np.zeros((n_pix,n_pix,n_pix)) - # TODO: write insertion code. use linear interpolation (order of interpolation kernel) so not expensive. - # nearest neightbors cheaper, but we can afford to do better than that - - return inserted_slice_3d, count_3d + # volume_3d = np.zeros((n_pix,n_pix,n_pix)) + # TODO: write insertion code. use linear interpolation + # (order of interpolation kernel) so not expensive. + # nearest neightbors cheaper, but want better than that + # return inserted_slice_3d, count_3d + # TODO: remove placeholder + return slice_real, xyz, n_pix for one_slice_idx in range(bayes_factors_1.shape[0]): - xyz = xyz_rotated[one_slice_idx] - inserted_slice_3d_r, count_3d_r = do_insert_slice(particle_f_deconv_1.real,xyz,volume_3d) # if this can be vectorized, can avoid loop over slices - inserted_slice_3d_i, count_3d_i = do_insert_slice(particle_f_deconv_1.imag,xyz,volume_3d) # if this can be vectorized, can avoid loop over slices - map_3d_f_updated_1 += inserted_slice_3d_r + 1j*inserted_slice_3d_i + xyz = xyz_rotated[one_slice_idx] + + # if this can be vectorized, can avoid loop over slices + inserted_slice_3d_r, count_3d_r = do_insert_slice( + particle_f_deconv_1.real, xyz, np.zeros((n_pix, n_pix, n_pix)) + ) + inserted_slice_3d_i, count_3d_i = do_insert_slice( + particle_f_deconv_1.imag, xyz, np.zeros((n_pix, n_pix, n_pix)) + ) + + imag = 1j * inserted_slice_3d_i + map_3d_f_updated_1 += inserted_slice_3d_r + imag counts_3d_updated_1 += count_3d_r + count_3d_i - + for one_slice_idx in range(bayes_factors_2.shape[0]): - xyz = xyz_rotated[one_slice_idx] - inserted_slice_3d_r, count_3d_r = do_insert_slice(particle_f_deconv_2.real,xyz,volume_3d) # if this can be vectorized, can avoid loop over slices - inserted_slice_3d_i, count_3d_i = do_insert_slice(particle_f_deconv_2.imag,xyz,volume_3d) # if this can be vectorized, can avoid loop over slices - map_3d_f_updated_2 += inserted_slice_3d_r + 1j*inserted_slice_3d_i + xyz = xyz_rotated[one_slice_idx] + + # if this can be vectorized, can avoid loop over slices + inserted_slice_3d_r, count_3d_r = do_insert_slice( + particle_f_deconv_2.real, xyz, np.zeros((n_pix, n_pix, n_pix)) + ) + inserted_slice_3d_i, count_3d_i = do_insert_slice( + particle_f_deconv_2.imag, xyz, np.zeros((n_pix, n_pix, n_pix)) + ) + imag = 1j * inserted_slice_3d_i + map_3d_f_updated_2 += inserted_slice_3d_r + imag counts_3d_updated_2 += count_3d_r + count_3d_i - # apply noise model # half_map_1, half_map_2 come from doing the above independently # filter by noise estimate (e.g. multiply both half maps by FSC) - def do_fsc(map_3d_f_1,map_3d_f_2): + def do_fsc(map_3d_f_1, map_3d_f_2): """ + Compute Fourier shell correlation. Estimate noise from half maps - for now do noise estimate as FSC between half maps - - """ - # TODO: write fast vectorized fsc from code snippets in - # https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/fsc.ipynb - # https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/mFSC.ipynb - # https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/guinier_fsc_sharpen.ipynb - n_pix = map_3d_f_1.shape[0] - fsc_1d = np.ones(n_pix//2) - return noise_estimate + Parameters + ---------- + map_3d_f_1 : arr + Shape (n_pix,n_pix,n_pix) + map_3d_f_2 : arr + Shape (n_pix,n_pix,n_pix) - fsc_1d = do_estimate_noise(map_3d_f_updated_1,map_3d_f_updated_2) + Returns + ------- + fsc_1d_1 : arr + Noise estimates for map 1. + Shape (n_pix // 2,) + fsc_1d_2 : arr + Noise estimates for map 2. + Shape (n_pix // 2,) + """ + # TODO: write fast vectorized fsc from code snippets in + # https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/fsc.ipynb + # https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/mFSC.ipynb + # https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/guinier_fsc_sharpen.ipynb + n_pix_1 = map_3d_f_1.shape[0] + n_pix_2 = map_3d_f_2.shape[0] + fsc_1d_1 = np.ones(n_pix_1 // 2) + fsc_1d_2 = np.ones(n_pix_2 // 2) + return fsc_1d_1, fsc_1d_2 + + fsc_1d = do_fsc(map_3d_f_updated_1, map_3d_f_updated_2) def do_expand_1d_3d(arr_1d): - n_pix = arr_1d.shape[0]*2 - arr_3d = np.ones((n_pix,n_pix,n_pix)) + """ + Expand 1D array data into spherical shell + + Parameters + ---------- + arr_1d : arr + Shape (n_pix // 2) + + Returns + ------- + arr_3d : arr + Shape (spherical coords) + """ + n_pix = arr_1d.shape[0] * 2 + arr_3d = np.ones((n_pix, n_pix, n_pix)) # TODO: arr_1d fsc_1d to 3d (spherical shells) return arr_3d fsc_3d = do_expand_1d_3d(fsc_1d) - + # multiplicative filter on maps with fsc # The FSC is 1D, one number per spherical shells - # it can be expanded back to a multiplicative filter of the same shape as the maps - map_3d_f_filtered_1 = map_3d_f_updated_1*fsc_3d - map_3d_f_filtered_2 = map_3d_f_updated_2*fsc_3d + # it can be expanded back to a multiplicative filter + # of the same shape as the maps + map_3d_f_filtered_1 = map_3d_f_updated_1 * fsc_3d + map_3d_f_filtered_2 = map_3d_f_updated_2 * fsc_3d # update iteration half_map_3d_f_1 = map_3d_f_filtered_1 half_map_3d_f_2 = map_3d_f_filtered_2 # final map - fsc_1d = do_estimate_noise(half_map_3d_f_1,half_map_3d_f_2) + fsc_1d = do_fsc(half_map_3d_f_1, half_map_3d_f_2) fsc_3d = do_expand_1d_3d(fsc_1d) - map_3d_f_final = (half_map_3d_f_1 + half_map_3d_f_2 / 2)*fsc_3d + map_3d_f_final = (half_map_3d_f_1 + half_map_3d_f_2 / 2) * fsc_3d map_3d_r_final = do_ifft(map_3d_f_final) half_map_3d_r_1 = do_ifft(half_map_3d_f_1) half_map_3d_r_2 = do_ifft(half_map_3d_f_2) return map_3d_r_final, half_map_3d_r_1, half_map_3d_r_2, fsc_1d - - - - - - - - - diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 6ce316a..51e5fdf 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -1,5 +1,5 @@ def test_do_iterative_refinement(): - n_pix = 64 + n_pix = 64 map_3d_init = np.random.normal(size=n_pix**3).reshape(n_pix,n_pix,n_pix) particles = np.random.normal(size=n_pix**2).reshape(n_pix,n_pix) map_3d_r_final, half_map_3d_r_1, half_map_3d_r_2, fsc_1d = iterative_refinement(map_3d_init, particles) From b027987f54544ead5f2cbb97dd10b8b0c0e92663 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Sun, 20 Mar 2022 23:34:39 +0000 Subject: [PATCH 006/148] Format code with black and isort --- iterative_refinement/expectation_maximization.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index 5380c3e..f4e68b9 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -150,7 +150,7 @@ def generate_xy_plane(n_pix): # TODO: # https://github.com/geoffwoollard/compSPI/blob/stash_simulate/src/simulate.py#L96 - xy_plane = np.ones((n_pix ** 2, 3)) + xy_plane = np.ones((n_pix**2, 3)) return xy_plane xy_plane = generate_xy_plane(n_pix) @@ -183,7 +183,7 @@ def do_slices(map_3d_f, rots): n_rotations = rots.shape[0] # TODO: map_values interpolation, calculate from map, rots xyz_rotated = np.ones_like(xy_plane) - size = n_rotations * n_pix ** 2 + size = n_rotations * n_pix**2 slices = np.random.normal(size=size) slices.reshape(n_rotations, n_pix, n_pix) return slices, xyz_rotated From 26dcf795b910623440f09dae6e4494f35667bb35 Mon Sep 17 00:00:00 2001 From: jed Date: Mon, 21 Mar 2022 16:41:08 -0700 Subject: [PATCH 007/148] Update docstrings --- .../expectation_maximization.py | 118 ++++++++++-------- 1 file changed, 67 insertions(+), 51 deletions(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index f4e68b9..8b9b507 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -5,6 +5,7 @@ import numpy as np from compSPI.transforms import do_fft, do_ifft +from simSPI.simSPI.transfer import eval_ctf def do_iterative_refinement(map_3d_init, particles, ctf_info): @@ -16,73 +17,76 @@ def do_iterative_refinement(map_3d_init, particles, ctf_info): ---------- map_3d_init : arr Initial particle map. - Shape (n_pix,n_pix,n_pix) + Shape (n_pix, n_pix, n_pix) particles : arr Particles to be reconstructed. - Shape (n_pix,n_pix) + Shape (n_particles, n_pix, n_pix) + ctf_info : list of dicts + Each dict contains CTF k,v pairs per particle. + Shape (n_particles,) Returns ------- map_3d_update : arr Current iteration of map. Shape (n_pix, n_pix, n_pix) - map_3d_final : arr Final updated map. - Shape (n_pix,n_pix,n_pix) + Shape (n_pix, n_pix, n_pix) half_map_3d_final_1 : arr - Shape (n_pix,n_pix,n_pix) + Shape (n_pix, n_pix, n_pix) half_map_3d_final_2 : arr - Shape (n_pix,n_pix,n_pix) + Shape (n_pix, n_pix, n_pix) fsc_1d : arr Final one dimensional fourier shell correlation. - Shape (n_pix//2,) + Shape (n_pix // 2,) """ def do_split(arr): """ - Split distribution of particles into two distinct sets for - statistical validation purposes. + Split array into two halves along 0th axis. Parameters ---------- arr : arr - Shape (n_pix,n_pix,n_pix) + Shape (n_pix, n_pix, n_pix) Returns ------- arr1 : arr - Shape (n_pix // 2,n_pix,n_pix) + Shape (n_pix // 2, n_pix, n_pix) arr2: arr - Shape (n_pix // 2,n_pix,n_pix) + Shape (n_pix // 2, n_pix, n_pix) """ idx_half = arr.shape[0] // 2 arr_1, arr_2 = arr[:idx_half], arr[idx_half:] - assert arr_1.shape[0] == arr_2.shape[0] - return arr_1, arr_2 - particles_1, particles_2 = do_split(particles) + if arr_1.shape[0] != arr_2.shape[0]: + arr_2 = arr[idx_half:2*idx_half] + + return arr_1, arr_2 def do_build_ctf(ctf_params): """ - Build 2D array of CTF from contrast transfer - function parameters. + Build 2D array of evaluated CTFs from inputted + CTF parameters for each particle. Parameters ---------- ctf_params : list of dicts Each dict contains CTF k,v pairs per particle. - Shape (n_pix,) + Shape (n_particles,) Returns ------- ctfs : arr - Shape (n_ctfs,n_pix,n_pix) + Shape (n_ctfs, n_pix, n_pix) """ n_ctfs = len(ctf_params) - # TODO: see simSPI.transfer - # https://github.com/compSPI/simSPI/blob/master/simSPI/transfer.py#L57 - ctfs = np.ones((n_ctfs, n_pix, n_pix)) + ctfs = [] + + for i in range(n_ctfs): + ctfs.append(eval_ctf(**ctf_params[i])) return ctfs @@ -91,8 +95,9 @@ def do_build_ctf(ctf_params): # work in Fourier space. as particles stay in Fourier space the whole time. # they are experimental measurements and are fixed in the algorithm - particles_f_1 = do_fft(particles_1) - particles_f_2 = do_fft(particles_2) + particles_1, particles_2 = do_split(particles) + particles_f_1 = do_fft(particles_1, d=3) + particles_f_2 = do_fft(particles_2, d=3) n_pix = map_3d_init.shape[0] @@ -100,14 +105,16 @@ def do_build_ctf(ctf_params): half_map_3d_r_1, half_map_3d_r_2 = map_3d_init, map_3d_init.copy() - for iteration in range(max_n_iters): + for _ in range(max_n_iters): + # TODO: implement 3D fft using torch half_map_3d_f_1 = do_fft(half_map_3d_r_1, d=3) half_map_3d_f_2 = do_fft(half_map_3d_r_2, d=3) def grid_SO3_uniform(n_rotations): """ - Uniformly gridded SO(3) rotations. + Generate a discrete set of uniformly distributed rotations + across SO(3). Parameters ---------- @@ -119,12 +126,7 @@ def grid_SO3_uniform(n_rotations): rots : arr List of rotations. Shape (n_rotations, 3, 3) - """ - # TODO: sample over the sphere at given granularity. - # easy: draw uniform samples of rotations on sphere. lots of code for this all over the internet. quick solution in geomstats - # harder: draw samples around some rotation using ProjectedNormal distribution (ask Geoff) - # unknown difficulty: make a regular grid of SO(3) at given granularity. Khanh says non-trivial. rots = np.ones((n_rotations, 3, 3)) return rots @@ -165,7 +167,7 @@ def do_slices(map_3d_f, rots): Parameters ---------- map_3d_f : arr - Shape (n_pix,n_pix,n_pix) + Shape (n_pix, n_pix, n_pix) rots : arr List of rotations. Shape (n_rotations, 3, 3) @@ -183,7 +185,7 @@ def do_slices(map_3d_f, rots): n_rotations = rots.shape[0] # TODO: map_values interpolation, calculate from map, rots xyz_rotated = np.ones_like(xy_plane) - size = n_rotations * n_pix**2 + size = n_rotations * n_pix ** 2 slices = np.random.normal(size=size) slices.reshape(n_rotations, n_pix, n_pix) return slices, xyz_rotated @@ -193,7 +195,8 @@ def do_slices(map_3d_f, rots): def do_conv_ctf(slice, ctf): """ - Apply CTF to projection + Apply CTF to projected slice by convolution. + slice : arr Slice of map_3d_f. Corresponds to Fourier transform of projection of rotated map_3d_f. @@ -253,6 +256,23 @@ def do_bayesian_weights(particle, slices): # particle_f_2 = particles_f_2[particle_idx] def do_wiener_filter(projection, ctf, small_number): + """ + Apply Wiener filter to particle projection. + + Parameters + ---------- + projection : arr + Shape (n_pix, n_pix) + ctf : arr + Shape (n_pix, n_pix) + small_number : float + Used for tuning Wiener filter. + + Returns + ------- + projection_wfilter_f : arr + Shape (n_pix, n_pix) the filtered projection. + """ wfilter = ctf / (ctf * ctf + small_number) projection_wfilter_f = projection * wfilter return projection_wfilter_f @@ -273,31 +293,27 @@ def do_wiener_filter(projection, ctf, small_number): def do_insert_slice(slice_real, xyz, n_pix): """ - Update map_3d_f_updated with values from slice. - Requires interpolation of off grid - see "Insert Fourier slices" in - https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/fourier_slice_2D_3D_with_trilinear.ipynb - # TODO: vectorize so can take in many slices; - # i.e. do the compoutation in a vetorized way and - # return inserted_slices_3d, counts_3d of shape - # (n_slice,n_pix,n_pix,n_pix) + Rotate slice and interpolate onto a 3D grid to prepare + for insertion. Parameters ---------- slice_real : float64 arr - Shape (n_pix, n_pix) + Shape (n_pix, n_pix) the slice of interest. xyz : arr - Shape (n_pix**2, 3) + Shape (n_pix**2, 3) plane corresponding to slice rotation. n_pix : int - Number of pixels + Number of pixels. Returns ------- inserted_slice_3d : float64 arr - Slice to insert. + Rotated slice in 3D voxel array. Shape (n_pix, n_pix) count_3d : arr - shape (n_pix?, n_pix, n_pix) + Voxel array to count slice presence: 1 if slice present, + otherwise 0. + shape (n_pix, n_pix, n_pix) """ @@ -346,14 +362,14 @@ def do_insert_slice(slice_real, xyz, n_pix): def do_fsc(map_3d_f_1, map_3d_f_2): """ Compute Fourier shell correlation. - Estimate noise from half maps + Estimate noise from half maps. Parameters ---------- map_3d_f_1 : arr - Shape (n_pix,n_pix,n_pix) + Shape (n_pix, n_pix, n_pix) map_3d_f_2 : arr - Shape (n_pix,n_pix,n_pix) + Shape (n_pix, n_pix, n_pix) Returns ------- @@ -378,7 +394,7 @@ def do_fsc(map_3d_f_1, map_3d_f_2): def do_expand_1d_3d(arr_1d): """ - Expand 1D array data into spherical shell + Expand 1D array data into spherical shell. Parameters ---------- From 2110a8a7fb8f88ae0c24f5ef2a6e8f4b78d254f5 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Mon, 21 Mar 2022 23:41:21 +0000 Subject: [PATCH 008/148] Format code with black and isort --- iterative_refinement/expectation_maximization.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index 8b9b507..33cf25f 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -62,7 +62,7 @@ def do_split(arr): arr_1, arr_2 = arr[:idx_half], arr[idx_half:] if arr_1.shape[0] != arr_2.shape[0]: - arr_2 = arr[idx_half:2*idx_half] + arr_2 = arr[idx_half : 2 * idx_half] return arr_1, arr_2 @@ -185,7 +185,7 @@ def do_slices(map_3d_f, rots): n_rotations = rots.shape[0] # TODO: map_values interpolation, calculate from map, rots xyz_rotated = np.ones_like(xy_plane) - size = n_rotations * n_pix ** 2 + size = n_rotations * n_pix**2 slices = np.random.normal(size=size) slices.reshape(n_rotations, n_pix, n_pix) return slices, xyz_rotated @@ -271,7 +271,7 @@ def do_wiener_filter(projection, ctf, small_number): Returns ------- projection_wfilter_f : arr - Shape (n_pix, n_pix) the filtered projection. + Shape (n_pix, n_pix) the filtered projection. """ wfilter = ctf / (ctf * ctf + small_number) projection_wfilter_f = projection * wfilter From f493da642788f3ef5589f15b8c355229b9cd6b10 Mon Sep 17 00:00:00 2001 From: Tyler Heim Date: Mon, 21 Mar 2022 17:00:41 -0700 Subject: [PATCH 009/148] Dummy tests --- tests/test_expectation_maximization.py | 101 +++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 8 deletions(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 51e5fdf..eb22b37 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -1,9 +1,94 @@ def test_do_iterative_refinement(): - n_pix = 64 - map_3d_init = np.random.normal(size=n_pix**3).reshape(n_pix,n_pix,n_pix) - particles = np.random.normal(size=n_pix**2).reshape(n_pix,n_pix) - map_3d_r_final, half_map_3d_r_1, half_map_3d_r_2, fsc_1d = iterative_refinement(map_3d_init, particles) - assert map_3d_r_final.shape == (n_pix,n_pix,n_pix) - assert fsc_1d.dtype = np.float32 - assert half_map_3d_r_1.dtype = np.float32 - assert half_map_3d_r_2.dtype = np.float32 \ No newline at end of file + n_pix = 64 + map_3d_init = np.random.normal(size=n_pix**3).reshape(n_pix,n_pix,n_pix) + particles = np.random.normal(size=n_pix**2).reshape(n_pix,n_pix) + map_3d_r_final, half_map_3d_r_1, half_map_3d_r_2, fsc_1d = iterative_refinement(map_3d_init, particles) + assert map_3d_r_final.shape == (n_pix,n_pix,n_pix) + assert fsc_1d.dtype = np.float32 + assert half_map_3d_r_1.dtype = np.float32 + assert half_map_3d_r_2.dtype = np.float32 + +def test_do_split(): + arr = np.zeros(4) + arr1, arr2 = do_split(arr) + assert arr1.shape == (2,) + assert arr2.shape == (2,) + +def test_do_build_ctf(): + ex_ctf = { + s : np.ones(2,2), + a : np.ones(2,2), + def1 : 1.0, + def2 : 1.0, + angast : 0.1, + kv : 0.1, + cs : 0.1, + bf : 0.1, + lp : 0.1 + } + ctf_params = [ex_ctf, ex_ctf] + ctfs = do_build_ctf(ctf_params) + assert ctfs.shape == (2,2,2) + +def test_grid_SO3_uniform(): + rots = grid_SO3_uniform(2) + assert rots.shape == (2,3,3) + +def test_generate_xy_plane(): + xy_plane = generate_xy_plane(2) + assert xy_plane.shape == (2,2,3) + +def test_do_slices(): + map_3d = np.ones(2,2,2) + rots = test_grid_SO3_uniform(2) + xy_plane = generate_xy_plane(2) + + slices, xyz_rotated = do_slices(map_3d, rots) + + assert slices.shape == (2,2,2) + assert xyz_rotated.shape == (2,2,3) + +def test_do_conv_ctf(): + particle_slice = np.ones(2,2) + ctf = np.ones(2,2) + convolved = do_conv_ctf(particle_slice, ctf) + + assert convolved.shape == (2,2) + +def test_do_bayesian_weights(): + particle = np.ones(1,2,2) + slices = np.ones(2,2,2) + bayesian_weights = do_bayesian_weights(particle, slices) + + assert bayesian_weights.shape == (2,) + +def test_do_wiener_filter(): + projection = np.ones(2,2) + ctf = np.zeros(2,2) + small_number = 0.01 + + projection_wfilter_f = do_wiener_filter(projection, ctf, small_number) + assert projection_wfilter_f.shape == (2,2) + +def test_do_insert_slice(): + particle_slice = np.ones(2,2) + xyz = generate_xy_plane(2,2) + n_pix = 2 + + inserted, count = do_insert_slice(particle_slice, xyz, n_pix) + assert inserted.shape == (2,2,2) + assert count.shape == (2,2,2) + +def test_do_fsc(): + map_1 = np.ones(2,2,2) + map_2 = np.ones(2,2,2) + + fsc_1, fsc_2 = do_fsc(map_1, map_2) + assert fsc_1.shape == (1,) + assert fsc_2.shape == (1,) + +def test_do_expand_1d_3d(): + arr1d = np.ones(1) + spherical = do_expand_1d_3d(arr1d) + + assert spherical.shape == (2,2,2) \ No newline at end of file From 00b8afbe446020e6c0e19664d7489a19b3e2f170 Mon Sep 17 00:00:00 2001 From: jed Date: Mon, 21 Mar 2022 18:14:10 -0700 Subject: [PATCH 010/148] docstrings and tests --- .github/auto-assign.yml | 4 +- .../expectation_maximization.py | 582 +++++++----------- tests/test_expectation_maximization.py | 82 ++- 3 files changed, 268 insertions(+), 400 deletions(-) diff --git a/.github/auto-assign.yml b/.github/auto-assign.yml index 115af98..a0a74eb 100644 --- a/.github/auto-assign.yml +++ b/.github/auto-assign.yml @@ -5,10 +5,10 @@ addReviewers: true addAssignees: false # A list of reviewers to be added to pull requests (GitHub user name) -reviewers: +reviewers: - fredericpoitevin -# A list of keywords to be skipped the process that add reviewers if pull requests include it +# A list of keywords to be skipped the process that add reviewers if pull requests include it skipKeywords: - wip diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index 33cf25f..9366334 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -5,10 +5,10 @@ import numpy as np from compSPI.transforms import do_fft, do_ifft -from simSPI.simSPI.transfer import eval_ctf +from simSPI.transfer import eval_ctf -def do_iterative_refinement(map_3d_init, particles, ctf_info): +class IterativeRefinement: """ Performs interative refimenent in a Bayesian expectation maximization setting, i.e. maximum a posteriori estimation. @@ -41,8 +41,13 @@ def do_iterative_refinement(map_3d_init, particles, ctf_info): Final one dimensional fourier shell correlation. Shape (n_pix // 2,) """ + def __init__(self, map_3d_init, particles, ctf_info, max_itr=7): + self.map_3d_init = map_3d_init + self.particles = particles + self.ctf_info = ctf_info + self.max_itr = max_itr - def do_split(arr): + def split_array(self, arr): """ Split array into two halves along 0th axis. @@ -62,374 +67,239 @@ def do_split(arr): arr_1, arr_2 = arr[:idx_half], arr[idx_half:] if arr_1.shape[0] != arr_2.shape[0]: - arr_2 = arr[idx_half : 2 * idx_half] + arr_2 = arr[idx_half:2 * idx_half] return arr_1, arr_2 - def do_build_ctf(ctf_params): + def build_ctf_array(self): """ Build 2D array of evaluated CTFs from inputted CTF parameters for each particle. - Parameters - ---------- - ctf_params : list of dicts - Each dict contains CTF k,v pairs per particle. - Shape (n_particles,) - Returns ------- ctfs : arr Shape (n_ctfs, n_pix, n_pix) """ - n_ctfs = len(ctf_params) + n_ctfs = len(self.ctf_info) ctfs = [] for i in range(n_ctfs): - ctfs.append(eval_ctf(**ctf_params[i])) + ctfs.append(eval_ctf(**self.ctf_info[i])) return ctfs - ctfs = do_build_ctf(ctf_info) - ctfs_1, ctfs_2 = do_split(ctfs) - - # work in Fourier space. as particles stay in Fourier space the whole time. - # they are experimental measurements and are fixed in the algorithm - particles_1, particles_2 = do_split(particles) - particles_f_1 = do_fft(particles_1, d=3) - particles_f_2 = do_fft(particles_2, d=3) - - n_pix = map_3d_init.shape[0] - - max_n_iters = 7 # in practice programs using 3-10 iterations. - - half_map_3d_r_1, half_map_3d_r_2 = map_3d_init, map_3d_init.copy() - - for _ in range(max_n_iters): - - # TODO: implement 3D fft using torch - half_map_3d_f_1 = do_fft(half_map_3d_r_1, d=3) - half_map_3d_f_2 = do_fft(half_map_3d_r_2, d=3) - - def grid_SO3_uniform(n_rotations): - """ - Generate a discrete set of uniformly distributed rotations - across SO(3). - - Parameters - ---------- - n_rotations : int - Number of rotations - - Returns - ------- - rots : arr - List of rotations. - Shape (n_rotations, 3, 3) - """ - rots = np.ones((n_rotations, 3, 3)) - return rots - - n_rotations = 1000 - rots = grid_SO3_uniform(n_rotations) - - def generate_xy_plane(n_pix): - """ - Generate xy plane. - - Parameters - ---------- - n_pix : int - Number of pixels - - Returns - xy_plane : arr - Array describing xy plane in space. - Shape (n_pix**2, 3) - """ - - # See how meshgrid and generate coordinates functions used - # TODO: - # https://github.com/geoffwoollard/compSPI/blob/stash_simulate/src/simulate.py#L96 - - xy_plane = np.ones((n_pix**2, 3)) - return xy_plane - - xy_plane = generate_xy_plane(n_pix) - - def do_slices(map_3d_f, rots): - """ - Generates slice coordinates by rotating xy plane. - Interpolate values from map_3d_f onto 3D coordinates. - TODO: See how scipy map_values used to interpolate in - https://github.com/geoffwoollard/compSPI/blob/stash_simulate/src/simulate.py#L111 - - Parameters - ---------- - map_3d_f : arr - Shape (n_pix, n_pix, n_pix) - rots : arr - List of rotations. - Shape (n_rotations, 3, 3) - - Returns - ------- - slices : arr - Slice of map_3d_f. Corresponds to Fourier transform - of projection of rotated map_3d_f. - Shape (n_rotations, n_pix, n_pix) - xyz_rotated : arr - Rotated xy plane. - Shape (n_pix**2, 3) - """ - n_rotations = rots.shape[0] - # TODO: map_values interpolation, calculate from map, rots - xyz_rotated = np.ones_like(xy_plane) - size = n_rotations * n_pix**2 - slices = np.random.normal(size=size) - slices.reshape(n_rotations, n_pix, n_pix) - return slices, xyz_rotated - - slices_1, xyz_rotated = do_slices(half_map_3d_f_1, rots) - slices_2, xyz_rotated = do_slices(half_map_3d_f_2, rots) - - def do_conv_ctf(slice, ctf): - """ - Apply CTF to projected slice by convolution. - - slice : arr - Slice of map_3d_f. Corresponds to Fourier transform - of projection of rotated map_3d_f. - Shape (n_pix, n_pix) - ctf : arr - CTF parameters for particle. - Shape (n_pix,n_pix) - """ - - # TODO: vectorize and have shape match - projection_f_conv_ctf = ctf * slice - return projection_f_conv_ctf - - def do_bayesian_weights(particle, slices): - """ - Compute Bayesian weights of particle to slice - under Gaussian white noise model. - - Parameters - ---------- - particle : arr - Shape (n_pix // 2,n_pix,n_pix) - - slices : complex64 arr - Shape (n_slices, n_pix, n_pix) - - Returns - ------- - bayesian_weights : float64 arr - Shape (n_slices,) - """ - n_slices = slices.shape[0] - # particle_l2 = np.linalg.norm(particle, ord='fro')**2 - # slices_l2 = np.linalg.norm(slices, axis=(1, 2), ord='fro')**2 - - # TODO: check right ax. should n_slices l2 norms, 1 for each slice - # can precompute slices_l2 and keep for all particles - # if slices the same for different particles - - # corr = np.zeros(slices) - # a_slice_corr = particle.dot(slices) - # |particle|^2 - particle.dot(a_slice) + |a_slice|^2 - # see Sigrowth et al and Nelson for how to get bayes factors - bayes_factors = np.random.normal(n_slices) - return bayes_factors - - # Initialize - map_3d_f_updated_1 = np.zeros_like(half_map_3d_f_1) # complex - map_3d_f_updated_2 = np.zeros_like(half_map_3d_f_2) # complex - counts_3d_updated_1 = np.zeros_like(half_map_3d_r_1) # float/real - counts_3d_updated_2 = np.zeros_like(half_map_3d_r_2) # float/real - - for particle_idx in range(particles_f_1.shape[0]): - ctf_1 = ctfs_1[particle_idx] - ctf_2 = ctfs_2[particle_idx] - # particle_f_1 = particles_f_1[particle_idx] - # particle_f_2 = particles_f_2[particle_idx] - - def do_wiener_filter(projection, ctf, small_number): - """ - Apply Wiener filter to particle projection. - - Parameters - ---------- - projection : arr - Shape (n_pix, n_pix) - ctf : arr - Shape (n_pix, n_pix) - small_number : float - Used for tuning Wiener filter. - - Returns - ------- - projection_wfilter_f : arr - Shape (n_pix, n_pix) the filtered projection. - """ - wfilter = ctf / (ctf * ctf + small_number) - projection_wfilter_f = projection * wfilter - return projection_wfilter_f - - particle_f_deconv_1 = do_wiener_filter(particles_f_1, ctf_1) - particle_f_deconv_2 = do_wiener_filter(particles_f_1, ctf_1) - - # all slices get convolved with the ctf for the particle - slices_conv_ctfs_1 = do_conv_ctf(slices_1, ctf_1) - slices_conv_ctfs_2 = do_conv_ctf(slices_2, ctf_2) - - bayes_factors_1 = do_bayesian_weights( - particles_f_1[particle_idx], slices_conv_ctfs_1 - ) - bayes_factors_2 = do_bayesian_weights( - particles_f_2[particle_idx], slices_conv_ctfs_2 - ) - - def do_insert_slice(slice_real, xyz, n_pix): - """ - Rotate slice and interpolate onto a 3D grid to prepare - for insertion. - - Parameters - ---------- - slice_real : float64 arr - Shape (n_pix, n_pix) the slice of interest. - xyz : arr - Shape (n_pix**2, 3) plane corresponding to slice rotation. - n_pix : int - Number of pixels. - - Returns - ------- - inserted_slice_3d : float64 arr - Rotated slice in 3D voxel array. - Shape (n_pix, n_pix) - count_3d : arr - Voxel array to count slice presence: 1 if slice present, - otherwise 0. - shape (n_pix, n_pix, n_pix) - - """ - - # volume_3d = np.zeros((n_pix,n_pix,n_pix)) - # TODO: write insertion code. use linear interpolation - # (order of interpolation kernel) so not expensive. - # nearest neightbors cheaper, but want better than that - - # return inserted_slice_3d, count_3d - # TODO: remove placeholder - return slice_real, xyz, n_pix - - for one_slice_idx in range(bayes_factors_1.shape[0]): - xyz = xyz_rotated[one_slice_idx] - - # if this can be vectorized, can avoid loop over slices - inserted_slice_3d_r, count_3d_r = do_insert_slice( - particle_f_deconv_1.real, xyz, np.zeros((n_pix, n_pix, n_pix)) - ) - inserted_slice_3d_i, count_3d_i = do_insert_slice( - particle_f_deconv_1.imag, xyz, np.zeros((n_pix, n_pix, n_pix)) - ) - - imag = 1j * inserted_slice_3d_i - map_3d_f_updated_1 += inserted_slice_3d_r + imag - counts_3d_updated_1 += count_3d_r + count_3d_i - - for one_slice_idx in range(bayes_factors_2.shape[0]): - xyz = xyz_rotated[one_slice_idx] - - # if this can be vectorized, can avoid loop over slices - inserted_slice_3d_r, count_3d_r = do_insert_slice( - particle_f_deconv_2.real, xyz, np.zeros((n_pix, n_pix, n_pix)) - ) - inserted_slice_3d_i, count_3d_i = do_insert_slice( - particle_f_deconv_2.imag, xyz, np.zeros((n_pix, n_pix, n_pix)) - ) - imag = 1j * inserted_slice_3d_i - map_3d_f_updated_2 += inserted_slice_3d_r + imag - counts_3d_updated_2 += count_3d_r + count_3d_i - - # apply noise model - # half_map_1, half_map_2 come from doing the above independently - # filter by noise estimate (e.g. multiply both half maps by FSC) - - def do_fsc(map_3d_f_1, map_3d_f_2): - """ - Compute Fourier shell correlation. - Estimate noise from half maps. - - Parameters - ---------- - map_3d_f_1 : arr - Shape (n_pix, n_pix, n_pix) - map_3d_f_2 : arr - Shape (n_pix, n_pix, n_pix) - - Returns - ------- - fsc_1d_1 : arr - Noise estimates for map 1. - Shape (n_pix // 2,) - fsc_1d_2 : arr - Noise estimates for map 2. - Shape (n_pix // 2,) - """ - # TODO: write fast vectorized fsc from code snippets in - # https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/fsc.ipynb - # https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/mFSC.ipynb - # https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/guinier_fsc_sharpen.ipynb - n_pix_1 = map_3d_f_1.shape[0] - n_pix_2 = map_3d_f_2.shape[0] - fsc_1d_1 = np.ones(n_pix_1 // 2) - fsc_1d_2 = np.ones(n_pix_2 // 2) - return fsc_1d_1, fsc_1d_2 - - fsc_1d = do_fsc(map_3d_f_updated_1, map_3d_f_updated_2) - - def do_expand_1d_3d(arr_1d): - """ - Expand 1D array data into spherical shell. - - Parameters - ---------- - arr_1d : arr - Shape (n_pix // 2) - - Returns - ------- - arr_3d : arr - Shape (spherical coords) - """ - n_pix = arr_1d.shape[0] * 2 - arr_3d = np.ones((n_pix, n_pix, n_pix)) - # TODO: arr_1d fsc_1d to 3d (spherical shells) - return arr_3d - - fsc_3d = do_expand_1d_3d(fsc_1d) - - # multiplicative filter on maps with fsc - # The FSC is 1D, one number per spherical shells - # it can be expanded back to a multiplicative filter - # of the same shape as the maps - map_3d_f_filtered_1 = map_3d_f_updated_1 * fsc_3d - map_3d_f_filtered_2 = map_3d_f_updated_2 * fsc_3d - - # update iteration - half_map_3d_f_1 = map_3d_f_filtered_1 - half_map_3d_f_2 = map_3d_f_filtered_2 - - # final map - fsc_1d = do_fsc(half_map_3d_f_1, half_map_3d_f_2) - fsc_3d = do_expand_1d_3d(fsc_1d) - map_3d_f_final = (half_map_3d_f_1 + half_map_3d_f_2 / 2) * fsc_3d - map_3d_r_final = do_ifft(map_3d_f_final) - half_map_3d_r_1 = do_ifft(half_map_3d_f_1) - half_map_3d_r_2 = do_ifft(half_map_3d_f_2) - - return map_3d_r_final, half_map_3d_r_1, half_map_3d_r_2, fsc_1d + def grid_SO3_uniform(self, n_rotations): + """ + Generate a discrete set of uniformly distributed rotations + across SO(3). + + Parameters + ---------- + n_rotations : int + Number of rotations + + Returns + ------- + rots : arr + List of rotations. + Shape (n_rotations, 3, 3) + """ + rots = np.ones((n_rotations, 3, 3)) + return rots + + def generate_xy_plane(self, n_pix): + """ + Generate xy plane. + + Parameters + ---------- + n_pix : int + Number of pixels + + Returns + xy_plane : arr + Array describing xy plane in space. + Shape (n_pix**2, 3) + """ + + # See how meshgrid and generate coordinates functions used + # TODO: + # https://github.com/geoffwoollard/compSPI/blob/stash_simulate/src/simulate.py#L96 + + xy_plane = np.ones((n_pix ** 2, 3)) + return xy_plane + + def generate_slices(self, map_3d_f, xy_plane, n_pix, rots): + """ + Generates slice coordinates by rotating xy plane. + Interpolate values from map_3d_f onto 3D coordinates. + TODO: See how scipy map_values used to interpolate in + https://github.com/geoffwoollard/compSPI/blob/stash_simulate/src/simulate.py#L111 + + Parameters + ---------- + map_3d_f : arr + Shape (n_pix, n_pix, n_pix) + rots : arr + List of rotations. + Shape (n_rotations, 3, 3) + + Returns + ------- + slices : arr + Slice of map_3d_f. Corresponds to Fourier transform + of projection of rotated map_3d_f. + Shape (n_rotations, n_pix, n_pix) + xyz_rotated : arr + Rotated xy plane. + Shape (n_pix**2, 3) + """ + n_rotations = rots.shape[0] + # TODO: map_values interpolation, calculate from map, rots + map_3d_f = np.ones_like(map_3d_f) + xyz_rotated = np.ones_like(xy_plane) + + size = n_rotations * n_pix ** 2 + slices = np.random.normal(size=size) + slices.reshape(n_rotations, n_pix, n_pix) + return slices, xyz_rotated + + def apply_ctf_to_slice(self, particle_slice, ctf): + """ + Apply CTF to projected slice by convolution. + + particle_slice : arr + Slice of map_3d_f. Corresponds to Fourier transform + of projection of rotated map_3d_f. + Shape (n_pix, n_pix) + ctf : arr + CTF parameters for particle. + Shape (n_pix,n_pix) + """ + + # TODO: vectorize and have shape match + projection_f_conv_ctf = ctf * slice + return projection_f_conv_ctf + + def compute_bayesian_weights(self, particle, slices): + """ + Compute Bayesian weights of particle to slice + under Gaussian white noise model. + + Parameters + ---------- + particle : arr + Shape (n_pix // 2,n_pix,n_pix) + + slices : complex64 arr + Shape (n_slices, n_pix, n_pix) + + Returns + ------- + bayesian_weights : float64 arr + Shape (n_slices,) + """ + n_slices = slices.shape[0] + particle = np.ones_like(particle) + bayes_factors = np.random.normal(n_slices) + return bayes_factors + + def apply_wiener_filter(self, projection, ctf, small_number): + """ + Apply Wiener filter to particle projection. + + Parameters + ---------- + projection : arr + Shape (n_pix, n_pix) + ctf : arr + Shape (n_pix, n_pix) + small_number : float + Used for tuning Wiener filter. + + Returns + ------- + projection_wfilter_f : arr + Shape (n_pix, n_pix) the filtered projection. + """ + wfilter = ctf / (ctf * ctf + small_number) + projection_wfilter_f = projection * wfilter + return projection_wfilter_f + + def insert_slice(self, slice_real, xyz, n_pix): + """ + Rotate slice and interpolate onto a 3D grid to prepare + for insertion. + + Parameters + ---------- + slice_real : float64 arr + Shape (n_pix, n_pix) the slice of interest. + xyz : arr + Shape (n_pix**2, 3) plane corresponding to slice rotation. + n_pix : int + Number of pixels. + + Returns + ------- + inserted_slice_3d : float64 arr + Rotated slice in 3D voxel array. + Shape (n_pix, n_pix) + count_3d : arr + Voxel array to count slice presence: 1 if slice present, + otherwise 0. + Shape (n_pix, n_pix, n_pix) + """ + + return slice_real, xyz, n_pix + + def compute_fsc(self, map_3d_f_1, map_3d_f_2): + """ + Compute Fourier shell correlation. + Estimate noise from half maps. + + Parameters + ---------- + map_3d_f_1 : arr + Shape (n_pix, n_pix, n_pix) + map_3d_f_2 : arr + Shape (n_pix, n_pix, n_pix) + + Returns + ------- + fsc_1d_1 : arr + Noise estimates for map 1. + Shape (n_pix // 2,) + fsc_1d_2 : arr + Noise estimates for map 2. + Shape (n_pix // 2,) + """ + # TODO: write fast vectorized fsc from code snippets in + # https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/fsc.ipynb + # https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/mFSC.ipynb + # https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/guinier_fsc_sharpen.ipynb + n_pix_1 = map_3d_f_1.shape[0] + n_pix_2 = map_3d_f_2.shape[0] + fsc_1d_1 = np.ones(n_pix_1 // 2) + fsc_1d_2 = np.ones(n_pix_2 // 2) + return fsc_1d_1, fsc_1d_2 + + def expand_1d_to_3d(self, arr_1d): + """ + Expand 1D array data into spherical shell. + + Parameters + ---------- + arr_1d : arr + Shape (n_pix // 2) + + Returns + ------- + arr_3d : arr + Shape (spherical coords) + """ + n_pix = arr_1d.shape[0] * 2 + arr_3d = np.ones((n_pix, n_pix, n_pix)) + # TODO: arr_1d fsc_1d to 3d (spherical shells) + return arr_3d diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index eb22b37..3188cbc 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -1,94 +1,92 @@ -def test_do_iterative_refinement(): - n_pix = 64 - map_3d_init = np.random.normal(size=n_pix**3).reshape(n_pix,n_pix,n_pix) - particles = np.random.normal(size=n_pix**2).reshape(n_pix,n_pix) - map_3d_r_final, half_map_3d_r_1, half_map_3d_r_2, fsc_1d = iterative_refinement(map_3d_init, particles) - assert map_3d_r_final.shape == (n_pix,n_pix,n_pix) - assert fsc_1d.dtype = np.float32 - assert half_map_3d_r_1.dtype = np.float32 - assert half_map_3d_r_2.dtype = np.float32 - -def test_do_split(): +import numpy as np + +from iterative_refinement import expectation_maximization + +def test_split_array(): arr = np.zeros(4) - arr1, arr2 = do_split(arr) + arr1, arr2 = split_array(arr) assert arr1.shape == (2,) assert arr2.shape == (2,) -def test_do_build_ctf(): - ex_ctf = { - s : np.ones(2,2), - a : np.ones(2,2), - def1 : 1.0, - def2 : 1.0, - angast : 0.1, - kv : 0.1, - cs : 0.1, - bf : 0.1, - lp : 0.1 - } - ctf_params = [ex_ctf, ex_ctf] - ctfs = do_build_ctf(ctf_params) - assert ctfs.shape == (2,2,2) + +def test_build_ctf_array(): + ex_ctf = { + 's' : np.ones(2, 2), + 'a ': np.ones(2, 2), + 'def1' : 1.0, + 'def2' : 1.0, + 'angast' : 0.1, + 'kv' : 0.1, + 'cs' : 0.1, + 'bf' : 0.1, + 'lp' : 0.1 + } + ctf_params = [ex_ctf, ex_ctf] + ctfs = build_ctf_array(ctf_params) + assert ctfs.shape == (2,2,2) + def test_grid_SO3_uniform(): rots = grid_SO3_uniform(2) assert rots.shape == (2,3,3) + def test_generate_xy_plane(): xy_plane = generate_xy_plane(2) assert xy_plane.shape == (2,2,3) -def test_do_slices(): + +def test_generate_slices(): map_3d = np.ones(2,2,2) rots = test_grid_SO3_uniform(2) xy_plane = generate_xy_plane(2) - slices, xyz_rotated = do_slices(map_3d, rots) + slices, xyz_rotated = generate_slices(map_3d, rots) assert slices.shape == (2,2,2) assert xyz_rotated.shape == (2,2,3) -def test_do_conv_ctf(): +def test_apply_ctf_to_slice(): particle_slice = np.ones(2,2) ctf = np.ones(2,2) - convolved = do_conv_ctf(particle_slice, ctf) + convolved = apply_ctf_to_slice(particle_slice, ctf) assert convolved.shape == (2,2) -def test_do_bayesian_weights(): +def test_compute_bayesian_weights(): particle = np.ones(1,2,2) slices = np.ones(2,2,2) - bayesian_weights = do_bayesian_weights(particle, slices) + bayesian_weights = compute_bayesian_weights(particle, slices) assert bayesian_weights.shape == (2,) -def test_do_wiener_filter(): +def test_apply_wiener_filter(): projection = np.ones(2,2) ctf = np.zeros(2,2) small_number = 0.01 - projection_wfilter_f = do_wiener_filter(projection, ctf, small_number) + projection_wfilter_f = apply_wiener_filter(projection, ctf, small_number) assert projection_wfilter_f.shape == (2,2) -def test_do_insert_slice(): +def test_insert_slice(): particle_slice = np.ones(2,2) xyz = generate_xy_plane(2,2) n_pix = 2 - inserted, count = do_insert_slice(particle_slice, xyz, n_pix) + inserted, count = insert_slice(particle_slice, xyz, n_pix) assert inserted.shape == (2,2,2) assert count.shape == (2,2,2) -def test_do_fsc(): +def test_compute_fsc(): map_1 = np.ones(2,2,2) map_2 = np.ones(2,2,2) - fsc_1, fsc_2 = do_fsc(map_1, map_2) + fsc_1, fsc_2 = compute_fsc(map_1, map_2) assert fsc_1.shape == (1,) assert fsc_2.shape == (1,) -def test_do_expand_1d_3d(): +def test_expand_1d_to_3d(): arr1d = np.ones(1) - spherical = do_expand_1d_3d(arr1d) + spherical = expand_1d_to_3d(arr1d) - assert spherical.shape == (2,2,2) \ No newline at end of file + assert spherical.shape == (2,2,2) From 387c8ed3a39bf5705a12bda59413a80c30d1707f Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Tue, 22 Mar 2022 01:14:23 +0000 Subject: [PATCH 011/148] Format code with black and isort --- .../expectation_maximization.py | 7 +- tests/test_expectation_maximization.py | 121 +++++++++--------- 2 files changed, 68 insertions(+), 60 deletions(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index 9366334..4a89701 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -41,6 +41,7 @@ class IterativeRefinement: Final one dimensional fourier shell correlation. Shape (n_pix // 2,) """ + def __init__(self, map_3d_init, particles, ctf_info, max_itr=7): self.map_3d_init = map_3d_init self.particles = particles @@ -67,7 +68,7 @@ def split_array(self, arr): arr_1, arr_2 = arr[:idx_half], arr[idx_half:] if arr_1.shape[0] != arr_2.shape[0]: - arr_2 = arr[idx_half:2 * idx_half] + arr_2 = arr[idx_half : 2 * idx_half] return arr_1, arr_2 @@ -127,7 +128,7 @@ def generate_xy_plane(self, n_pix): # TODO: # https://github.com/geoffwoollard/compSPI/blob/stash_simulate/src/simulate.py#L96 - xy_plane = np.ones((n_pix ** 2, 3)) + xy_plane = np.ones((n_pix**2, 3)) return xy_plane def generate_slices(self, map_3d_f, xy_plane, n_pix, rots): @@ -160,7 +161,7 @@ def generate_slices(self, map_3d_f, xy_plane, n_pix, rots): map_3d_f = np.ones_like(map_3d_f) xyz_rotated = np.ones_like(xy_plane) - size = n_rotations * n_pix ** 2 + size = n_rotations * n_pix**2 slices = np.random.normal(size=size) slices.reshape(n_rotations, n_pix, n_pix) return slices, xyz_rotated diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 3188cbc..c39cb1b 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -2,91 +2,98 @@ from iterative_refinement import expectation_maximization + def test_split_array(): - arr = np.zeros(4) - arr1, arr2 = split_array(arr) - assert arr1.shape == (2,) - assert arr2.shape == (2,) + arr = np.zeros(4) + arr1, arr2 = split_array(arr) + assert arr1.shape == (2,) + assert arr2.shape == (2,) def test_build_ctf_array(): - ex_ctf = { - 's' : np.ones(2, 2), - 'a ': np.ones(2, 2), - 'def1' : 1.0, - 'def2' : 1.0, - 'angast' : 0.1, - 'kv' : 0.1, - 'cs' : 0.1, - 'bf' : 0.1, - 'lp' : 0.1 - } - ctf_params = [ex_ctf, ex_ctf] - ctfs = build_ctf_array(ctf_params) - assert ctfs.shape == (2,2,2) + ex_ctf = { + "s": np.ones(2, 2), + "a ": np.ones(2, 2), + "def1": 1.0, + "def2": 1.0, + "angast": 0.1, + "kv": 0.1, + "cs": 0.1, + "bf": 0.1, + "lp": 0.1, + } + ctf_params = [ex_ctf, ex_ctf] + ctfs = build_ctf_array(ctf_params) + assert ctfs.shape == (2, 2, 2) def test_grid_SO3_uniform(): - rots = grid_SO3_uniform(2) - assert rots.shape == (2,3,3) + rots = grid_SO3_uniform(2) + assert rots.shape == (2, 3, 3) def test_generate_xy_plane(): - xy_plane = generate_xy_plane(2) - assert xy_plane.shape == (2,2,3) + xy_plane = generate_xy_plane(2) + assert xy_plane.shape == (2, 2, 3) def test_generate_slices(): - map_3d = np.ones(2,2,2) - rots = test_grid_SO3_uniform(2) - xy_plane = generate_xy_plane(2) + map_3d = np.ones(2, 2, 2) + rots = test_grid_SO3_uniform(2) + xy_plane = generate_xy_plane(2) + + slices, xyz_rotated = generate_slices(map_3d, rots) - slices, xyz_rotated = generate_slices(map_3d, rots) + assert slices.shape == (2, 2, 2) + assert xyz_rotated.shape == (2, 2, 3) - assert slices.shape == (2,2,2) - assert xyz_rotated.shape == (2,2,3) def test_apply_ctf_to_slice(): - particle_slice = np.ones(2,2) - ctf = np.ones(2,2) - convolved = apply_ctf_to_slice(particle_slice, ctf) - - assert convolved.shape == (2,2) + particle_slice = np.ones(2, 2) + ctf = np.ones(2, 2) + convolved = apply_ctf_to_slice(particle_slice, ctf) + + assert convolved.shape == (2, 2) + def test_compute_bayesian_weights(): - particle = np.ones(1,2,2) - slices = np.ones(2,2,2) - bayesian_weights = compute_bayesian_weights(particle, slices) + particle = np.ones(1, 2, 2) + slices = np.ones(2, 2, 2) + bayesian_weights = compute_bayesian_weights(particle, slices) + + assert bayesian_weights.shape == (2,) - assert bayesian_weights.shape == (2,) def test_apply_wiener_filter(): - projection = np.ones(2,2) - ctf = np.zeros(2,2) - small_number = 0.01 + projection = np.ones(2, 2) + ctf = np.zeros(2, 2) + small_number = 0.01 + + projection_wfilter_f = apply_wiener_filter(projection, ctf, small_number) + assert projection_wfilter_f.shape == (2, 2) - projection_wfilter_f = apply_wiener_filter(projection, ctf, small_number) - assert projection_wfilter_f.shape == (2,2) def test_insert_slice(): - particle_slice = np.ones(2,2) - xyz = generate_xy_plane(2,2) - n_pix = 2 - - inserted, count = insert_slice(particle_slice, xyz, n_pix) - assert inserted.shape == (2,2,2) - assert count.shape == (2,2,2) + particle_slice = np.ones(2, 2) + xyz = generate_xy_plane(2, 2) + n_pix = 2 + + inserted, count = insert_slice(particle_slice, xyz, n_pix) + assert inserted.shape == (2, 2, 2) + assert count.shape == (2, 2, 2) + def test_compute_fsc(): - map_1 = np.ones(2,2,2) - map_2 = np.ones(2,2,2) + map_1 = np.ones(2, 2, 2) + map_2 = np.ones(2, 2, 2) + + fsc_1, fsc_2 = compute_fsc(map_1, map_2) + assert fsc_1.shape == (1,) + assert fsc_2.shape == (1,) - fsc_1, fsc_2 = compute_fsc(map_1, map_2) - assert fsc_1.shape == (1,) - assert fsc_2.shape == (1,) def test_expand_1d_to_3d(): - arr1d = np.ones(1) - spherical = expand_1d_to_3d(arr1d) + arr1d = np.ones(1) + spherical = expand_1d_to_3d(arr1d) - assert spherical.shape == (2,2,2) + assert spherical.shape == (2, 2, 2) From be31e4c4557b9b3487ab0bf66638506f17f60b9f Mon Sep 17 00:00:00 2001 From: jed Date: Mon, 21 Mar 2022 23:55:15 -0700 Subject: [PATCH 012/148] Minor ds change. Fixed tests w/ fixture --- .../expectation_maximization.py | 7 +- tests/test_expectation_maximization.py | 134 ++++++++++-------- 2 files changed, 80 insertions(+), 61 deletions(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index 9366334..7b98a0d 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -41,6 +41,7 @@ class IterativeRefinement: Final one dimensional fourier shell correlation. Shape (n_pix // 2,) """ + def __init__(self, map_3d_init, particles, ctf_info, max_itr=7): self.map_3d_init = map_3d_init self.particles = particles @@ -59,15 +60,15 @@ def split_array(self, arr): Returns ------- arr1 : arr - Shape (n_pix // 2, n_pix, n_pix) + Shape (n_particles // 2, ...) arr2: arr - Shape (n_pix // 2, n_pix, n_pix) + Shape (n_particles // 2, ...) """ idx_half = arr.shape[0] // 2 arr_1, arr_2 = arr[:idx_half], arr[idx_half:] if arr_1.shape[0] != arr_2.shape[0]: - arr_2 = arr[idx_half:2 * idx_half] + arr_2 = arr[idx_half : 2 * idx_half] return arr_1, arr_2 diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 3188cbc..8a60cc7 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -1,92 +1,110 @@ +from cmath import exp + import numpy as np +import pytest + +from iterative_refinement import expectation_maximization as em + + +@pytest.fixture +def test_ir(): + """Instantiate IterativeRefinement class for testing.""" + ir = em.IterativeRefinement(np.ndarray(), list(), dict()) + return ir -from iterative_refinement import expectation_maximization def test_split_array(): - arr = np.zeros(4) - arr1, arr2 = split_array(arr) - assert arr1.shape == (2,) - assert arr2.shape == (2,) + arr = np.zeros(4) + arr1, arr2 = test_ir.split_array(arr) + assert arr1.shape == (2,) + assert arr2.shape == (2,) def test_build_ctf_array(): - ex_ctf = { - 's' : np.ones(2, 2), - 'a ': np.ones(2, 2), - 'def1' : 1.0, - 'def2' : 1.0, - 'angast' : 0.1, - 'kv' : 0.1, - 'cs' : 0.1, - 'bf' : 0.1, - 'lp' : 0.1 - } - ctf_params = [ex_ctf, ex_ctf] - ctfs = build_ctf_array(ctf_params) - assert ctfs.shape == (2,2,2) + ex_ctf = { + "s": np.ones(2, 2), + "a ": np.ones(2, 2), + "def1": 1.0, + "def2": 1.0, + "angast": 0.1, + "kv": 0.1, + "cs": 0.1, + "bf": 0.1, + "lp": 0.1, + } + ctf_params = [ex_ctf, ex_ctf] + ctfs = test_ir.build_ctf_array(ctf_params) + assert ctfs.shape == (2, 2, 2) def test_grid_SO3_uniform(): - rots = grid_SO3_uniform(2) - assert rots.shape == (2,3,3) + rots = test_ir.grid_SO3_uniform(2) + assert rots.shape == (2, 3, 3) def test_generate_xy_plane(): - xy_plane = generate_xy_plane(2) - assert xy_plane.shape == (2,2,3) + xy_plane = test_ir.generate_xy_plane(2) + assert xy_plane.shape == (2, 2, 3) def test_generate_slices(): - map_3d = np.ones(2,2,2) - rots = test_grid_SO3_uniform(2) - xy_plane = generate_xy_plane(2) + map_3d = np.ones(2, 2, 2) + rots = test_grid_SO3_uniform(2) + xy_plane = test_ir.generate_xy_plane(2) + + slices, xyz_rotated = test_ir.generate_slices(map_3d, rots) - slices, xyz_rotated = generate_slices(map_3d, rots) + assert xy_plane.shape == (4, 3) + assert slices.shape == (2, 2, 2) + assert xyz_rotated.shape == (2, 2, 3) - assert slices.shape == (2,2,2) - assert xyz_rotated.shape == (2,2,3) def test_apply_ctf_to_slice(): - particle_slice = np.ones(2,2) - ctf = np.ones(2,2) - convolved = apply_ctf_to_slice(particle_slice, ctf) - - assert convolved.shape == (2,2) + particle_slice = np.ones(2, 2) + ctf = np.ones(2, 2) + convolved = test_ir.apply_ctf_to_slice(particle_slice, ctf) + + assert convolved.shape == (2, 2) + def test_compute_bayesian_weights(): - particle = np.ones(1,2,2) - slices = np.ones(2,2,2) - bayesian_weights = compute_bayesian_weights(particle, slices) + particle = np.ones(1, 2, 2) + slices = np.ones(2, 2, 2) + bayesian_weights = test_ir.compute_bayesian_weights(particle, slices) + + assert bayesian_weights.shape == (2,) - assert bayesian_weights.shape == (2,) def test_apply_wiener_filter(): - projection = np.ones(2,2) - ctf = np.zeros(2,2) - small_number = 0.01 + projection = np.ones(2, 2) + ctf = np.zeros(2, 2) + small_number = 0.01 + + projection_wfilter_f = test_ir.apply_wiener_filter(projection, ctf, small_number) + assert projection_wfilter_f.shape == (2, 2) - projection_wfilter_f = apply_wiener_filter(projection, ctf, small_number) - assert projection_wfilter_f.shape == (2,2) def test_insert_slice(): - particle_slice = np.ones(2,2) - xyz = generate_xy_plane(2,2) - n_pix = 2 - - inserted, count = insert_slice(particle_slice, xyz, n_pix) - assert inserted.shape == (2,2,2) - assert count.shape == (2,2,2) + particle_slice = np.ones(2, 2) + xyz = test_ir.generate_xy_plane(2, 2) + n_pix = 2 + + inserted, count = test_ir.insert_slice(particle_slice, xyz, n_pix) + assert inserted.shape == (2, 2, 2) + assert count.shape == (2, 2, 2) + def test_compute_fsc(): - map_1 = np.ones(2,2,2) - map_2 = np.ones(2,2,2) + map_1 = np.ones(2, 2, 2) + map_2 = np.ones(2, 2, 2) + + fsc_1, fsc_2 = test_ir.compute_fsc(map_1, map_2) + assert fsc_1.shape == (1,) + assert fsc_2.shape == (1,) - fsc_1, fsc_2 = compute_fsc(map_1, map_2) - assert fsc_1.shape == (1,) - assert fsc_2.shape == (1,) def test_expand_1d_to_3d(): - arr1d = np.ones(1) - spherical = expand_1d_to_3d(arr1d) + arr1d = np.ones(1) + spherical = test_ir.expand_1d_to_3d(arr1d) - assert spherical.shape == (2,2,2) + assert spherical.shape == (2, 2, 2) From 7d423224db3dfbe5aca5381434f379ad9006f26d Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Tue, 22 Mar 2022 06:56:08 +0000 Subject: [PATCH 013/148] Format code with black and isort --- tests/test_expectation_maximization.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index dff34f2..8a60cc7 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -13,7 +13,6 @@ def test_ir(): return ir - def test_split_array(): arr = np.zeros(4) arr1, arr2 = test_ir.split_array(arr) From e3becc407e9951ec68f235eadfcf5ab1a324259b Mon Sep 17 00:00:00 2001 From: jed Date: Wed, 23 Mar 2022 13:55:04 -0700 Subject: [PATCH 014/148] Minor docstring changes --- .../expectation_maximization.py | 27 +++++++++++-------- tests/test_expectation_maximization.py | 27 ++++++++++++++----- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index bd0323c..3f8d8f2 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -55,7 +55,7 @@ def split_array(self, arr): Parameters ---------- arr : arr - Shape (n_pix, n_pix, n_pix) + Shape (n_particles, ...) Returns ------- @@ -103,7 +103,7 @@ def grid_SO3_uniform(self, n_rotations): Returns ------- rots : arr - List of rotations. + Array describing rotations. Shape (n_rotations, 3, 3) """ rots = np.ones((n_rotations, 3, 3)) @@ -116,7 +116,7 @@ def generate_xy_plane(self, n_pix): Parameters ---------- n_pix : int - Number of pixels + Number of pixels along one edge of the plane. Returns xy_plane : arr @@ -142,8 +142,13 @@ def generate_slices(self, map_3d_f, xy_plane, n_pix, rots): ---------- map_3d_f : arr Shape (n_pix, n_pix, n_pix) + xy_plane : arr + Array describing xy plane in space. + Shape (n_pix**2, 3) + n_pix : int + Number of pixels along one edge of the plane. rots : arr - List of rotations. + Array describing rotations. Shape (n_rotations, 3, 3) Returns @@ -172,7 +177,7 @@ def apply_ctf_to_slice(self, particle_slice, ctf): particle_slice : arr Slice of map_3d_f. Corresponds to Fourier transform - of projection of rotated map_3d_f. + of projection of rotated map_3d_r. Shape (n_pix, n_pix) ctf : arr CTF parameters for particle. @@ -252,10 +257,12 @@ def insert_slice(self, slice_real, xyz, n_pix): otherwise 0. Shape (n_pix, n_pix, n_pix) """ + inserted_slice_3d = slice_real + shape = xyz.shape[0] + count_3d = np.ones((shape, shape, shape)) + return inserted_slice_3d, count_3d - return slice_real, xyz, n_pix - - def compute_fsc(self, map_3d_f_1, map_3d_f_2): + def compute_fsc(self, map_3d_f_1): """ Compute Fourier shell correlation. Estimate noise from half maps. @@ -281,10 +288,8 @@ def compute_fsc(self, map_3d_f_1, map_3d_f_2): # https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/mFSC.ipynb # https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/guinier_fsc_sharpen.ipynb n_pix_1 = map_3d_f_1.shape[0] - n_pix_2 = map_3d_f_2.shape[0] fsc_1d_1 = np.ones(n_pix_1 // 2) - fsc_1d_2 = np.ones(n_pix_2 // 2) - return fsc_1d_1, fsc_1d_2 + return fsc_1d_1 def expand_1d_to_3d(self, arr_1d): """ diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index dff34f2..c2d3038 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -13,8 +13,8 @@ def test_ir(): return ir - def test_split_array(): + """Test splitting of array in two halves. """ arr = np.zeros(4) arr1, arr2 = test_ir.split_array(arr) assert arr1.shape == (2,) @@ -22,6 +22,7 @@ def test_split_array(): def test_build_ctf_array(): + """Test bulding of arbitrary CTF array.""" ex_ctf = { "s": np.ones(2, 2), "a ": np.ones(2, 2), @@ -39,16 +40,19 @@ def test_build_ctf_array(): def test_grid_SO3_uniform(): + """Test generation of rotations across SO(3).""" rots = test_ir.grid_SO3_uniform(2) assert rots.shape == (2, 3, 3) def test_generate_xy_plane(): + """Test generation of xy plane.""" xy_plane = test_ir.generate_xy_plane(2) assert xy_plane.shape == (2, 2, 3) def test_generate_slices(): + """Test generation of slices.""" map_3d = np.ones(2, 2, 2) rots = test_grid_SO3_uniform(2) xy_plane = test_ir.generate_xy_plane(2) @@ -61,6 +65,7 @@ def test_generate_slices(): def test_apply_ctf_to_slice(): + """Test convolution of slice with CTF.""" particle_slice = np.ones(2, 2) ctf = np.ones(2, 2) convolved = test_ir.apply_ctf_to_slice(particle_slice, ctf) @@ -69,6 +74,10 @@ def test_apply_ctf_to_slice(): def test_compute_bayesian_weights(): + """ + Test computation of bayesian weights under + Gaussian white noise model. + """ particle = np.ones(1, 2, 2) slices = np.ones(2, 2, 2) bayesian_weights = test_ir.compute_bayesian_weights(particle, slices) @@ -77,18 +86,22 @@ def test_compute_bayesian_weights(): def test_apply_wiener_filter(): + """Test application of Wiener filter to particle projection.""" projection = np.ones(2, 2) ctf = np.zeros(2, 2) small_number = 0.01 - projection_wfilter_f = test_ir.apply_wiener_filter(projection, ctf, small_number) + projection_wfilter_f = test_ir.apply_wiener_filter(projection, + ctf, + small_number) assert projection_wfilter_f.shape == (2, 2) def test_insert_slice(): - particle_slice = np.ones(2, 2) - xyz = test_ir.generate_xy_plane(2, 2) + """Test insertion of slice.""" n_pix = 2 + particle_slice = np.ones(n_pix, n_pix) + xyz = test_ir.generate_xy_plane(2, 2) inserted, count = test_ir.insert_slice(particle_slice, xyz, n_pix) assert inserted.shape == (2, 2, 2) @@ -96,15 +109,15 @@ def test_insert_slice(): def test_compute_fsc(): + """Test computation of FSC.""" map_1 = np.ones(2, 2, 2) - map_2 = np.ones(2, 2, 2) - fsc_1, fsc_2 = test_ir.compute_fsc(map_1, map_2) + fsc_1 = test_ir.compute_fsc(map_1) assert fsc_1.shape == (1,) - assert fsc_2.shape == (1,) def test_expand_1d_to_3d(): + """Test expansion of 1D array into spherical shell.""" arr1d = np.ones(1) spherical = test_ir.expand_1d_to_3d(arr1d) From e12673d42c52d0c114023f0906d78b49ede0ac39 Mon Sep 17 00:00:00 2001 From: jed Date: Wed, 23 Mar 2022 13:55:30 -0700 Subject: [PATCH 015/148] minor ds changes --- iterative_refinement/expectation_maximization.py | 5 ----- tests/test_expectation_maximization.py | 6 ++---- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index 3f8d8f2..d5ff5c0 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -271,17 +271,12 @@ def compute_fsc(self, map_3d_f_1): ---------- map_3d_f_1 : arr Shape (n_pix, n_pix, n_pix) - map_3d_f_2 : arr - Shape (n_pix, n_pix, n_pix) Returns ------- fsc_1d_1 : arr Noise estimates for map 1. Shape (n_pix // 2,) - fsc_1d_2 : arr - Noise estimates for map 2. - Shape (n_pix // 2,) """ # TODO: write fast vectorized fsc from code snippets in # https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/fsc.ipynb diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index c2d3038..563f3ab 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -14,7 +14,7 @@ def test_ir(): def test_split_array(): - """Test splitting of array in two halves. """ + """Test splitting of array in two halves.""" arr = np.zeros(4) arr1, arr2 = test_ir.split_array(arr) assert arr1.shape == (2,) @@ -91,9 +91,7 @@ def test_apply_wiener_filter(): ctf = np.zeros(2, 2) small_number = 0.01 - projection_wfilter_f = test_ir.apply_wiener_filter(projection, - ctf, - small_number) + projection_wfilter_f = test_ir.apply_wiener_filter(projection, ctf, small_number) assert projection_wfilter_f.shape == (2, 2) From 2429d1ba9518130d3ca3e80d4ef69bf42defa482 Mon Sep 17 00:00:00 2001 From: jed Date: Wed, 23 Mar 2022 19:52:23 -0700 Subject: [PATCH 016/148] Change class docstring --- iterative_refinement/expectation_maximization.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index d5ff5c0..8f303c9 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -10,8 +10,8 @@ class IterativeRefinement: """ - Performs interative refimenent in a Bayesian expectation - maximization setting, i.e. maximum a posteriori estimation. + Serves as a base class to perform iterative refinement + reconstruction with respect to maximum likelihood esimation. Parameters ---------- From e8503c28ae67901436dbefd351d09332f3ff5a69 Mon Sep 17 00:00:00 2001 From: jed Date: Wed, 23 Mar 2022 19:53:13 -0700 Subject: [PATCH 017/148] Remove todo's breaking deepsource --- iterative_refinement/expectation_maximization.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index 8f303c9..a3e1074 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -125,17 +125,17 @@ def generate_xy_plane(self, n_pix): """ # See how meshgrid and generate coordinates functions used - # TODO: # https://github.com/geoffwoollard/compSPI/blob/stash_simulate/src/simulate.py#L96 - xy_plane = np.ones((n_pix**2, 3)) + xy_plane = np.ones((n_pix ** 2, 3)) return xy_plane def generate_slices(self, map_3d_f, xy_plane, n_pix, rots): """ Generates slice coordinates by rotating xy plane. Interpolate values from map_3d_f onto 3D coordinates. - TODO: See how scipy map_values used to interpolate in + + See how scipy map_values used to interpolate in https://github.com/geoffwoollard/compSPI/blob/stash_simulate/src/simulate.py#L111 Parameters @@ -162,11 +162,11 @@ def generate_slices(self, map_3d_f, xy_plane, n_pix, rots): Shape (n_pix**2, 3) """ n_rotations = rots.shape[0] - # TODO: map_values interpolation, calculate from map, rots + # map_values interpolation, calculate from map, rots map_3d_f = np.ones_like(map_3d_f) xyz_rotated = np.ones_like(xy_plane) - size = n_rotations * n_pix**2 + size = n_rotations * n_pix ** 2 slices = np.random.normal(size=size) slices.reshape(n_rotations, n_pix, n_pix) return slices, xyz_rotated @@ -184,7 +184,7 @@ def apply_ctf_to_slice(self, particle_slice, ctf): Shape (n_pix,n_pix) """ - # TODO: vectorize and have shape match + # vectorize and have shape match projection_f_conv_ctf = ctf * slice return projection_f_conv_ctf @@ -278,7 +278,7 @@ def compute_fsc(self, map_3d_f_1): Noise estimates for map 1. Shape (n_pix // 2,) """ - # TODO: write fast vectorized fsc from code snippets in + # write fast vectorized fsc from code snippets in # https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/fsc.ipynb # https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/mFSC.ipynb # https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/guinier_fsc_sharpen.ipynb @@ -302,5 +302,5 @@ def expand_1d_to_3d(self, arr_1d): """ n_pix = arr_1d.shape[0] * 2 arr_3d = np.ones((n_pix, n_pix, n_pix)) - # TODO: arr_1d fsc_1d to 3d (spherical shells) + # arr_1d fsc_1d to 3d (spherical shells) return arr_3d From 87a9a87fe730721574848e302c70f037a86cae1f Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Thu, 24 Mar 2022 02:53:24 +0000 Subject: [PATCH 018/148] Format code with black and isort --- iterative_refinement/expectation_maximization.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index a3e1074..7f749ca 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -127,14 +127,14 @@ def generate_xy_plane(self, n_pix): # See how meshgrid and generate coordinates functions used # https://github.com/geoffwoollard/compSPI/blob/stash_simulate/src/simulate.py#L96 - xy_plane = np.ones((n_pix ** 2, 3)) + xy_plane = np.ones((n_pix**2, 3)) return xy_plane def generate_slices(self, map_3d_f, xy_plane, n_pix, rots): """ Generates slice coordinates by rotating xy plane. Interpolate values from map_3d_f onto 3D coordinates. - + See how scipy map_values used to interpolate in https://github.com/geoffwoollard/compSPI/blob/stash_simulate/src/simulate.py#L111 @@ -166,7 +166,7 @@ def generate_slices(self, map_3d_f, xy_plane, n_pix, rots): map_3d_f = np.ones_like(map_3d_f) xyz_rotated = np.ones_like(xy_plane) - size = n_rotations * n_pix ** 2 + size = n_rotations * n_pix**2 slices = np.random.normal(size=size) slices.reshape(n_rotations, n_pix, n_pix) return slices, xyz_rotated From 65273f0a22417987378b074819b505bfc1609b30 Mon Sep 17 00:00:00 2001 From: jed Date: Wed, 23 Mar 2022 20:17:07 -0700 Subject: [PATCH 019/148] DeepSource changes --- iterative_refinement/expectation_maximization.py | 9 +++------ tests/test_expectation_maximization.py | 10 +++++----- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index a3e1074..74f2e81 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -4,7 +4,6 @@ """ import numpy as np -from compSPI.transforms import do_fft, do_ifft from simSPI.transfer import eval_ctf @@ -68,7 +67,7 @@ def split_array(self, arr): arr_1, arr_2 = arr[:idx_half], arr[idx_half:] if arr_1.shape[0] != arr_2.shape[0]: - arr_2 = arr[idx_half : 2 * idx_half] + arr_2 = arr[idx_half:2 * idx_half] return arr_1, arr_2 @@ -123,7 +122,6 @@ def generate_xy_plane(self, n_pix): Array describing xy plane in space. Shape (n_pix**2, 3) """ - # See how meshgrid and generate coordinates functions used # https://github.com/geoffwoollard/compSPI/blob/stash_simulate/src/simulate.py#L96 @@ -183,9 +181,8 @@ def apply_ctf_to_slice(self, particle_slice, ctf): CTF parameters for particle. Shape (n_pix,n_pix) """ - # vectorize and have shape match - projection_f_conv_ctf = ctf * slice + projection_f_conv_ctf = ctf * particle_slice return projection_f_conv_ctf def compute_bayesian_weights(self, particle, slices): @@ -259,7 +256,7 @@ def insert_slice(self, slice_real, xyz, n_pix): """ inserted_slice_3d = slice_real shape = xyz.shape[0] - count_3d = np.ones((shape, shape, shape)) + count_3d = np.ones((shape, n_pix, n_pix)) return inserted_slice_3d, count_3d def compute_fsc(self, map_3d_f_1): diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 563f3ab..f5fef31 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -1,5 +1,3 @@ -from cmath import exp - import numpy as np import pytest @@ -9,7 +7,7 @@ @pytest.fixture def test_ir(): """Instantiate IterativeRefinement class for testing.""" - ir = em.IterativeRefinement(np.ndarray(), list(), dict()) + ir = em.IterativeRefinement(np.ndarray(), list(), dict(), 7) return ir @@ -54,7 +52,7 @@ def test_generate_xy_plane(): def test_generate_slices(): """Test generation of slices.""" map_3d = np.ones(2, 2, 2) - rots = test_grid_SO3_uniform(2) + rots = test_ir.grid_SO3_uniform(2) xy_plane = test_ir.generate_xy_plane(2) slices, xyz_rotated = test_ir.generate_slices(map_3d, rots) @@ -91,7 +89,9 @@ def test_apply_wiener_filter(): ctf = np.zeros(2, 2) small_number = 0.01 - projection_wfilter_f = test_ir.apply_wiener_filter(projection, ctf, small_number) + projection_wfilter_f = test_ir.apply_wiener_filter(projection, + ctf, + small_number) assert projection_wfilter_f.shape == (2, 2) From d5ea49741d1dcfa1dcd40f0d897e9a54a21bbd51 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Thu, 24 Mar 2022 03:17:50 +0000 Subject: [PATCH 020/148] Format code with black and isort --- iterative_refinement/expectation_maximization.py | 4 ++-- tests/test_expectation_maximization.py | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index a14574a..331486a 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -67,7 +67,7 @@ def split_array(self, arr): arr_1, arr_2 = arr[:idx_half], arr[idx_half:] if arr_1.shape[0] != arr_2.shape[0]: - arr_2 = arr[idx_half:2 * idx_half] + arr_2 = arr[idx_half : 2 * idx_half] return arr_1, arr_2 @@ -182,7 +182,7 @@ def apply_ctf_to_slice(self, particle_slice, ctf): Shape (n_pix,n_pix) """ # vectorize and have shape match - projection_f_conv_ctf = ctf * particle_slice + projection_f_conv_ctf = ctf * particle_slice return projection_f_conv_ctf def compute_bayesian_weights(self, particle, slices): diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index f5fef31..5314ca6 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -89,9 +89,7 @@ def test_apply_wiener_filter(): ctf = np.zeros(2, 2) small_number = 0.01 - projection_wfilter_f = test_ir.apply_wiener_filter(projection, - ctf, - small_number) + projection_wfilter_f = test_ir.apply_wiener_filter(projection, ctf, small_number) assert projection_wfilter_f.shape == (2, 2) From 1a797729cd11aae073c8b8bcb1997fab76d8332e Mon Sep 17 00:00:00 2001 From: jed Date: Wed, 23 Mar 2022 20:23:42 -0700 Subject: [PATCH 021/148] literals removed --- tests/test_expectation_maximization.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index f5fef31..2d8cc13 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -7,7 +7,10 @@ @pytest.fixture def test_ir(): """Instantiate IterativeRefinement class for testing.""" - ir = em.IterativeRefinement(np.ndarray(), list(), dict(), 7) + my_list = [] + my_dict = {} + itr = 7 + ir = em.IterativeRefinement(my_list, my_dict, itr) return ir From 38a9a5ea907507577c3ee091e2b01fb90098aeab Mon Sep 17 00:00:00 2001 From: jed Date: Wed, 23 Mar 2022 20:26:50 -0700 Subject: [PATCH 022/148] add pre commit --- .pre-commit-config.yaml | 42 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..e0b31c6 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,42 @@ +default_language_version : + python : python3 +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.1.0 + hooks: + - id: check-byte-order-marker + - id: check-case-conflict + - id: check-merge-conflict + - id: check-yaml + - id: mixed-line-ending + args: + - --fix=no + - id: no-commit-to-branch + args: + - --branch=master + - id: check-added-large-files + args: + - --maxkb=2048 + - id: trailing-whitespace + - repo: https://github.com/psf/black + rev: 21.8b0 + hooks: + - id: black + - repo: https://github.com/pycqa/isort + rev: 5.9.3 + hooks: + - id : isort + args : ["--profile", "black", "--filter-files"] + - repo: https://github.com/asottile/blacken-docs + rev: v1.8.0 + hooks: + - id: blacken-docs + additional_dependencies: [black==20.8b0] + - repo: https://gitlab.com/pycqa/flake8 + rev: 3.7.9 + hooks: + - id: flake8 + additional_dependencies: + - flake8-docstrings + - flake8-import-order + exclude: reconstructSPI/__init__.py,tests/__init__.py \ No newline at end of file From 7a4de6f4c4d2b08119a37b9f064b7b107a81a99c Mon Sep 17 00:00:00 2001 From: jed Date: Wed, 23 Mar 2022 22:28:03 -0700 Subject: [PATCH 023/148] Init tests --- tests/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/__init__.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 From df6c749be239c37778396f9743b0014a62e51d19 Mon Sep 17 00:00:00 2001 From: jed Date: Wed, 23 Mar 2022 22:37:51 -0700 Subject: [PATCH 024/148] temporary static methods --- iterative_refinement/expectation_maximization.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index 331486a..50d8aed 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -3,6 +3,7 @@ for reconstruction of particles. """ +from os import stat import numpy as np from simSPI.transfer import eval_ctf @@ -47,6 +48,7 @@ def __init__(self, map_3d_init, particles, ctf_info, max_itr=7): self.ctf_info = ctf_info self.max_itr = max_itr + @staticmethod def split_array(self, arr): """ Split array into two halves along 0th axis. @@ -71,6 +73,7 @@ def split_array(self, arr): return arr_1, arr_2 + @staticmethod def build_ctf_array(self): """ Build 2D array of evaluated CTFs from inputted @@ -89,6 +92,7 @@ def build_ctf_array(self): return ctfs + @staticmethod def grid_SO3_uniform(self, n_rotations): """ Generate a discrete set of uniformly distributed rotations @@ -108,6 +112,7 @@ def grid_SO3_uniform(self, n_rotations): rots = np.ones((n_rotations, 3, 3)) return rots + @staticmethod def generate_xy_plane(self, n_pix): """ Generate xy plane. @@ -128,6 +133,7 @@ def generate_xy_plane(self, n_pix): xy_plane = np.ones((n_pix**2, 3)) return xy_plane + @staticmethod def generate_slices(self, map_3d_f, xy_plane, n_pix, rots): """ Generates slice coordinates by rotating xy plane. @@ -169,6 +175,7 @@ def generate_slices(self, map_3d_f, xy_plane, n_pix, rots): slices.reshape(n_rotations, n_pix, n_pix) return slices, xyz_rotated + @staticmethod def apply_ctf_to_slice(self, particle_slice, ctf): """ Apply CTF to projected slice by convolution. @@ -185,6 +192,7 @@ def apply_ctf_to_slice(self, particle_slice, ctf): projection_f_conv_ctf = ctf * particle_slice return projection_f_conv_ctf + @staticmethod def compute_bayesian_weights(self, particle, slices): """ Compute Bayesian weights of particle to slice @@ -208,6 +216,7 @@ def compute_bayesian_weights(self, particle, slices): bayes_factors = np.random.normal(n_slices) return bayes_factors + @staticmethod def apply_wiener_filter(self, projection, ctf, small_number): """ Apply Wiener filter to particle projection. @@ -230,6 +239,7 @@ def apply_wiener_filter(self, projection, ctf, small_number): projection_wfilter_f = projection * wfilter return projection_wfilter_f + @staticmethod def insert_slice(self, slice_real, xyz, n_pix): """ Rotate slice and interpolate onto a 3D grid to prepare @@ -259,6 +269,7 @@ def insert_slice(self, slice_real, xyz, n_pix): count_3d = np.ones((shape, n_pix, n_pix)) return inserted_slice_3d, count_3d + @staticmethod def compute_fsc(self, map_3d_f_1): """ Compute Fourier shell correlation. @@ -283,6 +294,7 @@ def compute_fsc(self, map_3d_f_1): fsc_1d_1 = np.ones(n_pix_1 // 2) return fsc_1d_1 + @staticmethod def expand_1d_to_3d(self, arr_1d): """ Expand 1D array data into spherical shell. From 6849b0c9e6ff6d07c94ecf9e5e6ba18ed62dcea7 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Thu, 24 Mar 2022 05:38:02 +0000 Subject: [PATCH 025/148] Format code with black and isort --- iterative_refinement/expectation_maximization.py | 1 + 1 file changed, 1 insertion(+) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index 50d8aed..95495a6 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -4,6 +4,7 @@ """ from os import stat + import numpy as np from simSPI.transfer import eval_ctf From 4e927ab8230890f46ae141309a113339dafa619c Mon Sep 17 00:00:00 2001 From: jed Date: Wed, 23 Mar 2022 23:08:09 -0700 Subject: [PATCH 026/148] temporary static methods --- .../expectation_maximization.py | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index 50d8aed..a3f59c1 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -42,14 +42,14 @@ class IterativeRefinement: Shape (n_pix // 2,) """ - def __init__(self, map_3d_init, particles, ctf_info, max_itr=7): + def __init__(map_3d_init, particles, ctf_info, max_itr=7): self.map_3d_init = map_3d_init self.particles = particles self.ctf_info = ctf_info self.max_itr = max_itr @staticmethod - def split_array(self, arr): + def split_array(arr): """ Split array into two halves along 0th axis. @@ -93,7 +93,7 @@ def build_ctf_array(self): return ctfs @staticmethod - def grid_SO3_uniform(self, n_rotations): + def grid_SO3_uniform(n_rotations): """ Generate a discrete set of uniformly distributed rotations across SO(3). @@ -113,7 +113,7 @@ def grid_SO3_uniform(self, n_rotations): return rots @staticmethod - def generate_xy_plane(self, n_pix): + def generate_xy_plane(n_pix): """ Generate xy plane. @@ -134,7 +134,7 @@ def generate_xy_plane(self, n_pix): return xy_plane @staticmethod - def generate_slices(self, map_3d_f, xy_plane, n_pix, rots): + def generate_slices(map_3d_f, xy_plane, n_pix, rots): """ Generates slice coordinates by rotating xy plane. Interpolate values from map_3d_f onto 3D coordinates. @@ -176,7 +176,7 @@ def generate_slices(self, map_3d_f, xy_plane, n_pix, rots): return slices, xyz_rotated @staticmethod - def apply_ctf_to_slice(self, particle_slice, ctf): + def apply_ctf_to_slice(particle_slice, ctf): """ Apply CTF to projected slice by convolution. @@ -193,7 +193,7 @@ def apply_ctf_to_slice(self, particle_slice, ctf): return projection_f_conv_ctf @staticmethod - def compute_bayesian_weights(self, particle, slices): + def compute_bayesian_weights(particle, slices): """ Compute Bayesian weights of particle to slice under Gaussian white noise model. @@ -217,7 +217,7 @@ def compute_bayesian_weights(self, particle, slices): return bayes_factors @staticmethod - def apply_wiener_filter(self, projection, ctf, small_number): + def apply_wiener_filter(projection, ctf, small_number): """ Apply Wiener filter to particle projection. @@ -240,7 +240,7 @@ def apply_wiener_filter(self, projection, ctf, small_number): return projection_wfilter_f @staticmethod - def insert_slice(self, slice_real, xyz, n_pix): + def insert_slice(slice_real, xyz, n_pix): """ Rotate slice and interpolate onto a 3D grid to prepare for insertion. @@ -270,7 +270,7 @@ def insert_slice(self, slice_real, xyz, n_pix): return inserted_slice_3d, count_3d @staticmethod - def compute_fsc(self, map_3d_f_1): + def compute_fsc(map_3d_f_1): """ Compute Fourier shell correlation. Estimate noise from half maps. @@ -295,7 +295,7 @@ def compute_fsc(self, map_3d_f_1): return fsc_1d_1 @staticmethod - def expand_1d_to_3d(self, arr_1d): + def expand_1d_to_3d(arr_1d): """ Expand 1D array data into spherical shell. From ea2c766c06e1900b39ee754fcdefc7f39fa98c2f Mon Sep 17 00:00:00 2001 From: jed Date: Wed, 23 Mar 2022 23:16:53 -0700 Subject: [PATCH 027/148] ds changes --- iterative_refinement/expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index ef5476c..a743bdd 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -75,7 +75,7 @@ def split_array(arr): def build_ctf_array(self): """ Build 2D array of evaluated CTFs from inputted - CTF parameters for each particle. + CTF parameters for a particle. Returns ------- From 714b33d577cabba7f238ba2f9c8a129a28321876 Mon Sep 17 00:00:00 2001 From: jed Date: Wed, 23 Mar 2022 23:19:00 -0700 Subject: [PATCH 028/148] changed deepsource --- .deepsource.toml | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/.deepsource.toml b/.deepsource.toml index 93a9d6f..6c238bf 100644 --- a/.deepsource.toml +++ b/.deepsource.toml @@ -1,24 +1,13 @@ version = 1 -test_patterns = ["test/**"] +test_patterns = ["tests/**"] -exclude_patterns = ["README.md"] - -[[analyzers]] -name = "python" -enabled = true - - [analyzers.meta] - runtime_version = "3.x.x" +exclude_patterns = ["[\"README.md\"]"] [[analyzers]] name = "test-coverage" enabled = true -[[transformers]] -name = "isort" -enabled = true - -[[transformers]] -name = "black" -enabled = true +[[analyzers]] +name = "python" +enabled = true \ No newline at end of file From 5b4db6ed8ea68c1953637535e8eae7fbe0f43934 Mon Sep 17 00:00:00 2001 From: jed Date: Wed, 23 Mar 2022 23:20:25 -0700 Subject: [PATCH 029/148] lint --- iterative_refinement/expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index a743bdd..851dbc4 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -68,7 +68,7 @@ def split_array(arr): arr_1, arr_2 = arr[:idx_half], arr[idx_half:] if arr_1.shape[0] != arr_2.shape[0]: - arr_2 = arr[idx_half : 2 * idx_half] + arr_2 = arr[idx_half:2 * idx_half] return arr_1, arr_2 From ccd0d04e42a96d37ea3e0211e021924e27fa898b Mon Sep 17 00:00:00 2001 From: jed Date: Wed, 23 Mar 2022 23:21:22 -0700 Subject: [PATCH 030/148] add black back --- .deepsource.toml | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/.deepsource.toml b/.deepsource.toml index 6c238bf..0e75684 100644 --- a/.deepsource.toml +++ b/.deepsource.toml @@ -1,13 +1,21 @@ version = 1 -test_patterns = ["tests/**"] +test_patterns = ["test/**"] -exclude_patterns = ["[\"README.md\"]"] +exclude_patterns = ["README.md"] [[analyzers]] -name = "test-coverage" +name = "python" enabled = true + [analyzers.meta] + runtime_version = "3.x.x" + [[analyzers]] -name = "python" -enabled = true \ No newline at end of file +name = "test-coverage" +enabled = true + +[[transformers]] +name = "black" +enabled = true + From c8f3c60355d7190ae24db10f69bd027c8f42e890 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Thu, 24 Mar 2022 06:21:32 +0000 Subject: [PATCH 031/148] Format code with black --- iterative_refinement/expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index 851dbc4..a743bdd 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -68,7 +68,7 @@ def split_array(arr): arr_1, arr_2 = arr[:idx_half], arr[idx_half:] if arr_1.shape[0] != arr_2.shape[0]: - arr_2 = arr[idx_half:2 * idx_half] + arr_2 = arr[idx_half : 2 * idx_half] return arr_1, arr_2 From c2726b2edd3d66c45d7a404331cac168b7759d02 Mon Sep 17 00:00:00 2001 From: jed Date: Wed, 23 Mar 2022 23:25:21 -0700 Subject: [PATCH 032/148] update toml --- .deepsource.toml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.deepsource.toml b/.deepsource.toml index 0e75684..b4f754a 100644 --- a/.deepsource.toml +++ b/.deepsource.toml @@ -1,6 +1,6 @@ version = 1 -test_patterns = ["test/**"] +test_patterns = ["tests/**"] exclude_patterns = ["README.md"] @@ -8,9 +8,6 @@ exclude_patterns = ["README.md"] name = "python" enabled = true - [analyzers.meta] - runtime_version = "3.x.x" - [[analyzers]] name = "test-coverage" enabled = true From 7f962739ee5ee9d32e9e26689cadeab11492996d Mon Sep 17 00:00:00 2001 From: jed Date: Wed, 23 Mar 2022 23:28:28 -0700 Subject: [PATCH 033/148] ds --- iterative_refinement/expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index a743bdd..ef5476c 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -75,7 +75,7 @@ def split_array(arr): def build_ctf_array(self): """ Build 2D array of evaluated CTFs from inputted - CTF parameters for a particle. + CTF parameters for each particle. Returns ------- From 3d4ad77b13f829c260c05b4e813d0b5c97d1af85 Mon Sep 17 00:00:00 2001 From: jed Date: Wed, 23 Mar 2022 23:38:35 -0700 Subject: [PATCH 034/148] added .ini for pytest --- pytest.ini | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 pytest.ini diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..cf93d60 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,4 @@ +[pytest] +testpaths = tests +python_files = *.py +python_functions = test_* \ No newline at end of file From b2931812134b249a4b5856285e7d3734dfaf0bd8 Mon Sep 17 00:00:00 2001 From: jed Date: Wed, 23 Mar 2022 23:40:02 -0700 Subject: [PATCH 035/148] init --- iterative_refinement/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 iterative_refinement/__init__.py diff --git a/iterative_refinement/__init__.py b/iterative_refinement/__init__.py new file mode 100644 index 0000000..e69de29 From 02eec0a59890cb5e0c5b8d0d9020b3cc08b2c6d9 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Wed, 23 Mar 2022 23:46:41 -0700 Subject: [PATCH 036/148] Codecov files --- .codecov.yml | 24 ++++++++++++++++++++++++ .coveragerc | 3 +++ .flake8 | 6 ++++++ 3 files changed, 33 insertions(+) create mode 100644 .codecov.yml create mode 100644 .coveragerc create mode 100644 .flake8 diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 0000000..c576999 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,24 @@ +coverage: + round: up + precision: 2 + status: + patch: + default: + # basic + target: 90% + threshold: 2% + base: auto + flags: + - unit + # advanced + branches: + - master + if_no_uploads: error + if_not_found: error + if_ci_failed: error + only_pulls: false + +# Files to ignore +ignore: + - "data" + - "notebooks" diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..94a9d5a --- /dev/null +++ b/.coveragerc @@ -0,0 +1,3 @@ +[report] +fail_under = 90 +show_missing = True diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..88932be --- /dev/null +++ b/.flake8 @@ -0,0 +1,6 @@ +[flake8] +docstring-convention = numpy +import_order_style = smarkets +max-line-length = 88 +extend-ignore = E203,I202,I100 +exclude = ioSPI/__init__.py,tests/__init__.py,tests/test_iotools/__init__.py From 49138c707beebe07cec29088865d5a0b772f7b1f Mon Sep 17 00:00:00 2001 From: thisTyler Date: Wed, 23 Mar 2022 23:49:25 -0700 Subject: [PATCH 037/148] Linting files --- .github/workflows/lint.yml | 54 +++++++++++++++++++++++++++++++++ .github/workflows/test.yml | 62 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 .github/workflows/lint.yml create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..d78382a --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,54 @@ +name: "Lint" + +on: + push: + branches: [master,github-actions-test] + paths-ignore: + - 'README.md' + - '.deepsource.toml' + - '.gitignore' + - 'setup.py' + + + pull_request: + branches: [master] + paths-ignore: + - 'README.md' + - '.deepsource.toml' + - '.gitignore' + - 'setup.py' + + +jobs: + build: + + runs-on: ${{matrix.os}} + strategy: + matrix: + os: [ubuntu-18.04] + python-version: [3.9] + test-folder : ['tests'] + fail-fast: false + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Build using Python ${{matrix.python-version}} + uses: actions/setup-python@v2 + with: + python-version: ${{matrix.python-version}} + - name: install dependencies [pip] + run: | + pip install --upgrade pip setuptools wheel + for req in dev-requirements.txt; do + pip install -q -r $req + done + pip install -e . + - name: linting [black and isort] + run: | + black . --check + isort --profile black --check . + - name: linting [flake8] + run: | + flake8 ioSPI tests diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..d957659 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,62 @@ +name: "Test" + +on: + push: + branches: [master,github-actions-test] + paths-ignore: + - 'README.md' + - '.deepsource.toml' + - '.gitignore' + - 'setup.py' + + + pull_request: + branches: [master] + paths-ignore: + - 'README.md' + - '.deepsource.toml' + - '.gitignore' + - 'setup.py' + + +jobs: + build: + + runs-on: ${{matrix.os}} + strategy: + matrix: + os: [ubuntu-18.04] + python-version: [3.7,3.8,3.9] + test-folder : ['tests'] + fail-fast: false + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Build using Python ${{matrix.python-version}} + uses: actions/setup-python@v2 + with: + python-version: ${{matrix.python-version}} + + - name: cache conda + uses: actions/cache@v1 + with: + path: $CONDA + key: ${{ runner.os }}-conda-${{ hashFiles('environment.yml') }} + restore-keys: | + ${{ runner.os }}-conda- + - name: install dependencies [conda] + run: | + # $CONDA is an environment variable pointing to the root of the miniconda directory + $CONDA/bin/conda env update --file environment.yml --name base + $CONDA/bin/pip install -e . + - name: unit testing [pytest] + env: + TEST_TOKEN: ${{ secrets.TEST_TOKEN }} + run: | + $CONDA/bin/pytest --cov-report term --cov-report xml:coverage.xml --cov=ioSPI ${{matrix.test-folder}} + - name: uploading code coverage [codecov] + if: ${{matrix.python-version == 3.7}} + run: | + bash <(curl -s https://codecov.io/bash) -c From 6135c7257d0a5790231abe8bb0e8ec94db86051f Mon Sep 17 00:00:00 2001 From: thisTyler Date: Wed, 23 Mar 2022 23:50:59 -0700 Subject: [PATCH 038/148] Dev requirements --- dev-requirements.txt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 dev-requirements.txt diff --git a/dev-requirements.txt b/dev-requirements.txt new file mode 100644 index 0000000..663cad4 --- /dev/null +++ b/dev-requirements.txt @@ -0,0 +1,5 @@ +black +flake8 +flake8-docstrings +isort +pre-commit From f54ece8a2a366923c1078e2e085cf02e407f7a83 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Wed, 23 Mar 2022 23:53:39 -0700 Subject: [PATCH 039/148] Environment config --- environment.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 environment.yml diff --git a/environment.yml b/environment.yml new file mode 100644 index 0000000..01e8d5c --- /dev/null +++ b/environment.yml @@ -0,0 +1,18 @@ +name: reconstructSPI +channels: + - conda-forge + - defaults +dependencies: + - codecov + - coverage + - gemmi + - numba + - numpy + - pillow>=8.2.0 + - pip + - pytest + - pytest-cov + - pytorch + - pip : + - git+https://github.com/compSPI/simSPI.git + From b9b540e576178cf92f18a3c7167b03e2dfda1905 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Wed, 23 Mar 2022 23:56:29 -0700 Subject: [PATCH 040/148] Added setup.py --- setup.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 setup.py diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..9631ff3 --- /dev/null +++ b/setup.py @@ -0,0 +1,23 @@ +"""Create instructions to build the reconstructSPI package.""" + +import setuptools + +requirements = [] + +setuptools.setup( + name="reconstructSPI", + maintainer="Frederic Poitevin", + version="0.0.1", + maintainer_email="frederic.poitevin@stanford.edu", + description="Reconstruction methods and tools for SPI", + long_description=open("README.md", encoding="utf8").read(), + long_description_content_type="text/markdown", + url="https://github.com/compSPI/reconstructSPI.git", + packages=setuptools.find_packages(), + install_requires=requirements, + classifiers=[ + "Programming Language :: Python :: 3", + "Operating System :: OS Independent", + ], + zip_safe=False, +) From afd729079916630db2fab8bce0bc1d9b2d87fc70 Mon Sep 17 00:00:00 2001 From: jed Date: Wed, 23 Mar 2022 23:57:46 -0700 Subject: [PATCH 041/148] env yaml --- environment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/environment.yml b/environment.yml index 01e8d5c..9908d0c 100644 --- a/environment.yml +++ b/environment.yml @@ -14,5 +14,5 @@ dependencies: - pytest-cov - pytorch - pip : - - git+https://github.com/compSPI/simSPI.git + - git+https://github.com/compSPI/reconstructSPI.git From 5a4a56e75d1cb3341a619f8911635101d81ca7af Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 00:19:41 -0700 Subject: [PATCH 042/148] Reformatted docstrings --- .../expectation_maximization.py | 56 +++++++------------ 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index ef5476c..4723f99 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -1,16 +1,11 @@ -""" -Iterative refinement in Bayesian expection maximization setting -for reconstruction of particles. -""" +"""Iterative refinement with Bayesian expection maximization.""" import numpy as np from simSPI.transfer import eval_ctf class IterativeRefinement: - """ - Serves as a base class to perform iterative refinement - reconstruction with respect to maximum likelihood esimation. + """Perform iterative refinement with max likelihood esimation. Parameters ---------- @@ -49,8 +44,7 @@ def __init__(self, map_3d_init, particles, ctf_info, max_itr=7): @staticmethod def split_array(arr): - """ - Split array into two halves along 0th axis. + """Split array into two halves along 0th axis. Parameters ---------- @@ -73,9 +67,9 @@ def split_array(arr): return arr_1, arr_2 def build_ctf_array(self): - """ - Build 2D array of evaluated CTFs from inputted - CTF parameters for each particle. + """Build 2D array of evaluated CTFs. + + Use inputted CTF parameters, act for each particle. Returns ------- @@ -92,9 +86,7 @@ def build_ctf_array(self): @staticmethod def grid_SO3_uniform(n_rotations): - """ - Generate a discrete set of uniformly distributed rotations - across SO(3). + """Generate uniformly distributed rotations in SO(3). Parameters ---------- @@ -112,8 +104,7 @@ def grid_SO3_uniform(n_rotations): @staticmethod def generate_xy_plane(n_pix): - """ - Generate xy plane. + """Generate xy plane. Parameters ---------- @@ -133,9 +124,9 @@ def generate_xy_plane(n_pix): @staticmethod def generate_slices(map_3d_f, xy_plane, n_pix, rots): - """ - Generates slice coordinates by rotating xy plane. - Interpolate values from map_3d_f onto 3D coordinates. + """Generate slice coordinates by rotating xy plane. + + Interpolate values from map_3d_f onto 3D coordinates. See how scipy map_values used to interpolate in https://github.com/geoffwoollard/compSPI/blob/stash_simulate/src/simulate.py#L111 @@ -175,8 +166,7 @@ def generate_slices(map_3d_f, xy_plane, n_pix, rots): @staticmethod def apply_ctf_to_slice(particle_slice, ctf): - """ - Apply CTF to projected slice by convolution. + """Apply CTF to projected slice by convolution. particle_slice : arr Slice of map_3d_f. Corresponds to Fourier transform @@ -192,9 +182,9 @@ def apply_ctf_to_slice(particle_slice, ctf): @staticmethod def compute_bayesian_weights(particle, slices): - """ - Compute Bayesian weights of particle to slice - under Gaussian white noise model. + """Compute Bayesian weights of particle to slice. + + Use Gaussian white noise model. Parameters ---------- @@ -216,8 +206,7 @@ def compute_bayesian_weights(particle, slices): @staticmethod def apply_wiener_filter(projection, ctf, small_number): - """ - Apply Wiener filter to particle projection. + """Apply Wiener filter to particle projection. Parameters ---------- @@ -239,9 +228,7 @@ def apply_wiener_filter(projection, ctf, small_number): @staticmethod def insert_slice(slice_real, xyz, n_pix): - """ - Rotate slice and interpolate onto a 3D grid to prepare - for insertion. + """Rotate slice and interpolate onto a 3D grid. Parameters ---------- @@ -269,9 +256,9 @@ def insert_slice(slice_real, xyz, n_pix): @staticmethod def compute_fsc(map_3d_f_1): - """ - Compute Fourier shell correlation. - Estimate noise from half maps. + """Compute Fourier shell correlation. + + Estimate noise from half maps. Parameters ---------- @@ -294,8 +281,7 @@ def compute_fsc(map_3d_f_1): @staticmethod def expand_1d_to_3d(arr_1d): - """ - Expand 1D array data into spherical shell. + """Expand 1D array data into spherical shell. Parameters ---------- From e504e32b1d848f066af1545c2872344d73276a1b Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Thu, 24 Mar 2022 07:19:48 +0000 Subject: [PATCH 043/148] Format code with black --- .../expectation_maximization.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index 4723f99..78d2676 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -68,8 +68,8 @@ def split_array(arr): def build_ctf_array(self): """Build 2D array of evaluated CTFs. - - Use inputted CTF parameters, act for each particle. + + Use inputted CTF parameters, act for each particle. Returns ------- @@ -125,8 +125,8 @@ def generate_xy_plane(n_pix): @staticmethod def generate_slices(map_3d_f, xy_plane, n_pix, rots): """Generate slice coordinates by rotating xy plane. - - Interpolate values from map_3d_f onto 3D coordinates. + + Interpolate values from map_3d_f onto 3D coordinates. See how scipy map_values used to interpolate in https://github.com/geoffwoollard/compSPI/blob/stash_simulate/src/simulate.py#L111 @@ -183,8 +183,8 @@ def apply_ctf_to_slice(particle_slice, ctf): @staticmethod def compute_bayesian_weights(particle, slices): """Compute Bayesian weights of particle to slice. - - Use Gaussian white noise model. + + Use Gaussian white noise model. Parameters ---------- @@ -256,9 +256,9 @@ def insert_slice(slice_real, xyz, n_pix): @staticmethod def compute_fsc(map_3d_f_1): - """Compute Fourier shell correlation. - - Estimate noise from half maps. + """Compute Fourier shell correlation. + + Estimate noise from half maps. Parameters ---------- From a282ffc0ffecc68b2391e29c0ded0dcb35b88c16 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 00:23:01 -0700 Subject: [PATCH 044/148] Testing workspace changes --- .flake8 | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/test.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.flake8 b/.flake8 index 88932be..cbc6363 100644 --- a/.flake8 +++ b/.flake8 @@ -3,4 +3,4 @@ docstring-convention = numpy import_order_style = smarkets max-line-length = 88 extend-ignore = E203,I202,I100 -exclude = ioSPI/__init__.py,tests/__init__.py,tests/test_iotools/__init__.py +exclude = reconstructSPI/__init__.py,tests/__init__.py diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index d78382a..cfc5574 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -51,4 +51,4 @@ jobs: isort --profile black --check . - name: linting [flake8] run: | - flake8 ioSPI tests + flake8 reconstructSPI tests diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d957659..49cc26f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -55,7 +55,7 @@ jobs: env: TEST_TOKEN: ${{ secrets.TEST_TOKEN }} run: | - $CONDA/bin/pytest --cov-report term --cov-report xml:coverage.xml --cov=ioSPI ${{matrix.test-folder}} + $CONDA/bin/pytest --cov-report term --cov-report xml:coverage.xml --cov=reconstructSPI ${{matrix.test-folder}} - name: uploading code coverage [codecov] if: ${{matrix.python-version == 3.7}} run: | From 1a0af09af3d0117ab1cc7e3af3935d8a43bcfc40 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 00:26:45 -0700 Subject: [PATCH 045/148] Fixed test docstrings --- tests/test_expectation_maximization.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index bfe9703..2b5f328 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -1,3 +1,5 @@ +"""Test bayesian expectation maximization process.""" + import numpy as np import pytest @@ -75,9 +77,9 @@ def test_apply_ctf_to_slice(): def test_compute_bayesian_weights(): - """ - Test computation of bayesian weights under - Gaussian white noise model. + """Test computation of bayesian weights. + + For use under Gaussian white noise model. """ particle = np.ones(1, 2, 2) slices = np.ones(2, 2, 2) From 3d1be9b4c5b976075c55cb2091ee0bc7e6aaa694 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Thu, 24 Mar 2022 07:26:50 +0000 Subject: [PATCH 046/148] Format code with black --- tests/test_expectation_maximization.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 2b5f328..16b6612 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -78,8 +78,8 @@ def test_apply_ctf_to_slice(): def test_compute_bayesian_weights(): """Test computation of bayesian weights. - - For use under Gaussian white noise model. + + For use under Gaussian white noise model. """ particle = np.ones(1, 2, 2) slices = np.ones(2, 2, 2) From 5268b738e5052d360fec112f59eeba805a381d36 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 00:27:54 -0700 Subject: [PATCH 047/148] Edited module name --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index cfc5574..9240dcc 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -51,4 +51,4 @@ jobs: isort --profile black --check . - name: linting [flake8] run: | - flake8 reconstructSPI tests + flake8 iterative_refinement tests From 984af8ada2e2dec2dfe5d068c24a168e2123e3d7 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 00:29:52 -0700 Subject: [PATCH 048/148] Linting fixes --- iterative_refinement/__init__.py | 1 + iterative_refinement/expectation_maximization.py | 1 + 2 files changed, 2 insertions(+) diff --git a/iterative_refinement/__init__.py b/iterative_refinement/__init__.py index e69de29..9143076 100644 --- a/iterative_refinement/__init__.py +++ b/iterative_refinement/__init__.py @@ -0,0 +1 @@ +"""Iterative refinement reconstruction methods.""" \ No newline at end of file diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index 78d2676..bbb0090 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -112,6 +112,7 @@ def generate_xy_plane(n_pix): Number of pixels along one edge of the plane. Returns + ------- xy_plane : arr Array describing xy plane in space. Shape (n_pix**2, 3) From eecb97e4329a55b8111a9f6feaf131cf058f706c Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Thu, 24 Mar 2022 07:29:58 +0000 Subject: [PATCH 049/148] Format code with black --- iterative_refinement/__init__.py | 2 +- iterative_refinement/expectation_maximization.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/iterative_refinement/__init__.py b/iterative_refinement/__init__.py index 9143076..5500bfe 100644 --- a/iterative_refinement/__init__.py +++ b/iterative_refinement/__init__.py @@ -1 +1 @@ -"""Iterative refinement reconstruction methods.""" \ No newline at end of file +"""Iterative refinement reconstruction methods.""" diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index bbb0090..844a57c 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -112,7 +112,7 @@ def generate_xy_plane(n_pix): Number of pixels along one edge of the plane. Returns - ------- + ------- xy_plane : arr Array describing xy plane in space. Shape (n_pix**2, 3) From f7f98b4f32d983e279714d7cac2b7974da2145a3 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 00:32:30 -0700 Subject: [PATCH 050/148] Linting fixes --- iterative_refinement/expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index bbb0090..48213f0 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -112,7 +112,7 @@ def generate_xy_plane(n_pix): Number of pixels along one edge of the plane. Returns - ------- + ------- xy_plane : arr Array describing xy plane in space. Shape (n_pix**2, 3) From d2faa259b84f876fd719a0193bc7b1825ac41515 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 00:36:48 -0700 Subject: [PATCH 051/148] Fixed imports --- environment.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/environment.yml b/environment.yml index 9908d0c..80d0457 100644 --- a/environment.yml +++ b/environment.yml @@ -13,6 +13,4 @@ dependencies: - pytest - pytest-cov - pytorch - - pip : - - git+https://github.com/compSPI/reconstructSPI.git From a3eedd61f10c791385f1ff4ac50bbe669ef16f5f Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 00:40:51 -0700 Subject: [PATCH 052/148] Readded simSPI import --- environment.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/environment.yml b/environment.yml index 80d0457..01e8d5c 100644 --- a/environment.yml +++ b/environment.yml @@ -13,4 +13,6 @@ dependencies: - pytest - pytest-cov - pytorch + - pip : + - git+https://github.com/compSPI/simSPI.git From c898048f737c2c2c629b77e52415090fc33aa858 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 00:44:30 -0700 Subject: [PATCH 053/148] Fixed function reference --- tests/test_expectation_maximization.py | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 16b6612..bcbb95b 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -19,7 +19,7 @@ def test_ir(): def test_split_array(): """Test splitting of array in two halves.""" arr = np.zeros(4) - arr1, arr2 = test_ir.split_array(arr) + arr1, arr2 = test_ir().split_array(arr) assert arr1.shape == (2,) assert arr2.shape == (2,) @@ -38,29 +38,29 @@ def test_build_ctf_array(): "lp": 0.1, } ctf_params = [ex_ctf, ex_ctf] - ctfs = test_ir.build_ctf_array(ctf_params) + ctfs = test_ir().build_ctf_array(ctf_params) assert ctfs.shape == (2, 2, 2) def test_grid_SO3_uniform(): """Test generation of rotations across SO(3).""" - rots = test_ir.grid_SO3_uniform(2) + rots = test_ir().grid_SO3_uniform(2) assert rots.shape == (2, 3, 3) def test_generate_xy_plane(): """Test generation of xy plane.""" - xy_plane = test_ir.generate_xy_plane(2) + xy_plane = test_ir().generate_xy_plane(2) assert xy_plane.shape == (2, 2, 3) def test_generate_slices(): """Test generation of slices.""" map_3d = np.ones(2, 2, 2) - rots = test_ir.grid_SO3_uniform(2) - xy_plane = test_ir.generate_xy_plane(2) + rots = test_ir().grid_SO3_uniform(2) + xy_plane = test_ir().generate_xy_plane(2) - slices, xyz_rotated = test_ir.generate_slices(map_3d, rots) + slices, xyz_rotated = test_ir().generate_slices(map_3d, rots) assert xy_plane.shape == (4, 3) assert slices.shape == (2, 2, 2) @@ -71,7 +71,7 @@ def test_apply_ctf_to_slice(): """Test convolution of slice with CTF.""" particle_slice = np.ones(2, 2) ctf = np.ones(2, 2) - convolved = test_ir.apply_ctf_to_slice(particle_slice, ctf) + convolved = test_ir().apply_ctf_to_slice(particle_slice, ctf) assert convolved.shape == (2, 2) @@ -83,7 +83,7 @@ def test_compute_bayesian_weights(): """ particle = np.ones(1, 2, 2) slices = np.ones(2, 2, 2) - bayesian_weights = test_ir.compute_bayesian_weights(particle, slices) + bayesian_weights = test_ir().compute_bayesian_weights(particle, slices) assert bayesian_weights.shape == (2,) @@ -94,7 +94,7 @@ def test_apply_wiener_filter(): ctf = np.zeros(2, 2) small_number = 0.01 - projection_wfilter_f = test_ir.apply_wiener_filter(projection, ctf, small_number) + projection_wfilter_f = test_ir().apply_wiener_filter(projection, ctf, small_number) assert projection_wfilter_f.shape == (2, 2) @@ -102,9 +102,9 @@ def test_insert_slice(): """Test insertion of slice.""" n_pix = 2 particle_slice = np.ones(n_pix, n_pix) - xyz = test_ir.generate_xy_plane(2, 2) + xyz = test_ir().generate_xy_plane(2, 2) - inserted, count = test_ir.insert_slice(particle_slice, xyz, n_pix) + inserted, count = test_ir().insert_slice(particle_slice, xyz, n_pix) assert inserted.shape == (2, 2, 2) assert count.shape == (2, 2, 2) @@ -113,13 +113,13 @@ def test_compute_fsc(): """Test computation of FSC.""" map_1 = np.ones(2, 2, 2) - fsc_1 = test_ir.compute_fsc(map_1) + fsc_1 = test_ir().compute_fsc(map_1) assert fsc_1.shape == (1,) def test_expand_1d_to_3d(): """Test expansion of 1D array into spherical shell.""" arr1d = np.ones(1) - spherical = test_ir.expand_1d_to_3d(arr1d) + spherical = test_ir().expand_1d_to_3d(arr1d) assert spherical.shape == (2, 2, 2) From 71356a5393ccef52d56a473712976af7fc8ed185 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 00:48:01 -0700 Subject: [PATCH 054/148] Fixed test_build_ctf_array --- tests/test_expectation_maximization.py | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index bcbb95b..44eaf69 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -26,20 +26,8 @@ def test_split_array(): def test_build_ctf_array(): """Test bulding of arbitrary CTF array.""" - ex_ctf = { - "s": np.ones(2, 2), - "a ": np.ones(2, 2), - "def1": 1.0, - "def2": 1.0, - "angast": 0.1, - "kv": 0.1, - "cs": 0.1, - "bf": 0.1, - "lp": 0.1, - } - ctf_params = [ex_ctf, ex_ctf] - ctfs = test_ir().build_ctf_array(ctf_params) - assert ctfs.shape == (2, 2, 2) + ctfs = test_ir().build_ctf_array() + assert ctfs.shape == (0, 0, 0) def test_grid_SO3_uniform(): From 0522a91f250492198faaf76b87d0e28bb45da480 Mon Sep 17 00:00:00 2001 From: jed Date: Thu, 24 Mar 2022 01:02:41 -0700 Subject: [PATCH 055/148] tets --- .../expectation_maximization.py | 6 +- tests/test_expectation_maximization.py | 82 ++++++++++--------- 2 files changed, 47 insertions(+), 41 deletions(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index 48213f0..2edec9a 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -115,12 +115,12 @@ def generate_xy_plane(n_pix): ------- xy_plane : arr Array describing xy plane in space. - Shape (n_pix**2, 3) + Shape (n_pix, n_pix, 3) """ # See how meshgrid and generate coordinates functions used # https://github.com/geoffwoollard/compSPI/blob/stash_simulate/src/simulate.py#L96 - xy_plane = np.ones((n_pix**2, 3)) + xy_plane = np.ones((n_pix, n_pix, 3)) return xy_plane @staticmethod @@ -160,7 +160,7 @@ def generate_slices(map_3d_f, xy_plane, n_pix, rots): map_3d_f = np.ones_like(map_3d_f) xyz_rotated = np.ones_like(xy_plane) - size = n_rotations * n_pix**2 + size = n_rotations * n_pix ** 2 slices = np.random.normal(size=size) slices.reshape(n_rotations, n_pix, n_pix) return slices, xyz_rotated diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 44eaf69..dbf230c 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -16,98 +16,104 @@ def test_ir(): return ir -def test_split_array(): +@pytest.fixture +def n_pix(): + """Get sample n_pix value.""" + return 128 + + +def test_split_array(test_ir, n_pix): """Test splitting of array in two halves.""" - arr = np.zeros(4) - arr1, arr2 = test_ir().split_array(arr) - assert arr1.shape == (2,) - assert arr2.shape == (2,) + arr = np.zeros(n_pix) + arr1, arr2 = test_ir.split_array(arr) + assert arr1.shape == (n_pix / 2,) + assert arr2.shape == (n_pix / 2,) -def test_build_ctf_array(): +def test_build_ctf_array(test_ir): """Test bulding of arbitrary CTF array.""" - ctfs = test_ir().build_ctf_array() + ctfs = test_ir.build_ctf_array() assert ctfs.shape == (0, 0, 0) -def test_grid_SO3_uniform(): +def test_grid_SO3_uniform(test_ir): """Test generation of rotations across SO(3).""" - rots = test_ir().grid_SO3_uniform(2) + rots = test_ir.grid_SO3_uniform(2) assert rots.shape == (2, 3, 3) -def test_generate_xy_plane(): +def test_generate_xy_plane(test_ir, n_pix): """Test generation of xy plane.""" - xy_plane = test_ir().generate_xy_plane(2) - assert xy_plane.shape == (2, 2, 3) + xy_plane = test_ir.generate_xy_plane(n_pix) + assert xy_plane.shape == (n_pix, n_pix, 3) -def test_generate_slices(): +def test_generate_slices(test_ir): """Test generation of slices.""" - map_3d = np.ones(2, 2, 2) - rots = test_ir().grid_SO3_uniform(2) - xy_plane = test_ir().generate_xy_plane(2) + map_3d = np.ones((2, 2, 2)) + rots = test_ir.grid_SO3_uniform(2) + xy_plane = test_ir.generate_xy_plane(2) - slices, xyz_rotated = test_ir().generate_slices(map_3d, rots) + slices, xyz_rotated = test_ir.generate_slices(map_3d, rots) assert xy_plane.shape == (4, 3) assert slices.shape == (2, 2, 2) assert xyz_rotated.shape == (2, 2, 3) -def test_apply_ctf_to_slice(): +def test_apply_ctf_to_slice(test_ir): """Test convolution of slice with CTF.""" - particle_slice = np.ones(2, 2) - ctf = np.ones(2, 2) - convolved = test_ir().apply_ctf_to_slice(particle_slice, ctf) + particle_slice = np.ones((2, 2)) + ctf = np.ones((2, 2)) + convolved = test_ir.apply_ctf_to_slice(particle_slice, ctf) assert convolved.shape == (2, 2) -def test_compute_bayesian_weights(): +def test_compute_bayesian_weights(test_ir): """Test computation of bayesian weights. For use under Gaussian white noise model. """ - particle = np.ones(1, 2, 2) - slices = np.ones(2, 2, 2) - bayesian_weights = test_ir().compute_bayesian_weights(particle, slices) + particle = np.ones((1, 2, 2)) + slices = np.ones((2, 2, 2)) + bayesian_weights = test_ir.compute_bayesian_weights(particle, slices) assert bayesian_weights.shape == (2,) -def test_apply_wiener_filter(): +def test_apply_wiener_filter(test_ir): """Test application of Wiener filter to particle projection.""" - projection = np.ones(2, 2) - ctf = np.zeros(2, 2) + projection = np.ones((2, 2)) + ctf = np.zeros((2, 2)) small_number = 0.01 - projection_wfilter_f = test_ir().apply_wiener_filter(projection, ctf, small_number) + projection_wfilter_f = test_ir.apply_wiener_filter(projection, ctf, small_number) assert projection_wfilter_f.shape == (2, 2) -def test_insert_slice(): +def test_insert_slice(test_ir): """Test insertion of slice.""" n_pix = 2 - particle_slice = np.ones(n_pix, n_pix) - xyz = test_ir().generate_xy_plane(2, 2) + particle_slice = np.ones((n_pix, n_pix)) + xyz = test_ir.generate_xy_plane(2) - inserted, count = test_ir().insert_slice(particle_slice, xyz, n_pix) + inserted, count = test_ir.insert_slice(particle_slice, xyz, n_pix) assert inserted.shape == (2, 2, 2) assert count.shape == (2, 2, 2) -def test_compute_fsc(): +def test_compute_fsc(test_ir): """Test computation of FSC.""" - map_1 = np.ones(2, 2, 2) + map_1 = np.ones((2, 2, 2)) - fsc_1 = test_ir().compute_fsc(map_1) + fsc_1 = test_ir.compute_fsc(map_1) assert fsc_1.shape == (1,) -def test_expand_1d_to_3d(): +def test_expand_1d_to_3d(test_ir): """Test expansion of 1D array into spherical shell.""" arr1d = np.ones(1) - spherical = test_ir().expand_1d_to_3d(arr1d) + spherical = test_ir.expand_1d_to_3d(arr1d) assert spherical.shape == (2, 2, 2) From ee0ef8f832d883e963cab87b959a05adb7f23935 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Thu, 24 Mar 2022 08:02:54 +0000 Subject: [PATCH 056/148] Format code with black --- iterative_refinement/expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index 2edec9a..244c79d 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -160,7 +160,7 @@ def generate_slices(map_3d_f, xy_plane, n_pix, rots): map_3d_f = np.ones_like(map_3d_f) xyz_rotated = np.ones_like(xy_plane) - size = n_rotations * n_pix ** 2 + size = n_rotations * n_pix**2 slices = np.random.normal(size=size) slices.reshape(n_rotations, n_pix, n_pix) return slices, xyz_rotated From 7141f373d9fe4b00d609c69d0e5db3babb82c6e3 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 10:15:07 -0700 Subject: [PATCH 057/148] Reworked tests to use consistent array sizing --- tests/test_expectation_maximization.py | 59 +++++++++++++------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index dbf230c..2c1fe1f 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -9,17 +9,18 @@ @pytest.fixture def test_ir(): """Instantiate IterativeRefinement class for testing.""" - my_list = [] - my_dict = {} + map = np.zeros((n_pix, n_pix, n_pix)) + particles = np.zeros((n_pix, n_pix, n_pix)) + ctf_info = [{},] * n_pix itr = 7 - ir = em.IterativeRefinement(my_list, my_dict, itr) + ir = em.IterativeRefinement(map, particles, ctf_info, itr) return ir @pytest.fixture def n_pix(): """Get sample n_pix value.""" - return 128 + return 2 def test_split_array(test_ir, n_pix): @@ -33,13 +34,13 @@ def test_split_array(test_ir, n_pix): def test_build_ctf_array(test_ir): """Test bulding of arbitrary CTF array.""" ctfs = test_ir.build_ctf_array() - assert ctfs.shape == (0, 0, 0) + assert ctfs.shape == (n_pix, n_pix, n_pix) def test_grid_SO3_uniform(test_ir): """Test generation of rotations across SO(3).""" - rots = test_ir.grid_SO3_uniform(2) - assert rots.shape == (2, 3, 3) + rots = test_ir.grid_SO3_uniform(n_pix) + assert rots.shape == (n_pix, n_pix, n_pix) def test_generate_xy_plane(test_ir, n_pix): @@ -50,24 +51,24 @@ def test_generate_xy_plane(test_ir, n_pix): def test_generate_slices(test_ir): """Test generation of slices.""" - map_3d = np.ones((2, 2, 2)) - rots = test_ir.grid_SO3_uniform(2) - xy_plane = test_ir.generate_xy_plane(2) + map_3d = np.ones((n_pix, n_pix, n_pix)) + rots = test_ir.grid_SO3_uniform(n_pix) + xy_plane = test_ir.generate_xy_plane(n_pix) - slices, xyz_rotated = test_ir.generate_slices(map_3d, rots) + slices, xyz_rotated = test_ir.generate_slices(map_3d, xy_plane, n_pix, rots) - assert xy_plane.shape == (4, 3) - assert slices.shape == (2, 2, 2) - assert xyz_rotated.shape == (2, 2, 3) + assert xy_plane.shape == (n_pix * n_pix, 3) + assert slices.shape == (n_pix, n_pix, n_pix) + assert xyz_rotated.shape == (n_pix, n_pix, 3) def test_apply_ctf_to_slice(test_ir): """Test convolution of slice with CTF.""" - particle_slice = np.ones((2, 2)) - ctf = np.ones((2, 2)) + particle_slice = np.ones((n_pix, n_pix)) + ctf = np.ones((n_pix, n_pix)) convolved = test_ir.apply_ctf_to_slice(particle_slice, ctf) - assert convolved.shape == (2, 2) + assert convolved.shape == (n_pix, n_pix) def test_compute_bayesian_weights(test_ir): @@ -75,37 +76,37 @@ def test_compute_bayesian_weights(test_ir): For use under Gaussian white noise model. """ - particle = np.ones((1, 2, 2)) - slices = np.ones((2, 2, 2)) + particle = np.ones((1, n_pix, n_pix)) + slices = np.ones((n_pix, n_pix, n_pix)) bayesian_weights = test_ir.compute_bayesian_weights(particle, slices) - assert bayesian_weights.shape == (2,) + assert bayesian_weights.shape == (n_pix,) def test_apply_wiener_filter(test_ir): """Test application of Wiener filter to particle projection.""" - projection = np.ones((2, 2)) - ctf = np.zeros((2, 2)) + projection = np.ones((n_pix, n_pix)) + ctf = np.zeros((n_pix, n_pix)) small_number = 0.01 projection_wfilter_f = test_ir.apply_wiener_filter(projection, ctf, small_number) - assert projection_wfilter_f.shape == (2, 2) + assert projection_wfilter_f.shape == (n_pix, n_pix) def test_insert_slice(test_ir): """Test insertion of slice.""" - n_pix = 2 + n_pix = n_pix particle_slice = np.ones((n_pix, n_pix)) - xyz = test_ir.generate_xy_plane(2) + xyz = test_ir.generate_xy_plane(n_pix) inserted, count = test_ir.insert_slice(particle_slice, xyz, n_pix) - assert inserted.shape == (2, 2, 2) - assert count.shape == (2, 2, 2) + assert inserted.shape == (n_pix, n_pix, n_pix) + assert count.shape == (n_pix, n_pix, n_pix) def test_compute_fsc(test_ir): """Test computation of FSC.""" - map_1 = np.ones((2, 2, 2)) + map_1 = np.ones((n_pix, n_pix, n_pix)) fsc_1 = test_ir.compute_fsc(map_1) assert fsc_1.shape == (1,) @@ -116,4 +117,4 @@ def test_expand_1d_to_3d(test_ir): arr1d = np.ones(1) spherical = test_ir.expand_1d_to_3d(arr1d) - assert spherical.shape == (2, 2, 2) + assert spherical.shape == (n_pix, n_pix, n_pix) From 5d0d1a1dc2442fe2ac00b3174146ff64172003d1 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 10:17:47 -0700 Subject: [PATCH 058/148] Tab snuck in --- tests/test_expectation_maximization.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 2c1fe1f..48b4ff1 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -9,9 +9,9 @@ @pytest.fixture def test_ir(): """Instantiate IterativeRefinement class for testing.""" - map = np.zeros((n_pix, n_pix, n_pix)) + map = np.zeros((n_pix, n_pix, n_pix)) particles = np.zeros((n_pix, n_pix, n_pix)) - ctf_info = [{},] * n_pix + ctf_info = [{},] * n_pix itr = 7 ir = em.IterativeRefinement(map, particles, ctf_info, itr) return ir From 7289762aeddd547e5aeb354381cf08fbfeb5faa4 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Thu, 24 Mar 2022 17:17:52 +0000 Subject: [PATCH 059/148] Format code with black --- tests/test_expectation_maximization.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 48b4ff1..86544c0 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -11,7 +11,9 @@ def test_ir(): """Instantiate IterativeRefinement class for testing.""" map = np.zeros((n_pix, n_pix, n_pix)) particles = np.zeros((n_pix, n_pix, n_pix)) - ctf_info = [{},] * n_pix + ctf_info = [ + {}, + ] * n_pix itr = 7 ir = em.IterativeRefinement(map, particles, ctf_info, itr) return ir From f1bd4142bfdf7016792adac2d7151ce7115136b2 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 10:21:39 -0700 Subject: [PATCH 060/148] Comment change to force checks --- tests/test_expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 86544c0..c9fb560 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -21,7 +21,7 @@ def test_ir(): @pytest.fixture def n_pix(): - """Get sample n_pix value.""" + """Get sample n_pix value for consistency.""" return 2 From 756adbed960ce14bb704a1f1a5adb091123ad0a8 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 10:23:13 -0700 Subject: [PATCH 061/148] Fixes to tests --- tests/test_expectation_maximization.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index c9fb560..f6986e1 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -9,13 +9,13 @@ @pytest.fixture def test_ir(): """Instantiate IterativeRefinement class for testing.""" - map = np.zeros((n_pix, n_pix, n_pix)) + map_3d = np.zeros((n_pix, n_pix, n_pix)) particles = np.zeros((n_pix, n_pix, n_pix)) ctf_info = [ {}, ] * n_pix itr = 7 - ir = em.IterativeRefinement(map, particles, ctf_info, itr) + ir = em.IterativeRefinement(map_3d, particles, ctf_info, itr) return ir @@ -97,7 +97,6 @@ def test_apply_wiener_filter(test_ir): def test_insert_slice(test_ir): """Test insertion of slice.""" - n_pix = n_pix particle_slice = np.ones((n_pix, n_pix)) xyz = test_ir.generate_xy_plane(n_pix) From 40a6fb0189ddcb86b1a937006c9eb307c303af3a Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 10:44:03 -0700 Subject: [PATCH 062/148] Refactored tests a bit --- tests/test_expectation_maximization.py | 76 +++++++++++++------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index f6986e1..e67dd81 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -9,68 +9,68 @@ @pytest.fixture def test_ir(): """Instantiate IterativeRefinement class for testing.""" - map_3d = np.zeros((n_pix, n_pix, n_pix)) - particles = np.zeros((n_pix, n_pix, n_pix)) + map_3d = np.zeros((n(), n(), n())) + particles = np.zeros((n(), n(), n())) ctf_info = [ {}, - ] * n_pix + ] * n() itr = 7 ir = em.IterativeRefinement(map_3d, particles, ctf_info, itr) return ir @pytest.fixture -def n_pix(): - """Get sample n_pix value for consistency.""" +def n(): + """Get sample n() value for consistency.""" return 2 -def test_split_array(test_ir, n_pix): +def test_split_array(test_ir, n()): """Test splitting of array in two halves.""" - arr = np.zeros(n_pix) + arr = np.zeros(n()) arr1, arr2 = test_ir.split_array(arr) - assert arr1.shape == (n_pix / 2,) - assert arr2.shape == (n_pix / 2,) + assert arr1.shape == (n() / 2,) + assert arr2.shape == (n() / 2,) def test_build_ctf_array(test_ir): """Test bulding of arbitrary CTF array.""" ctfs = test_ir.build_ctf_array() - assert ctfs.shape == (n_pix, n_pix, n_pix) + assert ctfs.shape == (n(), n(), n()) def test_grid_SO3_uniform(test_ir): """Test generation of rotations across SO(3).""" - rots = test_ir.grid_SO3_uniform(n_pix) - assert rots.shape == (n_pix, n_pix, n_pix) + rots = test_ir.grid_SO3_uniform(n()) + assert rots.shape == (n(), n(), n()) -def test_generate_xy_plane(test_ir, n_pix): +def test_generate_xy_plane(test_ir, n()): """Test generation of xy plane.""" - xy_plane = test_ir.generate_xy_plane(n_pix) - assert xy_plane.shape == (n_pix, n_pix, 3) + xy_plane = test_ir.generate_xy_plane(n()) + assert xy_plane.shape == (n(), n(), 3) def test_generate_slices(test_ir): """Test generation of slices.""" - map_3d = np.ones((n_pix, n_pix, n_pix)) - rots = test_ir.grid_SO3_uniform(n_pix) - xy_plane = test_ir.generate_xy_plane(n_pix) + map_3d = np.ones((n(), n(), n())) + rots = test_ir.grid_SO3_uniform(n()) + xy_plane = test_ir.generate_xy_plane(n()) - slices, xyz_rotated = test_ir.generate_slices(map_3d, xy_plane, n_pix, rots) + slices, xyz_rotated = test_ir.generate_slices(map_3d, xy_plane, n(), rots) - assert xy_plane.shape == (n_pix * n_pix, 3) - assert slices.shape == (n_pix, n_pix, n_pix) - assert xyz_rotated.shape == (n_pix, n_pix, 3) + assert xy_plane.shape == (n() * n(), 3) + assert slices.shape == (n(), n(), n()) + assert xyz_rotated.shape == (n(), n(), 3) def test_apply_ctf_to_slice(test_ir): """Test convolution of slice with CTF.""" - particle_slice = np.ones((n_pix, n_pix)) - ctf = np.ones((n_pix, n_pix)) + particle_slice = np.ones((n(), n())) + ctf = np.ones((n(), n())) convolved = test_ir.apply_ctf_to_slice(particle_slice, ctf) - assert convolved.shape == (n_pix, n_pix) + assert convolved.shape == (n(), n()) def test_compute_bayesian_weights(test_ir): @@ -78,36 +78,36 @@ def test_compute_bayesian_weights(test_ir): For use under Gaussian white noise model. """ - particle = np.ones((1, n_pix, n_pix)) - slices = np.ones((n_pix, n_pix, n_pix)) + particle = np.ones((1, n(), n())) + slices = np.ones((n(), n(), n())) bayesian_weights = test_ir.compute_bayesian_weights(particle, slices) - assert bayesian_weights.shape == (n_pix,) + assert bayesian_weights.shape == (n(),) def test_apply_wiener_filter(test_ir): """Test application of Wiener filter to particle projection.""" - projection = np.ones((n_pix, n_pix)) - ctf = np.zeros((n_pix, n_pix)) + projection = np.ones((n(), n())) + ctf = np.zeros((n(), n())) small_number = 0.01 projection_wfilter_f = test_ir.apply_wiener_filter(projection, ctf, small_number) - assert projection_wfilter_f.shape == (n_pix, n_pix) + assert projection_wfilter_f.shape == (n(), n()) def test_insert_slice(test_ir): """Test insertion of slice.""" - particle_slice = np.ones((n_pix, n_pix)) - xyz = test_ir.generate_xy_plane(n_pix) + particle_slice = np.ones((n(), n())) + xyz = test_ir.generate_xy_plane(n()) - inserted, count = test_ir.insert_slice(particle_slice, xyz, n_pix) - assert inserted.shape == (n_pix, n_pix, n_pix) - assert count.shape == (n_pix, n_pix, n_pix) + inserted, count = test_ir.insert_slice(particle_slice, xyz, n()) + assert inserted.shape == (n(), n(), n()) + assert count.shape == (n(), n(), n()) def test_compute_fsc(test_ir): """Test computation of FSC.""" - map_1 = np.ones((n_pix, n_pix, n_pix)) + map_1 = np.ones((n(), n(), n())) fsc_1 = test_ir.compute_fsc(map_1) assert fsc_1.shape == (1,) @@ -118,4 +118,4 @@ def test_expand_1d_to_3d(test_ir): arr1d = np.ones(1) spherical = test_ir.expand_1d_to_3d(arr1d) - assert spherical.shape == (n_pix, n_pix, n_pix) + assert spherical.shape == (n(), n(), n()) From 5c74fb349068b8931248ab2dc0fcb15a0bcc5242 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 10:47:55 -0700 Subject: [PATCH 063/148] Fix to test_split_array --- tests/test_expectation_maximization.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index e67dd81..7c4e8a9 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -25,12 +25,12 @@ def n(): return 2 -def test_split_array(test_ir, n()): +def test_split_array(test_ir, k): """Test splitting of array in two halves.""" - arr = np.zeros(n()) + arr = np.zeros(k) arr1, arr2 = test_ir.split_array(arr) - assert arr1.shape == (n() / 2,) - assert arr2.shape == (n() / 2,) + assert arr1.shape == (k / 2,) + assert arr2.shape == (k / 2,) def test_build_ctf_array(test_ir): From c210d36a382cbfaa5c77aaeb55a78fb997575f30 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 10:50:22 -0700 Subject: [PATCH 064/148] Fix to test_generate_xy_plane --- tests/test_expectation_maximization.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 7c4e8a9..5147ad2 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -45,10 +45,10 @@ def test_grid_SO3_uniform(test_ir): assert rots.shape == (n(), n(), n()) -def test_generate_xy_plane(test_ir, n()): +def test_generate_xy_plane(test_ir, k): """Test generation of xy plane.""" - xy_plane = test_ir.generate_xy_plane(n()) - assert xy_plane.shape == (n(), n(), 3) + xy_plane = test_ir.generate_xy_plane(k) + assert xy_plane.shape == (k, k, 3) def test_generate_slices(test_ir): From 8753a1fd3b8c56482871bf76f146c4dee5d9f44f Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 10:53:23 -0700 Subject: [PATCH 065/148] Docstring fix --- tests/test_expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 5147ad2..47238e0 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -21,7 +21,7 @@ def test_ir(): @pytest.fixture def n(): - """Get sample n() value for consistency.""" + """Get test array size value for consistency.""" return 2 From 1ce2209b75f9bf55207cc6f75ac7d1fb06b76802 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 10:58:17 -0700 Subject: [PATCH 066/148] Deepsource tricked me --- tests/test_expectation_maximization.py | 58 +++++++++++++------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 47238e0..56f3734 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -9,11 +9,11 @@ @pytest.fixture def test_ir(): """Instantiate IterativeRefinement class for testing.""" - map_3d = np.zeros((n(), n(), n())) - particles = np.zeros((n(), n(), n())) + map_3d = np.zeros((n, n, n)) + particles = np.zeros((n, n, n)) ctf_info = [ {}, - ] * n() + ] * n itr = 7 ir = em.IterativeRefinement(map_3d, particles, ctf_info, itr) return ir @@ -36,13 +36,13 @@ def test_split_array(test_ir, k): def test_build_ctf_array(test_ir): """Test bulding of arbitrary CTF array.""" ctfs = test_ir.build_ctf_array() - assert ctfs.shape == (n(), n(), n()) + assert ctfs.shape == (n, n, n) def test_grid_SO3_uniform(test_ir): """Test generation of rotations across SO(3).""" - rots = test_ir.grid_SO3_uniform(n()) - assert rots.shape == (n(), n(), n()) + rots = test_ir.grid_SO3_uniform(n) + assert rots.shape == (n, n, n) def test_generate_xy_plane(test_ir, k): @@ -53,24 +53,24 @@ def test_generate_xy_plane(test_ir, k): def test_generate_slices(test_ir): """Test generation of slices.""" - map_3d = np.ones((n(), n(), n())) - rots = test_ir.grid_SO3_uniform(n()) - xy_plane = test_ir.generate_xy_plane(n()) + map_3d = np.ones((n, n, n)) + rots = test_ir.grid_SO3_uniform(n) + xy_plane = test_ir.generate_xy_plane(n) - slices, xyz_rotated = test_ir.generate_slices(map_3d, xy_plane, n(), rots) + slices, xyz_rotated = test_ir.generate_slices(map_3d, xy_plane, n, rots) - assert xy_plane.shape == (n() * n(), 3) - assert slices.shape == (n(), n(), n()) - assert xyz_rotated.shape == (n(), n(), 3) + assert xy_plane.shape == (n * n, 3) + assert slices.shape == (n, n, n) + assert xyz_rotated.shape == (n, n, 3) def test_apply_ctf_to_slice(test_ir): """Test convolution of slice with CTF.""" - particle_slice = np.ones((n(), n())) - ctf = np.ones((n(), n())) + particle_slice = np.ones((n, n)) + ctf = np.ones((n, n)) convolved = test_ir.apply_ctf_to_slice(particle_slice, ctf) - assert convolved.shape == (n(), n()) + assert convolved.shape == (n, n) def test_compute_bayesian_weights(test_ir): @@ -78,36 +78,36 @@ def test_compute_bayesian_weights(test_ir): For use under Gaussian white noise model. """ - particle = np.ones((1, n(), n())) - slices = np.ones((n(), n(), n())) + particle = np.ones((1, n, n)) + slices = np.ones((n, n, n)) bayesian_weights = test_ir.compute_bayesian_weights(particle, slices) - assert bayesian_weights.shape == (n(),) + assert bayesian_weights.shape == (n,) def test_apply_wiener_filter(test_ir): """Test application of Wiener filter to particle projection.""" - projection = np.ones((n(), n())) - ctf = np.zeros((n(), n())) + projection = np.ones((n, n)) + ctf = np.zeros((n, n)) small_number = 0.01 projection_wfilter_f = test_ir.apply_wiener_filter(projection, ctf, small_number) - assert projection_wfilter_f.shape == (n(), n()) + assert projection_wfilter_f.shape == (n, n) def test_insert_slice(test_ir): """Test insertion of slice.""" - particle_slice = np.ones((n(), n())) - xyz = test_ir.generate_xy_plane(n()) + particle_slice = np.ones((n, n)) + xyz = test_ir.generate_xy_plane(n) - inserted, count = test_ir.insert_slice(particle_slice, xyz, n()) - assert inserted.shape == (n(), n(), n()) - assert count.shape == (n(), n(), n()) + inserted, count = test_ir.insert_slice(particle_slice, xyz, n) + assert inserted.shape == (n, n, n) + assert count.shape == (n, n, n) def test_compute_fsc(test_ir): """Test computation of FSC.""" - map_1 = np.ones((n(), n(), n())) + map_1 = np.ones((n, n, n)) fsc_1 = test_ir.compute_fsc(map_1) assert fsc_1.shape == (1,) @@ -118,4 +118,4 @@ def test_expand_1d_to_3d(test_ir): arr1d = np.ones(1) spherical = test_ir.expand_1d_to_3d(arr1d) - assert spherical.shape == (n(), n(), n()) + assert spherical.shape == (n, n, n) From 0673ebb57241d999bd5cbb639bb269dc90951ace Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 11:02:15 -0700 Subject: [PATCH 067/148] Dosctring to force checks --- tests/test_expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 56f3734..c21c540 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -34,7 +34,7 @@ def test_split_array(test_ir, k): def test_build_ctf_array(test_ir): - """Test bulding of arbitrary CTF array.""" + """Test bulding arbitrary CTF array.""" ctfs = test_ir.build_ctf_array() assert ctfs.shape == (n, n, n) From ae8c96f5aa33e8f107f93f8bff719bc2be2b04f8 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 11:06:46 -0700 Subject: [PATCH 068/148] Fixture fix: --- tests/test_expectation_maximization.py | 45 +++++++++++++------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index c21c540..c7bb4a2 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -7,7 +7,12 @@ @pytest.fixture -def test_ir(): +def n(): + """Get test array size value for consistency.""" + return 2 + +@pytest.fixture +def test_ir(n): """Instantiate IterativeRefinement class for testing.""" map_3d = np.zeros((n, n, n)) particles = np.zeros((n, n, n)) @@ -19,39 +24,33 @@ def test_ir(): return ir -@pytest.fixture -def n(): - """Get test array size value for consistency.""" - return 2 - - -def test_split_array(test_ir, k): +def test_split_array(test_ir, n): """Test splitting of array in two halves.""" - arr = np.zeros(k) + arr = np.zeros(n) arr1, arr2 = test_ir.split_array(arr) - assert arr1.shape == (k / 2,) - assert arr2.shape == (k / 2,) + assert arr1.shape == (n / 2,) + assert arr2.shape == (n / 2,) -def test_build_ctf_array(test_ir): +def test_build_ctf_array(test_ir, n): """Test bulding arbitrary CTF array.""" ctfs = test_ir.build_ctf_array() assert ctfs.shape == (n, n, n) -def test_grid_SO3_uniform(test_ir): +def test_grid_SO3_uniform(test_ir, n): """Test generation of rotations across SO(3).""" rots = test_ir.grid_SO3_uniform(n) assert rots.shape == (n, n, n) -def test_generate_xy_plane(test_ir, k): +def test_generate_xy_plane(test_ir, n): """Test generation of xy plane.""" - xy_plane = test_ir.generate_xy_plane(k) - assert xy_plane.shape == (k, k, 3) + xy_plane = test_ir.generate_xy_plane(n) + assert xy_plane.shape == (n, n, 3) -def test_generate_slices(test_ir): +def test_generate_slices(test_ir, n): """Test generation of slices.""" map_3d = np.ones((n, n, n)) rots = test_ir.grid_SO3_uniform(n) @@ -64,7 +63,7 @@ def test_generate_slices(test_ir): assert xyz_rotated.shape == (n, n, 3) -def test_apply_ctf_to_slice(test_ir): +def test_apply_ctf_to_slice(test_ir, n): """Test convolution of slice with CTF.""" particle_slice = np.ones((n, n)) ctf = np.ones((n, n)) @@ -73,7 +72,7 @@ def test_apply_ctf_to_slice(test_ir): assert convolved.shape == (n, n) -def test_compute_bayesian_weights(test_ir): +def test_compute_bayesian_weights(test_ir, n): """Test computation of bayesian weights. For use under Gaussian white noise model. @@ -85,7 +84,7 @@ def test_compute_bayesian_weights(test_ir): assert bayesian_weights.shape == (n,) -def test_apply_wiener_filter(test_ir): +def test_apply_wiener_filter(test_ir, n): """Test application of Wiener filter to particle projection.""" projection = np.ones((n, n)) ctf = np.zeros((n, n)) @@ -95,7 +94,7 @@ def test_apply_wiener_filter(test_ir): assert projection_wfilter_f.shape == (n, n) -def test_insert_slice(test_ir): +def test_insert_slice(test_ir, n): """Test insertion of slice.""" particle_slice = np.ones((n, n)) xyz = test_ir.generate_xy_plane(n) @@ -105,7 +104,7 @@ def test_insert_slice(test_ir): assert count.shape == (n, n, n) -def test_compute_fsc(test_ir): +def test_compute_fsc(test_ir, n): """Test computation of FSC.""" map_1 = np.ones((n, n, n)) @@ -113,7 +112,7 @@ def test_compute_fsc(test_ir): assert fsc_1.shape == (1,) -def test_expand_1d_to_3d(test_ir): +def test_expand_1d_to_3d(test_ir, n): """Test expansion of 1D array into spherical shell.""" arr1d = np.ones(1) spherical = test_ir.expand_1d_to_3d(arr1d) From 16a0b9002250d07b9dc97d7788ef905f9591c0b9 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Thu, 24 Mar 2022 18:06:53 +0000 Subject: [PATCH 069/148] Format code with black --- tests/test_expectation_maximization.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index c7bb4a2..51e28ee 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -11,6 +11,7 @@ def n(): """Get test array size value for consistency.""" return 2 + @pytest.fixture def test_ir(n): """Instantiate IterativeRefinement class for testing.""" From 3c3595c78ad3293ffffab863d6446ee11ebf35c8 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Thu, 24 Mar 2022 18:07:02 +0000 Subject: [PATCH 070/148] Format code with black --- tests/test_expectation_maximization.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 51e28ee..9eb9097 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -12,6 +12,7 @@ def n(): return 2 + @pytest.fixture def test_ir(n): """Instantiate IterativeRefinement class for testing.""" From a5bc9eedaf1f53cfb1effff78e4eb7b120349192 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Thu, 24 Mar 2022 18:07:10 +0000 Subject: [PATCH 071/148] Format code with black --- tests/test_expectation_maximization.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 9eb9097..51e28ee 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -12,7 +12,6 @@ def n(): return 2 - @pytest.fixture def test_ir(n): """Instantiate IterativeRefinement class for testing.""" From b76ec986bc84e7dd59d330ab382f78b7f68146d6 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Thu, 24 Mar 2022 18:07:19 +0000 Subject: [PATCH 072/148] Format code with black --- tests/test_expectation_maximization.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 51e28ee..c7bb4a2 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -11,7 +11,6 @@ def n(): """Get test array size value for consistency.""" return 2 - @pytest.fixture def test_ir(n): """Instantiate IterativeRefinement class for testing.""" From b58ba2fe78e3e40c5427cca7f04d855a3001b79f Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 11:08:00 -0700 Subject: [PATCH 073/148] Forcing checks again... --- tests/test_expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index c7bb4a2..c8de08f 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -39,7 +39,7 @@ def test_build_ctf_array(test_ir, n): def test_grid_SO3_uniform(test_ir, n): - """Test generation of rotations across SO(3).""" + """Test generation of rotations in SO(3).""" rots = test_ir.grid_SO3_uniform(n) assert rots.shape == (n, n, n) From 98a879c3b84b02539a45ae216a125552e18d1d22 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Thu, 24 Mar 2022 18:10:28 +0000 Subject: [PATCH 074/148] Format code with black --- tests/test_expectation_maximization.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index c8de08f..cf0af55 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -11,6 +11,7 @@ def n(): """Get test array size value for consistency.""" return 2 + @pytest.fixture def test_ir(n): """Instantiate IterativeRefinement class for testing.""" From a1dc0cdf13ab7ff115519b9f34f31c2502d6811c Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 11:15:28 -0700 Subject: [PATCH 075/148] Better ctf_info for tests --- tests/test_expectation_maximization.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index c8de08f..c105e73 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -14,10 +14,21 @@ def n(): @pytest.fixture def test_ir(n): """Instantiate IterativeRefinement class for testing.""" + ex_ctf = { + s : np.ones(n, n), + a : np.ones(n, n), + def1 : 1.0, + def2 : 1.0, + angast : 0.1, + kv : 0.1, + cs : 0.1, + bf : 0.1, + lp : 0.1 + } map_3d = np.zeros((n, n, n)) particles = np.zeros((n, n, n)) ctf_info = [ - {}, + ex_ctf, ] * n itr = 7 ir = em.IterativeRefinement(map_3d, particles, ctf_info, itr) From 4bcf15a965739e71a1860e354235962ba9db71ba Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Thu, 24 Mar 2022 18:16:01 +0000 Subject: [PATCH 076/148] Format code with black --- tests/test_expectation_maximization.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 0245bd0..bd6c480 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -16,15 +16,15 @@ def n(): def test_ir(n): """Instantiate IterativeRefinement class for testing.""" ex_ctf = { - s : np.ones(n, n), - a : np.ones(n, n), - def1 : 1.0, - def2 : 1.0, - angast : 0.1, - kv : 0.1, - cs : 0.1, - bf : 0.1, - lp : 0.1 + s: np.ones(n, n), + a: np.ones(n, n), + def1: 1.0, + def2: 1.0, + angast: 0.1, + kv: 0.1, + cs: 0.1, + bf: 0.1, + lp: 0.1, } map_3d = np.zeros((n, n, n)) particles = np.zeros((n, n, n)) From ecee780e4f96f297d071d68f8bbcac01da314000 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 11:18:49 -0700 Subject: [PATCH 077/148] Strings not variables... --- tests/test_expectation_maximization.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 0245bd0..06318fb 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -16,15 +16,15 @@ def n(): def test_ir(n): """Instantiate IterativeRefinement class for testing.""" ex_ctf = { - s : np.ones(n, n), - a : np.ones(n, n), - def1 : 1.0, - def2 : 1.0, - angast : 0.1, - kv : 0.1, - cs : 0.1, - bf : 0.1, - lp : 0.1 + 's' : np.ones(n, n), + 'a' : np.ones(n, n), + 'def1' : 1.0, + 'def2' : 1.0, + 'angast' : 0.1, + 'kv' : 0.1, + 'cs' : 0.1, + 'bf' : 0.1, + 'lp' : 0.1 } map_3d = np.zeros((n, n, n)) particles = np.zeros((n, n, n)) From 0ac923c2d9fb74626aeff05a093fd3a50674ea4d Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Thu, 24 Mar 2022 18:19:48 +0000 Subject: [PATCH 078/148] Format code with black --- tests/test_expectation_maximization.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 1a5d1a2..1f761b1 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -16,15 +16,15 @@ def n(): def test_ir(n): """Instantiate IterativeRefinement class for testing.""" ex_ctf = { - 's': np.ones(n, n), - 'a': np.ones(n, n), - 'def1': 1.0, - 'def2': 1.0, - 'angast': 0.1, - 'kv': 0.1, - 'cs': 0.1, - 'bf': 0.1, - 'lp': 0.1 + "s": np.ones(n, n), + "a": np.ones(n, n), + "def1": 1.0, + "def2": 1.0, + "angast": 0.1, + "kv": 0.1, + "cs": 0.1, + "bf": 0.1, + "lp": 0.1, } map_3d = np.zeros((n, n, n)) particles = np.zeros((n, n, n)) From cb7b069bf5d7aead7b59ce7729fbbb77e191b7f6 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 11:23:30 -0700 Subject: [PATCH 079/148] Fix --- tests/test_expectation_maximization.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 1a5d1a2..3d8341e 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -16,8 +16,8 @@ def n(): def test_ir(n): """Instantiate IterativeRefinement class for testing.""" ex_ctf = { - 's': np.ones(n, n), - 'a': np.ones(n, n), + 's': np.ones((n, n)), + 'a': np.ones((n, n)), 'def1': 1.0, 'def2': 1.0, 'angast': 0.1, From 966ed3c31ebfdac15fe9abe1b9cabe770cf4b832 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 11:38:24 -0700 Subject: [PATCH 080/148] test fixes --- iterative_refinement/expectation_maximization.py | 8 ++++---- tests/test_expectation_maximization.py | 9 +++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index 244c79d..bcc899c 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -120,7 +120,7 @@ def generate_xy_plane(n_pix): # See how meshgrid and generate coordinates functions used # https://github.com/geoffwoollard/compSPI/blob/stash_simulate/src/simulate.py#L96 - xy_plane = np.ones((n_pix, n_pix, 3)) + xy_plane = np.ones((n_pix * n_pix, 3)) return xy_plane @staticmethod @@ -202,7 +202,7 @@ def compute_bayesian_weights(particle, slices): """ n_slices = slices.shape[0] particle = np.ones_like(particle) - bayes_factors = np.random.normal(n_slices) + bayes_factors = np.random.normal(size = n_slices) return bayes_factors @staticmethod @@ -244,11 +244,11 @@ def insert_slice(slice_real, xyz, n_pix): ------- inserted_slice_3d : float64 arr Rotated slice in 3D voxel array. - Shape (n_pix, n_pix) + Shape (n_pix**2, 3) count_3d : arr Voxel array to count slice presence: 1 if slice present, otherwise 0. - Shape (n_pix, n_pix, n_pix) + Shape (n_pix**2, 3) """ inserted_slice_3d = slice_real shape = xyz.shape[0] diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index d3846cb..e831b29 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -47,13 +47,14 @@ def test_split_array(test_ir, n): def test_build_ctf_array(test_ir, n): """Test bulding arbitrary CTF array.""" ctfs = test_ir.build_ctf_array() - assert ctfs.shape == (n, n, n) + assert len(ctfs) == n + assert ctfs[0].shape == (n, n) def test_grid_SO3_uniform(test_ir, n): """Test generation of rotations in SO(3).""" rots = test_ir.grid_SO3_uniform(n) - assert rots.shape == (n, n, n) + assert rots.shape == (n, 3, 3) def test_generate_xy_plane(test_ir, n): @@ -112,8 +113,8 @@ def test_insert_slice(test_ir, n): xyz = test_ir.generate_xy_plane(n) inserted, count = test_ir.insert_slice(particle_slice, xyz, n) - assert inserted.shape == (n, n, n) - assert count.shape == (n, n, n) + assert inserted.shape == (n * n, 3) + assert count.shape == (n * n, 3) def test_compute_fsc(test_ir, n): From ccbd07b50976c817f32c962879257ccd0b22e317 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Thu, 24 Mar 2022 18:38:28 +0000 Subject: [PATCH 081/148] Format code with black --- iterative_refinement/expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index bcc899c..471c38a 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -202,7 +202,7 @@ def compute_bayesian_weights(particle, slices): """ n_slices = slices.shape[0] particle = np.ones_like(particle) - bayes_factors = np.random.normal(size = n_slices) + bayes_factors = np.random.normal(size=n_slices) return bayes_factors @staticmethod From 4858d54b5eeebc9027ab4cb4aa58a0acb6ba6491 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 11:43:44 -0700 Subject: [PATCH 082/148] Test fixes --- iterative_refinement/expectation_maximization.py | 4 ++-- tests/test_expectation_maximization.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index bcc899c..32f1f7c 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -250,9 +250,9 @@ def insert_slice(slice_real, xyz, n_pix): otherwise 0. Shape (n_pix**2, 3) """ - inserted_slice_3d = slice_real shape = xyz.shape[0] - count_3d = np.ones((shape, n_pix, n_pix)) + count_3d = np.ones((shape, 3)) + inserted_slice_3d = np.ones((shape, 3)) return inserted_slice_3d, count_3d @staticmethod diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index e831b29..6ce9e90 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -60,7 +60,7 @@ def test_grid_SO3_uniform(test_ir, n): def test_generate_xy_plane(test_ir, n): """Test generation of xy plane.""" xy_plane = test_ir.generate_xy_plane(n) - assert xy_plane.shape == (n, n, 3) + assert xy_plane.shape == (n * n, 3) def test_generate_slices(test_ir, n): @@ -73,7 +73,7 @@ def test_generate_slices(test_ir, n): assert xy_plane.shape == (n * n, 3) assert slices.shape == (n, n, n) - assert xyz_rotated.shape == (n, n, 3) + assert xyz_rotated.shape == (n ** 3,) def test_apply_ctf_to_slice(test_ir, n): From bc884980c8e2dcee85691233e103c929ebf56cbd Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 11:47:08 -0700 Subject: [PATCH 083/148] Force checks --- tests/test_expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 6ce9e90..5e54cdf 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -108,7 +108,7 @@ def test_apply_wiener_filter(test_ir, n): def test_insert_slice(test_ir, n): - """Test insertion of slice.""" + """Test insertion of particle slice.""" particle_slice = np.ones((n, n)) xyz = test_ir.generate_xy_plane(n) From ab014c53d0cdcf9f706555bee43e0198a878e8cd Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Thu, 24 Mar 2022 18:47:12 +0000 Subject: [PATCH 084/148] Format code with black --- tests/test_expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 5e54cdf..bb2cc79 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -73,7 +73,7 @@ def test_generate_slices(test_ir, n): assert xy_plane.shape == (n * n, 3) assert slices.shape == (n, n, n) - assert xyz_rotated.shape == (n ** 3,) + assert xyz_rotated.shape == (n**3,) def test_apply_ctf_to_slice(test_ir, n): From 656b5d2e573c256b4af224e599d9e21095524743 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 11:51:35 -0700 Subject: [PATCH 085/148] test fixes --- iterative_refinement/expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index 0bd2f4d..d0f1e9f 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -162,7 +162,7 @@ def generate_slices(map_3d_f, xy_plane, n_pix, rots): size = n_rotations * n_pix**2 slices = np.random.normal(size=size) - slices.reshape(n_rotations, n_pix, n_pix) + slices.reshape((n_rotations, n_pix, n_pix)) return slices, xyz_rotated @staticmethod From b9e72ad51bb7917b2576215659e9428f067f93b9 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 11:53:31 -0700 Subject: [PATCH 086/148] Forcing checks --- tests/test_expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index bb2cc79..a063c4e 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -77,7 +77,7 @@ def test_generate_slices(test_ir, n): def test_apply_ctf_to_slice(test_ir, n): - """Test convolution of slice with CTF.""" + """Test convolution of particle slice with CTF.""" particle_slice = np.ones((n, n)) ctf = np.ones((n, n)) convolved = test_ir.apply_ctf_to_slice(particle_slice, ctf) From 5577dedac3462417405982193261031522c458f9 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 11:56:31 -0700 Subject: [PATCH 087/148] Reshape fix --- iterative_refinement/expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iterative_refinement/expectation_maximization.py b/iterative_refinement/expectation_maximization.py index d0f1e9f..b643910 100644 --- a/iterative_refinement/expectation_maximization.py +++ b/iterative_refinement/expectation_maximization.py @@ -162,7 +162,7 @@ def generate_slices(map_3d_f, xy_plane, n_pix, rots): size = n_rotations * n_pix**2 slices = np.random.normal(size=size) - slices.reshape((n_rotations, n_pix, n_pix)) + slices = slices.reshape((n_rotations, n_pix, n_pix)) return slices, xyz_rotated @staticmethod From bb6e258fc2a7dee0c5b1d5488a566620c89b6ee7 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 11:59:30 -0700 Subject: [PATCH 088/148] Shape fix --- tests/test_expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index a063c4e..1abf260 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -73,7 +73,7 @@ def test_generate_slices(test_ir, n): assert xy_plane.shape == (n * n, 3) assert slices.shape == (n, n, n) - assert xyz_rotated.shape == (n**3,) + assert xyz_rotated.shape == (n * n, 3) def test_apply_ctf_to_slice(test_ir, n): From 81e5faac62270e6fb6d9d87c7e0435d321e9ff49 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 12:07:18 -0700 Subject: [PATCH 089/148] Structure changes --- reconstructSPI/__init__.py | 1 + .../iterative_refinement}/__init__.py | 0 .../iterative_refinement}/expectation_maximization.py | 0 3 files changed, 1 insertion(+) create mode 100644 reconstructSPI/__init__.py rename {iterative_refinement => reconstructSPI/iterative_refinement}/__init__.py (100%) rename {iterative_refinement => reconstructSPI/iterative_refinement}/expectation_maximization.py (100%) diff --git a/reconstructSPI/__init__.py b/reconstructSPI/__init__.py new file mode 100644 index 0000000..a16d370 --- /dev/null +++ b/reconstructSPI/__init__.py @@ -0,0 +1 @@ +"""Various particle reconstruction methods.""" diff --git a/iterative_refinement/__init__.py b/reconstructSPI/iterative_refinement/__init__.py similarity index 100% rename from iterative_refinement/__init__.py rename to reconstructSPI/iterative_refinement/__init__.py diff --git a/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py similarity index 100% rename from iterative_refinement/expectation_maximization.py rename to reconstructSPI/iterative_refinement/expectation_maximization.py From a2f4b52ee46cb199be6c10c7f2ebffb9f7f34bf1 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 12:08:09 -0700 Subject: [PATCH 090/148] Test framework adjustment --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 9240dcc..cfc5574 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -51,4 +51,4 @@ jobs: isort --profile black --check . - name: linting [flake8] run: | - flake8 iterative_refinement tests + flake8 reconstructSPI tests From 17f94cabb889a53940a0fa1d85c1a56f9bfb4622 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 12:09:53 -0700 Subject: [PATCH 091/148] Import change --- tests/test_expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 1abf260..e64742d 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -3,7 +3,7 @@ import numpy as np import pytest -from iterative_refinement import expectation_maximization as em +from reconstructSPI.iterative_refinement import expectation_maximization as em @pytest.fixture From 9f7ee213a8f3de012b3f99334bb480eedb236b99 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 12:53:01 -0700 Subject: [PATCH 092/148] Small typo fixes --- .flake8 | 2 +- .../iterative_refinement/expectation_maximization.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.flake8 b/.flake8 index cbc6363..ece9ae4 100644 --- a/.flake8 +++ b/.flake8 @@ -3,4 +3,4 @@ docstring-convention = numpy import_order_style = smarkets max-line-length = 88 extend-ignore = E203,I202,I100 -exclude = reconstructSPI/__init__.py,tests/__init__.py +exclude = reconstructSPI/__init__.py,reconstructSPI/iterative_refinement/__init.py,tests/__init__.py diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index b643910..ebac87b 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -1,11 +1,11 @@ -"""Iterative refinement with Bayesian expection maximization.""" +"""Iterative refinement with Bayesian expectation maximization.""" import numpy as np from simSPI.transfer import eval_ctf class IterativeRefinement: - """Perform iterative refinement with max likelihood esimation. + """Perform iterative refinement with max likelihood estimation. Parameters ---------- From 0d418b980b6476913dd00a4f9c8283835b33524c Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 13:09:30 -0700 Subject: [PATCH 093/148] Added a slightly more comprehensive split test --- .../iterative_refinement/expectation_maximization.py | 2 +- tests/test_expectation_maximization.py | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index ebac87b..17ec2f7 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -62,7 +62,7 @@ def split_array(arr): arr_1, arr_2 = arr[:idx_half], arr[idx_half:] if arr_1.shape[0] != arr_2.shape[0]: - arr_2 = arr[idx_half : 2 * idx_half] + arr_2 = arr[idx_half: 2 * idx_half] return arr_1, arr_2 diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index e64742d..19fd7a7 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -40,8 +40,13 @@ def test_split_array(test_ir, n): """Test splitting of array in two halves.""" arr = np.zeros(n) arr1, arr2 = test_ir.split_array(arr) - assert arr1.shape == (n / 2,) - assert arr2.shape == (n / 2,) + assert arr1.shape == (n // 2,) + assert arr2.shape == (n // 2,) + + arr = np.zeros(n + 1) + arr1, arr2 = test_ir.split_array(arr) + assert arr1.shape == ((n + 1) // 2,) + assert arr2.shape == ((n + 1) // 2,) def test_build_ctf_array(test_ir, n): From e6410cdb24cae91ba5c8b76f2f214a1b98316c43 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Thu, 24 Mar 2022 20:09:50 +0000 Subject: [PATCH 094/148] Format code with black --- reconstructSPI/iterative_refinement/expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index 17ec2f7..ebac87b 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -62,7 +62,7 @@ def split_array(arr): arr_1, arr_2 = arr[:idx_half], arr[idx_half:] if arr_1.shape[0] != arr_2.shape[0]: - arr_2 = arr[idx_half: 2 * idx_half] + arr_2 = arr[idx_half : 2 * idx_half] return arr_1, arr_2 From 511d105e39c258e2a7c0946ac4317f60fcd530f0 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 13:17:12 -0700 Subject: [PATCH 095/148] Added back references --- .../expectation_maximization.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index ebac87b..073a75e 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -34,6 +34,23 @@ class IterativeRefinement: fsc_1d : arr Final one dimensional fourier shell correlation. Shape (n_pix // 2,) + + References + ---------- + 1. Nelson, P. C. (2019). Chapter 12 : Single Particle + Reconstruction in Cryo-electron Microscopy. + In Physical Models of Living Systems (pp. 305–325). + https://repository.upenn.edu/physics_papers/656/ + 2. Scheres, S. H. W. (2012). RELION: Implementation of a + Bayesian approach to cryo-EM structure determination. + Journal of Structural Biology, 180(3), 519–530. + http://doi.org/10.1016/j.jsb.2012.09.006 + 3. Sigworth, F. J., Doerschuk, P. C., Carazo, J.-M., & Scheres, + S. H. W. (2010). + An Introduction to Maximum-Likelihood Methods in Cryo-EM. + In Methods in Enzymology (1st ed., Vol. 482, + pp. 263–294). Elsevier Inc. + http://doi.org/10.1016/S0076-6879(10)82011-7 """ def __init__(self, map_3d_init, particles, ctf_info, max_itr=7): From b575e13994a3f665521e042e055e8a2671408d0c Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 22:57:10 -0700 Subject: [PATCH 096/148] Added the big method --- .../expectation_maximization.py | 272 ++++++++++++++++-- tests/test_expectation_maximization.py | 27 +- 2 files changed, 276 insertions(+), 23 deletions(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index 073a75e..f8a0587 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -5,7 +5,7 @@ class IterativeRefinement: - """Perform iterative refinement with max likelihood estimation. + """Iterative refinement with max likelihood estimation. Parameters ---------- @@ -19,22 +19,6 @@ class IterativeRefinement: Each dict contains CTF k,v pairs per particle. Shape (n_particles,) - Returns - ------- - map_3d_update : arr - Current iteration of map. - Shape (n_pix, n_pix, n_pix) - map_3d_final : arr - Final updated map. - Shape (n_pix, n_pix, n_pix) - half_map_3d_final_1 : arr - Shape (n_pix, n_pix, n_pix) - half_map_3d_final_2 : arr - Shape (n_pix, n_pix, n_pix) - fsc_1d : arr - Final one dimensional fourier shell correlation. - Shape (n_pix // 2,) - References ---------- 1. Nelson, P. C. (2019). Chapter 12 : Single Particle @@ -59,6 +43,209 @@ def __init__(self, map_3d_init, particles, ctf_info, max_itr=7): self.ctf_info = ctf_info self.max_itr = max_itr + def iterative_refinement(self): + """Perform iterative refinement. + + Acts in a Bayesian expectation maximization setting, + i.e. maximum a posteriori estimation. + + Returns + ------- + map_3d_update : arr + Current iteration of map. + Shape (n_pix, n_pix, n_pix) + map_3d_final : arr + Final updated map. + Shape (n_pix, n_pix, n_pix) + half_map_3d_final_1 : arr + Shape (n_pix, n_pix, n_pix) + half_map_3d_final_2 : arr + Shape (n_pix, n_pix, n_pix) + fsc_1d : arr + Final one dimensional fourier shell correlation. + Shape (n_pix,) + """ + + particles_1, particles_2 = \ + IterativeRefinement.split_array(self.particles) + + ctfs = self.build_ctf_array() + ctfs_1, ctfs_2 = IterativeRefinement.split_array(ctfs) + + # work in Fourier space. So particles can stay in Fourier + # space the whole time. They are experimental measurements + # and are fixed in the algorithm + particles_f_1 = IterativeRefinement.fft_3d(particles_1) + particles_f_2 = IterativeRefinement.fft_3d(particles_2) + + n_pix = self.map_3d_init.shape[0] + # suggest 32 or 64 to start with. real data will be more like + # 128 or 256. Can have issues with ctf at small pixels and + # need to zero pad to avoid artefacts. Artefacts from ctf not + # going to zero at edges, and sinusoidal ctf rippling too fast + # can zero pad when do Fourier convolution (fft is on zero + # padded and larger sized array) + + half_map_3d_r_1, half_map_3d_r_2 = \ + self.map_3d_init.copy(), self.map_3d_init.copy() + # should diverge because different particles averaging in + + half_map_3d_f_1 = \ + IterativeRefinement.fft_3d(half_map_3d_r_1) + half_map_3d_f_2 = \ + IterativeRefinement.fft_3d(half_map_3d_r_2) + + for iteration in range(self.max_itr): + + half_map_3d_f_1 = \ + IterativeRefinement.fft_3d(half_map_3d_r_1) + half_map_3d_f_2 = \ + IterativeRefinement.fft_3d(half_map_3d_r_2) + + # align particles to 3D volume + # decide on granularity of rotations + # i.e. how finely the rotational SO(3) space is sampled + # in a grid search. Smarter method is branch and bound... + # perhaps can make grid up front of slices, and then only + # compute norms on finer grid later. So re-use slices. + + # def do_adaptive_grid_search(particle, map_3d): + # a la branch and bound. Not sure exactly how you decide + # how finely gridded to make it. Perhaps heuristics + # based on how well the signal agrees in half_map_1, + # half_map_2 (Fourier frequency). + + n_rotations = 1000 + rots = IterativeRefinement.grid_SO3_uniform(n_rotations) + + xy0_plane = IterativeRefinement.generate_xy_plane(n_pix) + + slices_1, xyz_rotated = \ + IterativeRefinement.generate_slices( + half_map_3d_f_1, xy0_plane, n_pix, rots + ) + # Here rots are the same for the half maps, + # but could be different in general. + slices_2, xyz_rotated = \ + IterativeRefinement.generate_slices( + half_map_3d_f_2, xy0_plane, n_pix, rots + ) + + # initialize + # complex + map_3d_f_updated_1 = np.zeros_like(half_map_3d_f_1) + # complex + map_3d_f_updated_2 = np.zeros_like(half_map_3d_f_2) + # float/real + counts_3d_updated_1 = np.zeros_like(half_map_3d_r_1) + # float/real + counts_3d_updated_2 = np.zeros_like(half_map_3d_r_2) + + for particle_idx in range(particles_f_1.shape[0]): + ctf_1 = ctfs_1[particle_idx] + ctf_2 = ctfs_2[particle_idx] + # particle_f_1 = particles_f_1[particle_idx] + # particle_f_2 = particles_f_2[particle_idx] + + particle_f_deconv_1 = \ + IterativeRefinement.apply_wiener_filter( + particles_f_1, ctf_1, 0.01 + ) + particle_f_deconv_2 = \ + IterativeRefinement.apply_wiener_filter( + particles_f_2, ctf_1, 0.01 + ) + + # all slices get convolved with the particle ctf + apply_ctf = np.vectorize( + IterativeRefinement.apply_ctf_to_slice + ) + + slices_conv_ctfs_1 = apply_ctf(slices_1, ctf_1) + slices_conv_ctfs_2 = apply_ctf(slices_2, ctf_2) + + bayes_factors_1 = \ + IterativeRefinement.compute_bayesian_weights( + particles_f_1[particle_idx], + slices_conv_ctfs_1 + ) + bayes_factors_2 = \ + IterativeRefinement.compute_bayesian_weights( + particles_f_2[particle_idx], + slices_conv_ctfs_2 + ) + + for one_slice_idx in range(bayes_factors_1.shape[0]): + xyz = xyz_rotated[one_slice_idx] + # if this can be vectorized, can avoid loop + inserted_slice_3d_r, count_3d_r = \ + IterativeRefinement.insert_slice( + particle_f_deconv_1.real, + xyz, + n_pix + ) + inserted_slice_3d_i, count_3d_i = \ + IterativeRefinement.insert_slice( + particle_f_deconv_1.imag, + xyz, + n_pix + ) + map_3d_f_updated_1 += inserted_slice_3d_r \ + + 1j * inserted_slice_3d_i + counts_3d_updated_1 += count_3d_r + count_3d_i + + for one_slice_idx in range(bayes_factors_2.shape[0]): + xyz = xyz_rotated[one_slice_idx] + inserted_slice_3d_r, count_3d_r = \ + IterativeRefinement.insert_slice( + particle_f_deconv_2.real, + xyz, + n_pix + ) + inserted_slice_3d_i, count_3d_i = \ + IterativeRefinement.insert_slice( + particle_f_deconv_2.imag, + xyz, + n_pix + ) + map_3d_f_updated_2 += inserted_slice_3d_r \ + + 1j * inserted_slice_3d_i + counts_3d_updated_2 += count_3d_r + count_3d_i + + # apply noise model + # half_map_1, half_map_2 come from doing the above + # independently. Filter by noise estimate (e.g. multiply + # both half maps by FSC) + fsc_1d = IterativeRefinement.compute_fsc( + map_3d_f_updated_1, map_3d_f_updated_2 + ) + + fsc_3d = IterativeRefinement.expand_1d_to_3d(fsc_1d) + + # multiplicative filter on maps with fsc + # The FSC is 1D, one number per spherical shells + # it can be expanded back to a multiplicative filter of the same shape as the maps + map_3d_f_filtered_1 = map_3d_f_updated_1 * fsc_3d + map_3d_f_filtered_2 = map_3d_f_updated_2 * fsc_3d + + # update iteration + half_map_3d_f_1 = map_3d_f_filtered_1 + half_map_3d_f_2 = map_3d_f_filtered_2 + + # final map + fsc_1d = IterativeRefinement.compute_fsc( + half_map_3d_f_1, half_map_3d_f_2 + ) + fsc_3d = IterativeRefinement.expand_1d_to_3d(fsc_1d) + map_3d_f_final = \ + (half_map_3d_f_1 + half_map_3d_f_2 / 2) * fsc_3d + map_3d_r_final = IterativeRefinement.ifft_3d(map_3d_f_final) + half_map_3d_r_1 = IterativeRefinement.ifft_3d(half_map_3d_f_1) + half_map_3d_r_2 = IterativeRefinement.ifft_3d(half_map_3d_f_2) + + return map_3d_r_final, half_map_3d_r_1, \ + half_map_3d_r_2, fsc_1d + @staticmethod def split_array(arr): """Split array into two halves along 0th axis. @@ -273,7 +460,7 @@ def insert_slice(slice_real, xyz, n_pix): return inserted_slice_3d, count_3d @staticmethod - def compute_fsc(map_3d_f_1): + def compute_fsc(map_3d_f_1, map_3d_f_2): """Compute Fourier shell correlation. Estimate noise from half maps. @@ -282,20 +469,25 @@ def compute_fsc(map_3d_f_1): ---------- map_3d_f_1 : arr Shape (n_pix, n_pix, n_pix) + map_3d_f_2 : arr + Shape (n_pix, n_pix, n_pix) Returns ------- - fsc_1d_1 : arr - Noise estimates for map 1. - Shape (n_pix // 2,) + noise_estimate : arr + Noise estimates from half maps. + Shape (n_pix,) """ # write fast vectorized fsc from code snippets in # https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/fsc.ipynb # https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/mFSC.ipynb # https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/guinier_fsc_sharpen.ipynb n_pix_1 = map_3d_f_1.shape[0] + n_pix_2 = map_3d_f_2.shape[0] fsc_1d_1 = np.ones(n_pix_1 // 2) - return fsc_1d_1 + fsc_1d_2 = np.ones(n_pix_2 // 2) + noise_estimates = np.concatenate(fsc_1d_1, fsc_1d_2) + return noise_estimates @staticmethod def expand_1d_to_3d(arr_1d): @@ -315,3 +507,39 @@ def expand_1d_to_3d(arr_1d): arr_3d = np.ones((n_pix, n_pix, n_pix)) # arr_1d fsc_1d to 3d (spherical shells) return arr_3d + + @staticmethod + def fft_3d(array): + """3D Fast Fourier Transform. + + Parameters + ---------- + array : arr + Input array. + Shape (n, n, n) + + Returns + ------- + fft_array : arr + Fourier transform of array. + Shape (n, n, n) + """ + return array.copy() + + @staticmethod + def ifft_3d(fft_array): + """3D Inverse Fast Fourier Transform. + + Parameters + ---------- + fft_array : arr + Fourier transform of array. + Shape (n, n, n) + + Returns + ------- + array : arr + Original array. + Shape (n, n, n) + """ + return fft_array.copy() diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 19fd7a7..02276dc 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -31,7 +31,7 @@ def test_ir(n): ctf_info = [ ex_ctf, ] * n - itr = 7 + itr = 2 ir = em.IterativeRefinement(map_3d, particles, ctf_info, itr) return ir @@ -49,6 +49,20 @@ def test_split_array(test_ir, n): assert arr2.shape == ((n + 1) // 2,) +def test_fft_3d(test_ir, n): + """Test 3D fourier transform.""" + arr = np.zeros((n, n, n)) + fft_arr = test_ir.fft_3d(arr) + assert fft_arr.shape == arr.shape + + +def test_ifft_3d(test_ir, n): + """Test 3D inverse fourier transform.""" + fft_arr = np.zeros((n, n, n)) + arr = test_ir.fft_3d(fft_arr) + assert fft_arr.shape == arr.shape + + def test_build_ctf_array(test_ir, n): """Test bulding arbitrary CTF array.""" ctfs = test_ir.build_ctf_array() @@ -136,3 +150,14 @@ def test_expand_1d_to_3d(test_ir, n): spherical = test_ir.expand_1d_to_3d(arr1d) assert spherical.shape == (n, n, n) + + +def test_iterative_refinement(test_ir, n): + """Test complete iterative refinement algorithm.""" + map_3d_r_final, half_map_3d_r_1, \ + half_map_3d_r_2, fsc_1d = test_ir.iterative_refinement() + + assert map_3d_r_final.shape == (n, n, n) + assert half_map_3d_r_1.shape == (n, n, n) + assert half_map_3d_r_2.shape == (n, n, n) + assert fsc_1d.shape == (n,) From 41a5807702641e6b3f259791475f251c5e1de7f1 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Fri, 25 Mar 2022 05:58:39 +0000 Subject: [PATCH 097/148] Format code with black --- .../expectation_maximization.py | 121 +++++++----------- tests/test_expectation_maximization.py | 8 +- 2 files changed, 51 insertions(+), 78 deletions(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index f8a0587..a280cd1 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -66,8 +66,7 @@ def iterative_refinement(self): Shape (n_pix,) """ - particles_1, particles_2 = \ - IterativeRefinement.split_array(self.particles) + particles_1, particles_2 = IterativeRefinement.split_array(self.particles) ctfs = self.build_ctf_array() ctfs_1, ctfs_2 = IterativeRefinement.split_array(ctfs) @@ -86,21 +85,19 @@ def iterative_refinement(self): # can zero pad when do Fourier convolution (fft is on zero # padded and larger sized array) - half_map_3d_r_1, half_map_3d_r_2 = \ - self.map_3d_init.copy(), self.map_3d_init.copy() + half_map_3d_r_1, half_map_3d_r_2 = ( + self.map_3d_init.copy(), + self.map_3d_init.copy(), + ) # should diverge because different particles averaging in - half_map_3d_f_1 = \ - IterativeRefinement.fft_3d(half_map_3d_r_1) - half_map_3d_f_2 = \ - IterativeRefinement.fft_3d(half_map_3d_r_2) + half_map_3d_f_1 = IterativeRefinement.fft_3d(half_map_3d_r_1) + half_map_3d_f_2 = IterativeRefinement.fft_3d(half_map_3d_r_2) for iteration in range(self.max_itr): - half_map_3d_f_1 = \ - IterativeRefinement.fft_3d(half_map_3d_r_1) - half_map_3d_f_2 = \ - IterativeRefinement.fft_3d(half_map_3d_r_2) + half_map_3d_f_1 = IterativeRefinement.fft_3d(half_map_3d_r_1) + half_map_3d_f_2 = IterativeRefinement.fft_3d(half_map_3d_r_2) # align particles to 3D volume # decide on granularity of rotations @@ -120,16 +117,14 @@ def iterative_refinement(self): xy0_plane = IterativeRefinement.generate_xy_plane(n_pix) - slices_1, xyz_rotated = \ - IterativeRefinement.generate_slices( - half_map_3d_f_1, xy0_plane, n_pix, rots - ) + slices_1, xyz_rotated = IterativeRefinement.generate_slices( + half_map_3d_f_1, xy0_plane, n_pix, rots + ) # Here rots are the same for the half maps, # but could be different in general. - slices_2, xyz_rotated = \ - IterativeRefinement.generate_slices( - half_map_3d_f_2, xy0_plane, n_pix, rots - ) + slices_2, xyz_rotated = IterativeRefinement.generate_slices( + half_map_3d_f_2, xy0_plane, n_pix, rots + ) # initialize # complex @@ -147,69 +142,47 @@ def iterative_refinement(self): # particle_f_1 = particles_f_1[particle_idx] # particle_f_2 = particles_f_2[particle_idx] - particle_f_deconv_1 = \ - IterativeRefinement.apply_wiener_filter( - particles_f_1, ctf_1, 0.01 - ) - particle_f_deconv_2 = \ - IterativeRefinement.apply_wiener_filter( - particles_f_2, ctf_1, 0.01 - ) + particle_f_deconv_1 = IterativeRefinement.apply_wiener_filter( + particles_f_1, ctf_1, 0.01 + ) + particle_f_deconv_2 = IterativeRefinement.apply_wiener_filter( + particles_f_2, ctf_1, 0.01 + ) # all slices get convolved with the particle ctf - apply_ctf = np.vectorize( - IterativeRefinement.apply_ctf_to_slice - ) + apply_ctf = np.vectorize(IterativeRefinement.apply_ctf_to_slice) slices_conv_ctfs_1 = apply_ctf(slices_1, ctf_1) slices_conv_ctfs_2 = apply_ctf(slices_2, ctf_2) - bayes_factors_1 = \ - IterativeRefinement.compute_bayesian_weights( - particles_f_1[particle_idx], - slices_conv_ctfs_1 - ) - bayes_factors_2 = \ - IterativeRefinement.compute_bayesian_weights( - particles_f_2[particle_idx], - slices_conv_ctfs_2 - ) + bayes_factors_1 = IterativeRefinement.compute_bayesian_weights( + particles_f_1[particle_idx], slices_conv_ctfs_1 + ) + bayes_factors_2 = IterativeRefinement.compute_bayesian_weights( + particles_f_2[particle_idx], slices_conv_ctfs_2 + ) for one_slice_idx in range(bayes_factors_1.shape[0]): xyz = xyz_rotated[one_slice_idx] # if this can be vectorized, can avoid loop - inserted_slice_3d_r, count_3d_r = \ - IterativeRefinement.insert_slice( - particle_f_deconv_1.real, - xyz, - n_pix - ) - inserted_slice_3d_i, count_3d_i = \ - IterativeRefinement.insert_slice( - particle_f_deconv_1.imag, - xyz, - n_pix - ) - map_3d_f_updated_1 += inserted_slice_3d_r \ - + 1j * inserted_slice_3d_i + inserted_slice_3d_r, count_3d_r = IterativeRefinement.insert_slice( + particle_f_deconv_1.real, xyz, n_pix + ) + inserted_slice_3d_i, count_3d_i = IterativeRefinement.insert_slice( + particle_f_deconv_1.imag, xyz, n_pix + ) + map_3d_f_updated_1 += inserted_slice_3d_r + 1j * inserted_slice_3d_i counts_3d_updated_1 += count_3d_r + count_3d_i for one_slice_idx in range(bayes_factors_2.shape[0]): xyz = xyz_rotated[one_slice_idx] - inserted_slice_3d_r, count_3d_r = \ - IterativeRefinement.insert_slice( - particle_f_deconv_2.real, - xyz, - n_pix - ) - inserted_slice_3d_i, count_3d_i = \ - IterativeRefinement.insert_slice( - particle_f_deconv_2.imag, - xyz, - n_pix - ) - map_3d_f_updated_2 += inserted_slice_3d_r \ - + 1j * inserted_slice_3d_i + inserted_slice_3d_r, count_3d_r = IterativeRefinement.insert_slice( + particle_f_deconv_2.real, xyz, n_pix + ) + inserted_slice_3d_i, count_3d_i = IterativeRefinement.insert_slice( + particle_f_deconv_2.imag, xyz, n_pix + ) + map_3d_f_updated_2 += inserted_slice_3d_r + 1j * inserted_slice_3d_i counts_3d_updated_2 += count_3d_r + count_3d_i # apply noise model @@ -233,18 +206,14 @@ def iterative_refinement(self): half_map_3d_f_2 = map_3d_f_filtered_2 # final map - fsc_1d = IterativeRefinement.compute_fsc( - half_map_3d_f_1, half_map_3d_f_2 - ) + fsc_1d = IterativeRefinement.compute_fsc(half_map_3d_f_1, half_map_3d_f_2) fsc_3d = IterativeRefinement.expand_1d_to_3d(fsc_1d) - map_3d_f_final = \ - (half_map_3d_f_1 + half_map_3d_f_2 / 2) * fsc_3d + map_3d_f_final = (half_map_3d_f_1 + half_map_3d_f_2 / 2) * fsc_3d map_3d_r_final = IterativeRefinement.ifft_3d(map_3d_f_final) half_map_3d_r_1 = IterativeRefinement.ifft_3d(half_map_3d_f_1) half_map_3d_r_2 = IterativeRefinement.ifft_3d(half_map_3d_f_2) - return map_3d_r_final, half_map_3d_r_1, \ - half_map_3d_r_2, fsc_1d + return map_3d_r_final, half_map_3d_r_1, half_map_3d_r_2, fsc_1d @staticmethod def split_array(arr): diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 02276dc..d1b4785 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -154,8 +154,12 @@ def test_expand_1d_to_3d(test_ir, n): def test_iterative_refinement(test_ir, n): """Test complete iterative refinement algorithm.""" - map_3d_r_final, half_map_3d_r_1, \ - half_map_3d_r_2, fsc_1d = test_ir.iterative_refinement() + ( + map_3d_r_final, + half_map_3d_r_1, + half_map_3d_r_2, + fsc_1d, + ) = test_ir.iterative_refinement() assert map_3d_r_final.shape == (n, n, n) assert half_map_3d_r_1.shape == (n, n, n) From 22efe76329bbb6f083e6e712ce6625cd361addda Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 22:59:32 -0700 Subject: [PATCH 098/148] Pre-commit and black changes --- .../expectation_maximization.py | 125 +++++++----------- tests/test_expectation_maximization.py | 8 +- 2 files changed, 53 insertions(+), 80 deletions(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index f8a0587..9288a13 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -65,9 +65,7 @@ def iterative_refinement(self): Final one dimensional fourier shell correlation. Shape (n_pix,) """ - - particles_1, particles_2 = \ - IterativeRefinement.split_array(self.particles) + particles_1, particles_2 = IterativeRefinement.split_array(self.particles) ctfs = self.build_ctf_array() ctfs_1, ctfs_2 = IterativeRefinement.split_array(ctfs) @@ -86,21 +84,19 @@ def iterative_refinement(self): # can zero pad when do Fourier convolution (fft is on zero # padded and larger sized array) - half_map_3d_r_1, half_map_3d_r_2 = \ - self.map_3d_init.copy(), self.map_3d_init.copy() + half_map_3d_r_1, half_map_3d_r_2 = ( + self.map_3d_init.copy(), + self.map_3d_init.copy(), + ) # should diverge because different particles averaging in - half_map_3d_f_1 = \ - IterativeRefinement.fft_3d(half_map_3d_r_1) - half_map_3d_f_2 = \ - IterativeRefinement.fft_3d(half_map_3d_r_2) + half_map_3d_f_1 = IterativeRefinement.fft_3d(half_map_3d_r_1) + half_map_3d_f_2 = IterativeRefinement.fft_3d(half_map_3d_r_2) for iteration in range(self.max_itr): - half_map_3d_f_1 = \ - IterativeRefinement.fft_3d(half_map_3d_r_1) - half_map_3d_f_2 = \ - IterativeRefinement.fft_3d(half_map_3d_r_2) + half_map_3d_f_1 = IterativeRefinement.fft_3d(half_map_3d_r_1) + half_map_3d_f_2 = IterativeRefinement.fft_3d(half_map_3d_r_2) # align particles to 3D volume # decide on granularity of rotations @@ -120,16 +116,14 @@ def iterative_refinement(self): xy0_plane = IterativeRefinement.generate_xy_plane(n_pix) - slices_1, xyz_rotated = \ - IterativeRefinement.generate_slices( - half_map_3d_f_1, xy0_plane, n_pix, rots - ) + slices_1, xyz_rotated = IterativeRefinement.generate_slices( + half_map_3d_f_1, xy0_plane, n_pix, rots + ) # Here rots are the same for the half maps, # but could be different in general. - slices_2, xyz_rotated = \ - IterativeRefinement.generate_slices( - half_map_3d_f_2, xy0_plane, n_pix, rots - ) + slices_2, xyz_rotated = IterativeRefinement.generate_slices( + half_map_3d_f_2, xy0_plane, n_pix, rots + ) # initialize # complex @@ -147,69 +141,47 @@ def iterative_refinement(self): # particle_f_1 = particles_f_1[particle_idx] # particle_f_2 = particles_f_2[particle_idx] - particle_f_deconv_1 = \ - IterativeRefinement.apply_wiener_filter( - particles_f_1, ctf_1, 0.01 - ) - particle_f_deconv_2 = \ - IterativeRefinement.apply_wiener_filter( - particles_f_2, ctf_1, 0.01 - ) + particle_f_deconv_1 = IterativeRefinement.apply_wiener_filter( + particles_f_1, ctf_1, 0.01 + ) + particle_f_deconv_2 = IterativeRefinement.apply_wiener_filter( + particles_f_2, ctf_1, 0.01 + ) # all slices get convolved with the particle ctf - apply_ctf = np.vectorize( - IterativeRefinement.apply_ctf_to_slice - ) + apply_ctf = np.vectorize(IterativeRefinement.apply_ctf_to_slice) slices_conv_ctfs_1 = apply_ctf(slices_1, ctf_1) slices_conv_ctfs_2 = apply_ctf(slices_2, ctf_2) - bayes_factors_1 = \ - IterativeRefinement.compute_bayesian_weights( - particles_f_1[particle_idx], - slices_conv_ctfs_1 - ) - bayes_factors_2 = \ - IterativeRefinement.compute_bayesian_weights( - particles_f_2[particle_idx], - slices_conv_ctfs_2 - ) + bayes_factors_1 = IterativeRefinement.compute_bayesian_weights( + particles_f_1[particle_idx], slices_conv_ctfs_1 + ) + bayes_factors_2 = IterativeRefinement.compute_bayesian_weights( + particles_f_2[particle_idx], slices_conv_ctfs_2 + ) for one_slice_idx in range(bayes_factors_1.shape[0]): xyz = xyz_rotated[one_slice_idx] # if this can be vectorized, can avoid loop - inserted_slice_3d_r, count_3d_r = \ - IterativeRefinement.insert_slice( - particle_f_deconv_1.real, - xyz, - n_pix - ) - inserted_slice_3d_i, count_3d_i = \ - IterativeRefinement.insert_slice( - particle_f_deconv_1.imag, - xyz, - n_pix - ) - map_3d_f_updated_1 += inserted_slice_3d_r \ - + 1j * inserted_slice_3d_i + inserted_slice_3d_r, count_3d_r = IterativeRefinement.insert_slice( + particle_f_deconv_1.real, xyz, n_pix + ) + inserted_slice_3d_i, count_3d_i = IterativeRefinement.insert_slice( + particle_f_deconv_1.imag, xyz, n_pix + ) + map_3d_f_updated_1 += inserted_slice_3d_r + 1j * inserted_slice_3d_i counts_3d_updated_1 += count_3d_r + count_3d_i for one_slice_idx in range(bayes_factors_2.shape[0]): xyz = xyz_rotated[one_slice_idx] - inserted_slice_3d_r, count_3d_r = \ - IterativeRefinement.insert_slice( - particle_f_deconv_2.real, - xyz, - n_pix - ) - inserted_slice_3d_i, count_3d_i = \ - IterativeRefinement.insert_slice( - particle_f_deconv_2.imag, - xyz, - n_pix - ) - map_3d_f_updated_2 += inserted_slice_3d_r \ - + 1j * inserted_slice_3d_i + inserted_slice_3d_r, count_3d_r = IterativeRefinement.insert_slice( + particle_f_deconv_2.real, xyz, n_pix + ) + inserted_slice_3d_i, count_3d_i = IterativeRefinement.insert_slice( + particle_f_deconv_2.imag, xyz, n_pix + ) + map_3d_f_updated_2 += inserted_slice_3d_r + 1j * inserted_slice_3d_i counts_3d_updated_2 += count_3d_r + count_3d_i # apply noise model @@ -224,7 +196,8 @@ def iterative_refinement(self): # multiplicative filter on maps with fsc # The FSC is 1D, one number per spherical shells - # it can be expanded back to a multiplicative filter of the same shape as the maps + # it can be expanded back to a multiplicative filter of + # the same shape as the maps map_3d_f_filtered_1 = map_3d_f_updated_1 * fsc_3d map_3d_f_filtered_2 = map_3d_f_updated_2 * fsc_3d @@ -233,18 +206,14 @@ def iterative_refinement(self): half_map_3d_f_2 = map_3d_f_filtered_2 # final map - fsc_1d = IterativeRefinement.compute_fsc( - half_map_3d_f_1, half_map_3d_f_2 - ) + fsc_1d = IterativeRefinement.compute_fsc(half_map_3d_f_1, half_map_3d_f_2) fsc_3d = IterativeRefinement.expand_1d_to_3d(fsc_1d) - map_3d_f_final = \ - (half_map_3d_f_1 + half_map_3d_f_2 / 2) * fsc_3d + map_3d_f_final = (half_map_3d_f_1 + half_map_3d_f_2 / 2) * fsc_3d map_3d_r_final = IterativeRefinement.ifft_3d(map_3d_f_final) half_map_3d_r_1 = IterativeRefinement.ifft_3d(half_map_3d_f_1) half_map_3d_r_2 = IterativeRefinement.ifft_3d(half_map_3d_f_2) - return map_3d_r_final, half_map_3d_r_1, \ - half_map_3d_r_2, fsc_1d + return map_3d_r_final, half_map_3d_r_1, half_map_3d_r_2, fsc_1d @staticmethod def split_array(arr): diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 02276dc..d1b4785 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -154,8 +154,12 @@ def test_expand_1d_to_3d(test_ir, n): def test_iterative_refinement(test_ir, n): """Test complete iterative refinement algorithm.""" - map_3d_r_final, half_map_3d_r_1, \ - half_map_3d_r_2, fsc_1d = test_ir.iterative_refinement() + ( + map_3d_r_final, + half_map_3d_r_1, + half_map_3d_r_2, + fsc_1d, + ) = test_ir.iterative_refinement() assert map_3d_r_final.shape == (n, n, n) assert half_map_3d_r_1.shape == (n, n, n) From bda6275c1199a5b28ae5ea67fe118b5e740a51ba Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 23:03:05 -0700 Subject: [PATCH 099/148] Deepsource --- .../iterative_refinement/expectation_maximization.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index 9288a13..1b30a75 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -93,7 +93,7 @@ def iterative_refinement(self): half_map_3d_f_1 = IterativeRefinement.fft_3d(half_map_3d_r_1) half_map_3d_f_2 = IterativeRefinement.fft_3d(half_map_3d_r_2) - for iteration in range(self.max_itr): + for _ in range(self.max_itr): half_map_3d_f_1 = IterativeRefinement.fft_3d(half_map_3d_r_1) half_map_3d_f_2 = IterativeRefinement.fft_3d(half_map_3d_r_2) @@ -125,7 +125,7 @@ def iterative_refinement(self): half_map_3d_f_2, xy0_plane, n_pix, rots ) - # initialize + # initialize: # complex map_3d_f_updated_1 = np.zeros_like(half_map_3d_f_1) # complex @@ -138,8 +138,8 @@ def iterative_refinement(self): for particle_idx in range(particles_f_1.shape[0]): ctf_1 = ctfs_1[particle_idx] ctf_2 = ctfs_2[particle_idx] - # particle_f_1 = particles_f_1[particle_idx] - # particle_f_2 = particles_f_2[particle_idx] + # Option: particle_f_1 = particles_f_1[particle_idx] + # Option: particle_f_2 = particles_f_2[particle_idx] particle_f_deconv_1 = IterativeRefinement.apply_wiener_filter( particles_f_1, ctf_1, 0.01 From 976be19f6dc59139e3633b8ef6388c63b626c436 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 23:09:15 -0700 Subject: [PATCH 100/148] Expanded split_array to allow lists and tuples --- .../iterative_refinement/expectation_maximization.py | 5 ++++- tests/test_expectation_maximization.py | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index 1b30a75..4eea545 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -231,7 +231,10 @@ def split_array(arr): arr2: arr Shape (n_particles // 2, ...) """ - idx_half = arr.shape[0] // 2 + if isinstance(arr, list) or isinstance(arr, tuple): + idx_half = len(arr) // 2 + else: + idx_half = arr.shape[0] // 2 arr_1, arr_2 = arr[:idx_half], arr[idx_half:] if arr_1.shape[0] != arr_2.shape[0]: diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index d1b4785..9e139a9 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -48,6 +48,11 @@ def test_split_array(test_ir, n): assert arr1.shape == ((n + 1) // 2,) assert arr2.shape == ((n + 1) // 2,) + arr = ['a', 'b'] + arr1, arr2 = test_ir.split_array(arr) + assert len(arr1) == 1 + assert len(arr2) == 1 + def test_fft_3d(test_ir, n): """Test 3D fourier transform.""" From 00e72810c43b3fd0ca881d165d905420c340f8f1 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Fri, 25 Mar 2022 06:09:44 +0000 Subject: [PATCH 101/148] Format code with black --- tests/test_expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 9e139a9..a261e28 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -48,7 +48,7 @@ def test_split_array(test_ir, n): assert arr1.shape == ((n + 1) // 2,) assert arr2.shape == ((n + 1) // 2,) - arr = ['a', 'b'] + arr = ["a", "b"] arr1, arr2 = test_ir.split_array(arr) assert len(arr1) == 1 assert len(arr2) == 1 From ff3658877d9101e5d87881a09aca4b620894eb4b Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 23:11:01 -0700 Subject: [PATCH 102/148] Deepsource fix --- reconstructSPI/iterative_refinement/expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index 4eea545..c63637f 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -231,7 +231,7 @@ def split_array(arr): arr2: arr Shape (n_particles // 2, ...) """ - if isinstance(arr, list) or isinstance(arr, tuple): + if isinstance(arr, (list, tuple)): idx_half = len(arr) // 2 else: idx_half = arr.shape[0] // 2 From b8e37ef6978e5e77a67c730b80997c7a16602839 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 23:17:43 -0700 Subject: [PATCH 103/148] Fixed split_array expansion --- .../iterative_refinement/expectation_maximization.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index c63637f..bb13db7 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -233,12 +233,16 @@ def split_array(arr): """ if isinstance(arr, (list, tuple)): idx_half = len(arr) // 2 + arr_1, arr_2 = arr[:idx_half], arr[idx_half:] + + if len(arr_1) != len(arr_2): + arr_2 = arr[idx_half : 2 * idx_half] else: idx_half = arr.shape[0] // 2 - arr_1, arr_2 = arr[:idx_half], arr[idx_half:] + arr_1, arr_2 = arr[:idx_half], arr[idx_half:] - if arr_1.shape[0] != arr_2.shape[0]: - arr_2 = arr[idx_half : 2 * idx_half] + if arr_1.shape[0] != arr_2.shape[0]: + arr_2 = arr[idx_half : 2 * idx_half] return arr_1, arr_2 From abfd9aa5be4a53d0988b068de9238cc29ce605f6 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 23:20:14 -0700 Subject: [PATCH 104/148] Fix to fsc test --- tests/test_expectation_maximization.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index a261e28..643dd41 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -144,9 +144,10 @@ def test_insert_slice(test_ir, n): def test_compute_fsc(test_ir, n): """Test computation of FSC.""" map_1 = np.ones((n, n, n)) + map_2 = np.ones((n, n, n)) - fsc_1 = test_ir.compute_fsc(map_1) - assert fsc_1.shape == (1,) + fsc_1 = test_ir.compute_fsc(map_1, map_2) + assert fsc_1.shape == (n,) def test_expand_1d_to_3d(test_ir, n): From e1cd12054136efd47a8011400ba1b8613bae3eae Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 23:34:26 -0700 Subject: [PATCH 105/148] Data type fix --- .../iterative_refinement/expectation_maximization.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index bb13db7..7e0d906 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -500,7 +500,7 @@ def fft_3d(array): Fourier transform of array. Shape (n, n, n) """ - return array.copy() + return np.zeros(array.shape, dtype=np.cdouble) @staticmethod def ifft_3d(fft_array): @@ -518,4 +518,4 @@ def ifft_3d(fft_array): Original array. Shape (n, n, n) """ - return fft_array.copy() + return np.zeros(fft_array.shape) From 83d9cf7ab1c1704d102bff26cee7e1dd461df07c Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 23:35:36 -0700 Subject: [PATCH 106/148] Concatenate fix --- reconstructSPI/iterative_refinement/expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index 7e0d906..bc30317 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -462,7 +462,7 @@ def compute_fsc(map_3d_f_1, map_3d_f_2): n_pix_2 = map_3d_f_2.shape[0] fsc_1d_1 = np.ones(n_pix_1 // 2) fsc_1d_2 = np.ones(n_pix_2 // 2) - noise_estimates = np.concatenate(fsc_1d_1, fsc_1d_2) + noise_estimates = np.concatenate((fsc_1d_1, fsc_1d_2)) return noise_estimates @staticmethod From f8dc07a0ef3b94f0878a918ee486e848952025af Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 23:52:43 -0700 Subject: [PATCH 107/148] Change to insert slice shape --- .../iterative_refinement/expectation_maximization.py | 9 +++++---- tests/test_expectation_maximization.py | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index bc30317..faaa659 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -424,15 +424,16 @@ def insert_slice(slice_real, xyz, n_pix): ------- inserted_slice_3d : float64 arr Rotated slice in 3D voxel array. - Shape (n_pix**2, 3) + Shape (n_pix, n_pix, n_pix) count_3d : arr Voxel array to count slice presence: 1 if slice present, otherwise 0. - Shape (n_pix**2, 3) + Shape (n_pix, n_pix, n_pix) """ shape = xyz.shape[0] - count_3d = np.ones((shape, 3)) - inserted_slice_3d = np.ones((shape, 3)) + count_3d = np.ones(n_pix, n_pix, n_pix) + count_3d[0,0,0] *= shape + inserted_slice_3d = np.ones(n_pix, n_pix, n_pix) return inserted_slice_3d, count_3d @staticmethod diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 643dd41..02701ec 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -137,8 +137,8 @@ def test_insert_slice(test_ir, n): xyz = test_ir.generate_xy_plane(n) inserted, count = test_ir.insert_slice(particle_slice, xyz, n) - assert inserted.shape == (n * n, 3) - assert count.shape == (n * n, 3) + assert inserted.shape == (n, n, n) + assert count.shape == (n, n, n) def test_compute_fsc(test_ir, n): From 352a9898748c24a6e2272fbd5572115e9ba8b9f8 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Fri, 25 Mar 2022 06:52:50 +0000 Subject: [PATCH 108/148] Format code with black --- reconstructSPI/iterative_refinement/expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index faaa659..441c901 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -432,7 +432,7 @@ def insert_slice(slice_real, xyz, n_pix): """ shape = xyz.shape[0] count_3d = np.ones(n_pix, n_pix, n_pix) - count_3d[0,0,0] *= shape + count_3d[0, 0, 0] *= shape inserted_slice_3d = np.ones(n_pix, n_pix, n_pix) return inserted_slice_3d, count_3d From d381e3cd38a6ade75fcbaa254c06fe47cbc1e0f8 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 24 Mar 2022 23:56:38 -0700 Subject: [PATCH 109/148] fix to numpy array initialization --- .../iterative_refinement/expectation_maximization.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index faaa659..4a8ddbc 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -431,9 +431,9 @@ def insert_slice(slice_real, xyz, n_pix): Shape (n_pix, n_pix, n_pix) """ shape = xyz.shape[0] - count_3d = np.ones(n_pix, n_pix, n_pix) + count_3d = np.ones((n_pix, n_pix, n_pix)) count_3d[0,0,0] *= shape - inserted_slice_3d = np.ones(n_pix, n_pix, n_pix) + inserted_slice_3d = np.ones((n_pix, n_pix, n_pix)) return inserted_slice_3d, count_3d @staticmethod From 294d4b54ddb4b64ce3624b064a5e433930e20e18 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Fri, 25 Mar 2022 06:57:23 +0000 Subject: [PATCH 110/148] Format code with black --- reconstructSPI/iterative_refinement/expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index 4a8ddbc..3c0d480 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -432,7 +432,7 @@ def insert_slice(slice_real, xyz, n_pix): """ shape = xyz.shape[0] count_3d = np.ones((n_pix, n_pix, n_pix)) - count_3d[0,0,0] *= shape + count_3d[0, 0, 0] *= shape inserted_slice_3d = np.ones((n_pix, n_pix, n_pix)) return inserted_slice_3d, count_3d From 3819306451a94c8b62fd3bdc276874afcc09db19 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Fri, 25 Mar 2022 00:10:35 -0700 Subject: [PATCH 111/148] num rotations change --- .../iterative_refinement/expectation_maximization.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index 4a8ddbc..33e86eb 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -77,6 +77,9 @@ def iterative_refinement(self): particles_f_2 = IterativeRefinement.fft_3d(particles_2) n_pix = self.map_3d_init.shape[0] + + n_rotations = self.particles.shape[0] + # suggest 32 or 64 to start with. real data will be more like # 128 or 256. Can have issues with ctf at small pixels and # need to zero pad to avoid artefacts. Artefacts from ctf not @@ -111,7 +114,6 @@ def iterative_refinement(self): # based on how well the signal agrees in half_map_1, # half_map_2 (Fourier frequency). - n_rotations = 1000 rots = IterativeRefinement.grid_SO3_uniform(n_rotations) xy0_plane = IterativeRefinement.generate_xy_plane(n_pix) From f28ccfab1fe6e19dff6270bcd5e99c97ae9dc536 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Fri, 25 Mar 2022 07:10:49 +0000 Subject: [PATCH 112/148] Format code with black --- .../iterative_refinement/expectation_maximization.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index 1671309..97307f4 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -77,9 +77,9 @@ def iterative_refinement(self): particles_f_2 = IterativeRefinement.fft_3d(particles_2) n_pix = self.map_3d_init.shape[0] - + n_rotations = self.particles.shape[0] - + # suggest 32 or 64 to start with. real data will be more like # 128 or 256. Can have issues with ctf at small pixels and # need to zero pad to avoid artefacts. Artefacts from ctf not From c0539804fce0cb6ce069161289339208b796d01b Mon Sep 17 00:00:00 2001 From: thisTyler Date: Fri, 25 Mar 2022 00:17:05 -0700 Subject: [PATCH 113/148] fsc fix --- .../iterative_refinement/expectation_maximization.py | 6 +++--- tests/test_expectation_maximization.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index 1671309..6943565 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -63,7 +63,7 @@ def iterative_refinement(self): Shape (n_pix, n_pix, n_pix) fsc_1d : arr Final one dimensional fourier shell correlation. - Shape (n_pix,) + Shape (n_pix // 2,) """ particles_1, particles_2 = IterativeRefinement.split_array(self.particles) @@ -455,7 +455,7 @@ def compute_fsc(map_3d_f_1, map_3d_f_2): ------- noise_estimate : arr Noise estimates from half maps. - Shape (n_pix,) + Shape (n_pix // 2,) """ # write fast vectorized fsc from code snippets in # https://github.com/geoffwoollard/learn_cryoem_math/blob/master/nb/fsc.ipynb @@ -465,7 +465,7 @@ def compute_fsc(map_3d_f_1, map_3d_f_2): n_pix_2 = map_3d_f_2.shape[0] fsc_1d_1 = np.ones(n_pix_1 // 2) fsc_1d_2 = np.ones(n_pix_2 // 2) - noise_estimates = np.concatenate((fsc_1d_1, fsc_1d_2)) + noise_estimates = np.dot(fsc_1d_1, fsc_1d_2) return noise_estimates @staticmethod diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 02701ec..f001ce4 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -147,7 +147,7 @@ def test_compute_fsc(test_ir, n): map_2 = np.ones((n, n, n)) fsc_1 = test_ir.compute_fsc(map_1, map_2) - assert fsc_1.shape == (n,) + assert fsc_1.shape == (n // 2,) def test_expand_1d_to_3d(test_ir, n): From 7083a547f4cc98170c6d816edb07d4c315183b3d Mon Sep 17 00:00:00 2001 From: thisTyler Date: Fri, 25 Mar 2022 00:22:14 -0700 Subject: [PATCH 114/148] Forgot what a dot product did for a second there --- reconstructSPI/iterative_refinement/expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index afed5c1..17e393f 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -465,7 +465,7 @@ def compute_fsc(map_3d_f_1, map_3d_f_2): n_pix_2 = map_3d_f_2.shape[0] fsc_1d_1 = np.ones(n_pix_1 // 2) fsc_1d_2 = np.ones(n_pix_2 // 2) - noise_estimates = np.dot(fsc_1d_1, fsc_1d_2) + noise_estimates = fsc_1d_1 * fsc_1d_2 return noise_estimates @staticmethod From 2a1eda7e5ccfbc544bfcd1e36cd8ea5ce4c29298 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Fri, 25 Mar 2022 00:26:03 -0700 Subject: [PATCH 115/148] Updated iterative refinement test to reflect fsc fix --- tests/test_expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index f001ce4..d63edfe 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -170,4 +170,4 @@ def test_iterative_refinement(test_ir, n): assert map_3d_r_final.shape == (n, n, n) assert half_map_3d_r_1.shape == (n, n, n) assert half_map_3d_r_2.shape == (n, n, n) - assert fsc_1d.shape == (n,) + assert fsc_1d.shape == (n // 2,) From cba93206c2b43b0858858771bcacc6d237f49152 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Sat, 26 Mar 2022 18:20:08 -0700 Subject: [PATCH 116/148] Removed superfluous imports --- environment.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/environment.yml b/environment.yml index 01e8d5c..16ab6ba 100644 --- a/environment.yml +++ b/environment.yml @@ -6,7 +6,6 @@ dependencies: - codecov - coverage - gemmi - - numba - numpy - pillow>=8.2.0 - pip From d40e6ae1856f4ecc4b852183851d1b864f771d65 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Sat, 26 Mar 2022 18:20:29 -0700 Subject: [PATCH 117/148] Updated docstrings, fixed splitting arrs --- .../expectation_maximization.py | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index 17e393f..e104c3f 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -21,10 +21,10 @@ class IterativeRefinement: References ---------- - 1. Nelson, P. C. (2019). Chapter 12 : Single Particle - Reconstruction in Cryo-electron Microscopy. - In Physical Models of Living Systems (pp. 305–325). - https://repository.upenn.edu/physics_papers/656/ + 1. Nelson, P. C. (2021). Physical Models of Living Systems new + chapter: Single Particle Reconstruction in Cryo-electron + Microscopy. + https://repository.upenn.edu/physics_papers/656 2. Scheres, S. H. W. (2012). RELION: Implementation of a Bayesian approach to cryo-EM structure determination. Journal of Structural Biology, 180(3), 519–530. @@ -233,18 +233,11 @@ def split_array(arr): arr2: arr Shape (n_particles // 2, ...) """ - if isinstance(arr, (list, tuple)): - idx_half = len(arr) // 2 - arr_1, arr_2 = arr[:idx_half], arr[idx_half:] + idx_half = len(arr) // 2 + arr_1, arr_2 = arr[:idx_half], arr[idx_half:] - if len(arr_1) != len(arr_2): - arr_2 = arr[idx_half : 2 * idx_half] - else: - idx_half = arr.shape[0] // 2 - arr_1, arr_2 = arr[:idx_half], arr[idx_half:] - - if arr_1.shape[0] != arr_2.shape[0]: - arr_2 = arr[idx_half : 2 * idx_half] + if len(arr_1) != len(arr_2): + arr_2 = arr[idx_half : 2 * idx_half] return arr_1, arr_2 @@ -501,7 +494,7 @@ def fft_3d(array): ------- fft_array : arr Fourier transform of array. - Shape (n, n, n) + Shape (n_pix, n_pix, n_pix) """ return np.zeros(array.shape, dtype=np.cdouble) @@ -513,7 +506,7 @@ def ifft_3d(fft_array): ---------- fft_array : arr Fourier transform of array. - Shape (n, n, n) + Shape (n_pix, n_pix, n_pix) Returns ------- From 6c6e1f0e159980bf1da3e97abda4d43e708c1b6c Mon Sep 17 00:00:00 2001 From: thisTyler Date: Sat, 26 Mar 2022 18:20:48 -0700 Subject: [PATCH 118/148] Split n into n_pix, n_particles --- tests/test_expectation_maximization.py | 142 +++++++++++++------------ 1 file changed, 76 insertions(+), 66 deletions(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index d63edfe..41063a0 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -7,17 +7,22 @@ @pytest.fixture -def n(): - """Get test array size value for consistency.""" +def n_pix(): + """Get test pixel count for consistency.""" return 2 @pytest.fixture -def test_ir(n): +def n_particles(): + """Get test particle count for consistency.""" + return 2 + +@pytest.fixture +def test_ir(n_pix, n_particles): """Instantiate IterativeRefinement class for testing.""" ex_ctf = { - "s": np.ones((n, n)), - "a": np.ones((n, n)), + "s": np.ones((n_pix, n_pix)), + "a": np.ones((n_pix, n_pix)), "def1": 1.0, "def2": 1.0, "angast": 0.1, @@ -26,27 +31,27 @@ def test_ir(n): "bf": 0.1, "lp": 0.1, } - map_3d = np.zeros((n, n, n)) - particles = np.zeros((n, n, n)) + map_3d = np.zeros((n_pix, n_pix, n_pix)) + particles = np.zeros((n_particles, n_pix, n_pix)) ctf_info = [ ex_ctf, - ] * n + ] * n_particles itr = 2 ir = em.IterativeRefinement(map_3d, particles, ctf_info, itr) return ir -def test_split_array(test_ir, n): +def test_split_array(test_ir, n_particles): """Test splitting of array in two halves.""" - arr = np.zeros(n) + arr = np.zeros(n_particles) arr1, arr2 = test_ir.split_array(arr) - assert arr1.shape == (n // 2,) - assert arr2.shape == (n // 2,) + assert arr1.shape == (n_particles // 2,) + assert arr2.shape == (n_particles // 2,) - arr = np.zeros(n + 1) + arr = np.zeros(n_particles + 1) arr1, arr2 = test_ir.split_array(arr) - assert arr1.shape == ((n + 1) // 2,) - assert arr2.shape == ((n + 1) // 2,) + assert arr1.shape == ((n_particles + 1) // 2,) + assert arr2.shape == ((n_particles + 1) // 2,) arr = ["a", "b"] arr1, arr2 = test_ir.split_array(arr) @@ -54,111 +59,116 @@ def test_split_array(test_ir, n): assert len(arr2) == 1 -def test_fft_3d(test_ir, n): +def test_fft_3d(test_ir, n_pix): """Test 3D fourier transform.""" - arr = np.zeros((n, n, n)) + arr = np.zeros((n_pix, n_pix, n_pix)) fft_arr = test_ir.fft_3d(arr) assert fft_arr.shape == arr.shape -def test_ifft_3d(test_ir, n): +def test_ifft_3d(test_ir, n_pix): """Test 3D inverse fourier transform.""" - fft_arr = np.zeros((n, n, n)) + fft_arr = np.zeros((n_pix, n_pix, n_pix)) arr = test_ir.fft_3d(fft_arr) assert fft_arr.shape == arr.shape -def test_build_ctf_array(test_ir, n): +def test_build_ctf_array(test_ir, n_particles, n_pix): """Test bulding arbitrary CTF array.""" ctfs = test_ir.build_ctf_array() - assert len(ctfs) == n - assert ctfs[0].shape == (n, n) + assert len(ctfs) == n_particles + assert ctfs[0].shape == (n_pix, n_pix) -def test_grid_SO3_uniform(test_ir, n): +def test_grid_SO3_uniform(test_ir, n_particles): """Test generation of rotations in SO(3).""" - rots = test_ir.grid_SO3_uniform(n) - assert rots.shape == (n, 3, 3) + rots = test_ir.grid_SO3_uniform(n_particles) + assert rots.shape == (n_particles, 3, 3) -def test_generate_xy_plane(test_ir, n): +def test_generate_xy_plane(test_ir, n_pix): """Test generation of xy plane.""" - xy_plane = test_ir.generate_xy_plane(n) - assert xy_plane.shape == (n * n, 3) + xy_plane = test_ir.generate_xy_plane(n_pix) + assert xy_plane.shape == (n_pix ** 2, 3) -def test_generate_slices(test_ir, n): +def test_generate_slices(test_ir, n_particles, n_pix): """Test generation of slices.""" - map_3d = np.ones((n, n, n)) - rots = test_ir.grid_SO3_uniform(n) - xy_plane = test_ir.generate_xy_plane(n) + map_3d = np.ones((n_pix, n_pix, n_pix)) + rots = test_ir.grid_SO3_uniform(n_particles) + xy_plane = test_ir.generate_xy_plane(n_pix) - slices, xyz_rotated = test_ir.generate_slices(map_3d, xy_plane, n, rots) + slices, xyz_rotated = test_ir.generate_slices( + map_3d, + xy_plane, + n_pix, + rots + ) - assert xy_plane.shape == (n * n, 3) - assert slices.shape == (n, n, n) - assert xyz_rotated.shape == (n * n, 3) + assert xy_plane.shape == (n_pix ** 2, 3) + assert slices.shape == (n_particles, n_pix, n_pix) + assert xyz_rotated.shape == (n_pix ** 2, 3) -def test_apply_ctf_to_slice(test_ir, n): +def test_apply_ctf_to_slice(test_ir, n_pix): """Test convolution of particle slice with CTF.""" - particle_slice = np.ones((n, n)) - ctf = np.ones((n, n)) + particle_slice = np.ones((n_pix, n_pix)) + ctf = np.ones((n_pix, n_pix)) convolved = test_ir.apply_ctf_to_slice(particle_slice, ctf) - assert convolved.shape == (n, n) + assert convolved.shape == (n_pix, n_pix) -def test_compute_bayesian_weights(test_ir, n): +def test_compute_bayesian_weights(test_ir, n_particles, n_pix): """Test computation of bayesian weights. For use under Gaussian white noise model. """ - particle = np.ones((1, n, n)) - slices = np.ones((n, n, n)) + particle = np.ones((n_pix // 2, n_pix, n_pix)) + slices = np.ones((n_particles, n_pix, n_pix)) bayesian_weights = test_ir.compute_bayesian_weights(particle, slices) - assert bayesian_weights.shape == (n,) + assert bayesian_weights.shape == (n_particles,) -def test_apply_wiener_filter(test_ir, n): +def test_apply_wiener_filter(test_ir, n_pix): """Test application of Wiener filter to particle projection.""" - projection = np.ones((n, n)) - ctf = np.zeros((n, n)) + projection = np.ones((n_pix, n_pix)) + ctf = np.zeros((n_pix, n_pix)) small_number = 0.01 projection_wfilter_f = test_ir.apply_wiener_filter(projection, ctf, small_number) - assert projection_wfilter_f.shape == (n, n) + assert projection_wfilter_f.shape == (n_pix, n_pix) -def test_insert_slice(test_ir, n): +def test_insert_slice(test_ir, n_pix): """Test insertion of particle slice.""" - particle_slice = np.ones((n, n)) - xyz = test_ir.generate_xy_plane(n) + particle_slice = np.ones((n_pix, n_pix)) + xyz = test_ir.generate_xy_plane(n_pix) - inserted, count = test_ir.insert_slice(particle_slice, xyz, n) - assert inserted.shape == (n, n, n) - assert count.shape == (n, n, n) + inserted, count = test_ir.insert_slice(particle_slice, xyz, n_pix) + assert inserted.shape == (n_pix, n_pix, n_pix) + assert count.shape == (n_pix, n_pix, n_pix) -def test_compute_fsc(test_ir, n): +def test_compute_fsc(test_ir, n_pix): """Test computation of FSC.""" - map_1 = np.ones((n, n, n)) - map_2 = np.ones((n, n, n)) + map_1 = np.ones((n_pix, n_pix, n_pix)) + map_2 = np.ones((n_pix, n_pix, n_pix)) fsc_1 = test_ir.compute_fsc(map_1, map_2) - assert fsc_1.shape == (n // 2,) + assert fsc_1.shape == (n_pix // 2,) -def test_expand_1d_to_3d(test_ir, n): +def test_expand_1d_to_3d(test_ir, n_pix): """Test expansion of 1D array into spherical shell.""" - arr1d = np.ones(1) + arr1d = np.ones(n_pix // 2) spherical = test_ir.expand_1d_to_3d(arr1d) - assert spherical.shape == (n, n, n) + assert spherical.shape == (n_pix, n_pix, n_pix) -def test_iterative_refinement(test_ir, n): +def test_iterative_refinement(test_ir, n_pix): """Test complete iterative refinement algorithm.""" ( map_3d_r_final, @@ -167,7 +177,7 @@ def test_iterative_refinement(test_ir, n): fsc_1d, ) = test_ir.iterative_refinement() - assert map_3d_r_final.shape == (n, n, n) - assert half_map_3d_r_1.shape == (n, n, n) - assert half_map_3d_r_2.shape == (n, n, n) - assert fsc_1d.shape == (n // 2,) + assert map_3d_r_final.shape == (n_pix, n_pix, n_pix) + assert half_map_3d_r_1.shape == (n_pix, n_pix, n_pix) + assert half_map_3d_r_2.shape == (n_pix, n_pix, n_pix) + assert fsc_1d.shape == (n_pix // 2,) From 1e11a9d2a4f599b1b88ee9d62b0c9930b01e1244 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Sun, 27 Mar 2022 01:21:06 +0000 Subject: [PATCH 119/148] Format code with black --- tests/test_expectation_maximization.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 41063a0..7a17b24 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -17,6 +17,7 @@ def n_particles(): """Get test particle count for consistency.""" return 2 + @pytest.fixture def test_ir(n_pix, n_particles): """Instantiate IterativeRefinement class for testing.""" @@ -89,7 +90,7 @@ def test_grid_SO3_uniform(test_ir, n_particles): def test_generate_xy_plane(test_ir, n_pix): """Test generation of xy plane.""" xy_plane = test_ir.generate_xy_plane(n_pix) - assert xy_plane.shape == (n_pix ** 2, 3) + assert xy_plane.shape == (n_pix**2, 3) def test_generate_slices(test_ir, n_particles, n_pix): @@ -98,16 +99,11 @@ def test_generate_slices(test_ir, n_particles, n_pix): rots = test_ir.grid_SO3_uniform(n_particles) xy_plane = test_ir.generate_xy_plane(n_pix) - slices, xyz_rotated = test_ir.generate_slices( - map_3d, - xy_plane, - n_pix, - rots - ) + slices, xyz_rotated = test_ir.generate_slices(map_3d, xy_plane, n_pix, rots) - assert xy_plane.shape == (n_pix ** 2, 3) + assert xy_plane.shape == (n_pix**2, 3) assert slices.shape == (n_particles, n_pix, n_pix) - assert xyz_rotated.shape == (n_pix ** 2, 3) + assert xyz_rotated.shape == (n_pix**2, 3) def test_apply_ctf_to_slice(test_ir, n_pix): From d621fb105c33a523a876558cd7e941f768df2998 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Sat, 26 Mar 2022 18:26:25 -0700 Subject: [PATCH 120/148] Added back numba --- environment.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/environment.yml b/environment.yml index 16ab6ba..e52e0d7 100644 --- a/environment.yml +++ b/environment.yml @@ -7,6 +7,7 @@ dependencies: - coverage - gemmi - numpy + - numba - pillow>=8.2.0 - pip - pytest From 496f88a4ef572001df2c805ec6e16557f9967b79 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Sat, 26 Mar 2022 18:33:46 -0700 Subject: [PATCH 121/148] Force checks --- reconstructSPI/iterative_refinement/expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index e104c3f..cbfaa7c 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -433,7 +433,7 @@ def insert_slice(slice_real, xyz, n_pix): @staticmethod def compute_fsc(map_3d_f_1, map_3d_f_2): - """Compute Fourier shell correlation. + """Compute the Fourier shell correlation. Estimate noise from half maps. From 8aaff785d5ae7bcf85b81649eb9811a7dbe09e49 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Mon, 28 Mar 2022 11:53:58 -0700 Subject: [PATCH 122/148] n_pix in fft docstrings --- .../iterative_refinement/expectation_maximization.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index cbfaa7c..47c61ee 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -488,7 +488,7 @@ def fft_3d(array): ---------- array : arr Input array. - Shape (n, n, n) + Shape (n_pix, n_pix, n_pix) Returns ------- @@ -512,6 +512,6 @@ def ifft_3d(fft_array): ------- array : arr Original array. - Shape (n, n, n) + Shape (n_pix, n_pix, n_pix) """ return np.zeros(fft_array.shape) From 33e3229b1dd193f1cecdc7334668b3e991cdf4de Mon Sep 17 00:00:00 2001 From: Tyler Heim Date: Mon, 28 Mar 2022 14:33:25 -0700 Subject: [PATCH 123/148] Normalizing half maps --- .../expectation_maximization.py | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index 47c61ee..5e98350 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -43,12 +43,19 @@ def __init__(self, map_3d_init, particles, ctf_info, max_itr=7): self.ctf_info = ctf_info self.max_itr = max_itr - def iterative_refinement(self): + def iterative_refinement(self, wiener_small_number = 0.01, count_norm_const = 1): """Perform iterative refinement. Acts in a Bayesian expectation maximization setting, i.e. maximum a posteriori estimation. + Parameters + ---------- + wiener_small_number : float + Used to tune Wiener filter. + count_norm_const : float + Used to tune normalization of slice inserting. + Returns ------- map_3d_update : arr @@ -132,6 +139,10 @@ def iterative_refinement(self): map_3d_f_updated_1 = np.zeros_like(half_map_3d_f_1) # complex map_3d_f_updated_2 = np.zeros_like(half_map_3d_f_2) + # complex + map_3d_f_norm_1 = np.zeros_like(half_map_3d_f_1) + # complex + map_3d_f_norm_2 = np.zeros_like(half_map_3d_f_2) # float/real counts_3d_updated_1 = np.zeros_like(half_map_3d_r_1) # float/real @@ -144,10 +155,10 @@ def iterative_refinement(self): # Option: particle_f_2 = particles_f_2[particle_idx] particle_f_deconv_1 = IterativeRefinement.apply_wiener_filter( - particles_f_1, ctf_1, 0.01 + particles_f_1, ctf_1, wiener_small_number ) particle_f_deconv_2 = IterativeRefinement.apply_wiener_filter( - particles_f_2, ctf_1, 0.01 + particles_f_2, ctf_1, wiener_small_number ) # all slices get convolved with the particle ctf @@ -186,12 +197,17 @@ def iterative_refinement(self): map_3d_f_updated_2 += inserted_slice_3d_r + 1j * inserted_slice_3d_i counts_3d_updated_2 += count_3d_r + count_3d_i + # normalize maps by slice counts to account + # for spherical density differences + map_3d_f_norm_1 = map_3d_f_updated_1 * counts_3d_updated_1 / (count_norm_const + counts_3d_updated_1**2) + map_3d_f_norm_1 = map_3d_f_updated_2 * counts_3d_updated_2 / (count_norm_const + counts_3d_updated_2**2) + # apply noise model # half_map_1, half_map_2 come from doing the above # independently. Filter by noise estimate (e.g. multiply # both half maps by FSC) fsc_1d = IterativeRefinement.compute_fsc( - map_3d_f_updated_1, map_3d_f_updated_2 + map_3d_f_norm_1, map_3d_f_norm_2 ) fsc_3d = IterativeRefinement.expand_1d_to_3d(fsc_1d) @@ -200,8 +216,8 @@ def iterative_refinement(self): # The FSC is 1D, one number per spherical shells # it can be expanded back to a multiplicative filter of # the same shape as the maps - map_3d_f_filtered_1 = map_3d_f_updated_1 * fsc_3d - map_3d_f_filtered_2 = map_3d_f_updated_2 * fsc_3d + map_3d_f_filtered_1 = map_3d_f_norm_1 * fsc_3d + map_3d_f_filtered_2 = map_3d_f_norm_2 * fsc_3d # update iteration half_map_3d_f_1 = map_3d_f_filtered_1 From 01ceaf5f7f80614fae628a89649908ca11d1a9a0 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Mon, 28 Mar 2022 21:35:29 +0000 Subject: [PATCH 124/148] Format code with black --- .../expectation_maximization.py | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index 5e98350..93674ea 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -43,7 +43,7 @@ def __init__(self, map_3d_init, particles, ctf_info, max_itr=7): self.ctf_info = ctf_info self.max_itr = max_itr - def iterative_refinement(self, wiener_small_number = 0.01, count_norm_const = 1): + def iterative_refinement(self, wiener_small_number=0.01, count_norm_const=1): """Perform iterative refinement. Acts in a Bayesian expectation maximization setting, @@ -54,7 +54,7 @@ def iterative_refinement(self, wiener_small_number = 0.01, count_norm_const = 1) wiener_small_number : float Used to tune Wiener filter. count_norm_const : float - Used to tune normalization of slice inserting. + Used to tune normalization of slice inserting. Returns ------- @@ -197,18 +197,24 @@ def iterative_refinement(self, wiener_small_number = 0.01, count_norm_const = 1) map_3d_f_updated_2 += inserted_slice_3d_r + 1j * inserted_slice_3d_i counts_3d_updated_2 += count_3d_r + count_3d_i - # normalize maps by slice counts to account + # normalize maps by slice counts to account # for spherical density differences - map_3d_f_norm_1 = map_3d_f_updated_1 * counts_3d_updated_1 / (count_norm_const + counts_3d_updated_1**2) - map_3d_f_norm_1 = map_3d_f_updated_2 * counts_3d_updated_2 / (count_norm_const + counts_3d_updated_2**2) + map_3d_f_norm_1 = ( + map_3d_f_updated_1 + * counts_3d_updated_1 + / (count_norm_const + counts_3d_updated_1**2) + ) + map_3d_f_norm_1 = ( + map_3d_f_updated_2 + * counts_3d_updated_2 + / (count_norm_const + counts_3d_updated_2**2) + ) # apply noise model # half_map_1, half_map_2 come from doing the above # independently. Filter by noise estimate (e.g. multiply # both half maps by FSC) - fsc_1d = IterativeRefinement.compute_fsc( - map_3d_f_norm_1, map_3d_f_norm_2 - ) + fsc_1d = IterativeRefinement.compute_fsc(map_3d_f_norm_1, map_3d_f_norm_2) fsc_3d = IterativeRefinement.expand_1d_to_3d(fsc_1d) From 20f223217f69971f5fcbaeb65a059de285af63a1 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Wed, 30 Mar 2022 17:09:01 -0700 Subject: [PATCH 125/148] Removed comments, refactored big method, fixed bayesian weights shapes --- .../expectation_maximization.py | 124 +++++++++--------- tests/test_expectation_maximization.py | 2 +- 2 files changed, 60 insertions(+), 66 deletions(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index 93674ea..cf95b0c 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -87,18 +87,10 @@ def iterative_refinement(self, wiener_small_number=0.01, count_norm_const=1): n_rotations = self.particles.shape[0] - # suggest 32 or 64 to start with. real data will be more like - # 128 or 256. Can have issues with ctf at small pixels and - # need to zero pad to avoid artefacts. Artefacts from ctf not - # going to zero at edges, and sinusoidal ctf rippling too fast - # can zero pad when do Fourier convolution (fft is on zero - # padded and larger sized array) - half_map_3d_r_1, half_map_3d_r_2 = ( self.map_3d_init.copy(), self.map_3d_init.copy(), ) - # should diverge because different particles averaging in half_map_3d_f_1 = IterativeRefinement.fft_3d(half_map_3d_r_1) half_map_3d_f_2 = IterativeRefinement.fft_3d(half_map_3d_r_2) @@ -108,19 +100,6 @@ def iterative_refinement(self, wiener_small_number=0.01, count_norm_const=1): half_map_3d_f_1 = IterativeRefinement.fft_3d(half_map_3d_r_1) half_map_3d_f_2 = IterativeRefinement.fft_3d(half_map_3d_r_2) - # align particles to 3D volume - # decide on granularity of rotations - # i.e. how finely the rotational SO(3) space is sampled - # in a grid search. Smarter method is branch and bound... - # perhaps can make grid up front of slices, and then only - # compute norms on finer grid later. So re-use slices. - - # def do_adaptive_grid_search(particle, map_3d): - # a la branch and bound. Not sure exactly how you decide - # how finely gridded to make it. Perhaps heuristics - # based on how well the signal agrees in half_map_1, - # half_map_2 (Fourier frequency). - rots = IterativeRefinement.grid_SO3_uniform(n_rotations) xy0_plane = IterativeRefinement.generate_xy_plane(n_pix) @@ -134,25 +113,16 @@ def iterative_refinement(self, wiener_small_number=0.01, count_norm_const=1): half_map_3d_f_2, xy0_plane, n_pix, rots ) - # initialize: - # complex map_3d_f_updated_1 = np.zeros_like(half_map_3d_f_1) - # complex map_3d_f_updated_2 = np.zeros_like(half_map_3d_f_2) - # complex map_3d_f_norm_1 = np.zeros_like(half_map_3d_f_1) - # complex map_3d_f_norm_2 = np.zeros_like(half_map_3d_f_2) - # float/real counts_3d_updated_1 = np.zeros_like(half_map_3d_r_1) - # float/real counts_3d_updated_2 = np.zeros_like(half_map_3d_r_2) for particle_idx in range(particles_f_1.shape[0]): ctf_1 = ctfs_1[particle_idx] ctf_2 = ctfs_2[particle_idx] - # Option: particle_f_1 = particles_f_1[particle_idx] - # Option: particle_f_2 = particles_f_2[particle_idx] particle_f_deconv_1 = IterativeRefinement.apply_wiener_filter( particles_f_1, ctf_1, wiener_small_number @@ -161,11 +131,10 @@ def iterative_refinement(self, wiener_small_number=0.01, count_norm_const=1): particles_f_2, ctf_1, wiener_small_number ) - # all slices get convolved with the particle ctf - apply_ctf = np.vectorize(IterativeRefinement.apply_ctf_to_slice) + ctf_vectorized = np.vectorize(IterativeRefinement.apply_ctf_to_slice) - slices_conv_ctfs_1 = apply_ctf(slices_1, ctf_1) - slices_conv_ctfs_2 = apply_ctf(slices_2, ctf_2) + slices_conv_ctfs_1 = ctf_vectorized(slices_1, ctf_1) + slices_conv_ctfs_2 = ctf_vectorized(slices_2, ctf_2) bayes_factors_1 = IterativeRefinement.compute_bayesian_weights( particles_f_1[particle_idx], slices_conv_ctfs_1 @@ -176,7 +145,6 @@ def iterative_refinement(self, wiener_small_number=0.01, count_norm_const=1): for one_slice_idx in range(bayes_factors_1.shape[0]): xyz = xyz_rotated[one_slice_idx] - # if this can be vectorized, can avoid loop inserted_slice_3d_r, count_3d_r = IterativeRefinement.insert_slice( particle_f_deconv_1.real, xyz, n_pix ) @@ -199,37 +167,11 @@ def iterative_refinement(self, wiener_small_number=0.01, count_norm_const=1): # normalize maps by slice counts to account # for spherical density differences - map_3d_f_norm_1 = ( - map_3d_f_updated_1 - * counts_3d_updated_1 - / (count_norm_const + counts_3d_updated_1**2) - ) - map_3d_f_norm_1 = ( - map_3d_f_updated_2 - * counts_3d_updated_2 - / (count_norm_const + counts_3d_updated_2**2) - ) - - # apply noise model - # half_map_1, half_map_2 come from doing the above - # independently. Filter by noise estimate (e.g. multiply - # both half maps by FSC) - fsc_1d = IterativeRefinement.compute_fsc(map_3d_f_norm_1, map_3d_f_norm_2) - - fsc_3d = IterativeRefinement.expand_1d_to_3d(fsc_1d) - - # multiplicative filter on maps with fsc - # The FSC is 1D, one number per spherical shells - # it can be expanded back to a multiplicative filter of - # the same shape as the maps - map_3d_f_filtered_1 = map_3d_f_norm_1 * fsc_3d - map_3d_f_filtered_2 = map_3d_f_norm_2 * fsc_3d + map_3d_f_norm_1 = IterativeRefinement.normalize_map(map_3d_f_updated_1, counts_3d_updated_1, count_norm_const) + map_3d_f_norm_2 = IterativeRefinement.normalize_map(map_3d_f_updated_2, counts_3d_updated_2, count_norm_const) - # update iteration - half_map_3d_f_1 = map_3d_f_filtered_1 - half_map_3d_f_2 = map_3d_f_filtered_2 + half_map_3d_f_1, half_map_3d_f_2 = apply_noise_model(map_3d_f_norm_1, map_3d_f_norm_2) - # final map fsc_1d = IterativeRefinement.compute_fsc(half_map_3d_f_1, half_map_3d_f_2) fsc_3d = IterativeRefinement.expand_1d_to_3d(fsc_1d) map_3d_f_final = (half_map_3d_f_1 + half_map_3d_f_2 / 2) * fsc_3d @@ -239,6 +181,58 @@ def iterative_refinement(self, wiener_small_number=0.01, count_norm_const=1): return map_3d_r_final, half_map_3d_r_1, half_map_3d_r_2, fsc_1d + @staticmethod + def normalize_map(map, counts, norm_const): + """Normalize map by slice counts per voxel. + + Parameters + ---------- + map : arr + Shape (n_pix, n_pix, n_pix) + The map to be normalized. + counts : arr + Shape (n_pix, n_pix, n_pix) + The number of slices that were added within each voxel. + norm_const : float + A small number used as part of the wiener-filter-like + normalization. + + Returns + ------- + norm_map : arr + Shape (n_pix, n_pix, n_pix) + map normalized by counts. + """ + return map * counts / (norm_const + counts**2) + + @staticmethod + def apply_noise_model(map_3d_f_norm_1, map_3d_f_norm_2): + """Apply noise model to normalized maps in fourier space. + + Parameters + ---------- + map_3d_f_norm_1 : arr + Shape (n_pix, n_pix, n_pix) + Normalized fourier space half-map 1. + map_3d_f_norm_2 : arr + Shape (n_pix, n_pix, n_pix) + Normalized fourier space half-map 2. + + Returns + ------- + (map_3d_f_filtered_1, map_3d_f_filtered_2) : (arr, arr) + Shapes (n_pix, n_pix, n_pix) + Half-maps with fsc noise filtering applied. + """ + fsc_1d = IterativeRefinement.compute_fsc(map_3d_f_norm_1, map_3d_f_norm_2) + + fsc_3d = IterativeRefinement.expand_1d_to_3d(fsc_1d) + + map_3d_f_filtered_1 = map_3d_f_norm_1 * fsc_3d + map_3d_f_filtered_2 = map_3d_f_norm_2 * fsc_3d + + return (map_3d_f_filtered_1, map_3d_f_filtered_2) + @staticmethod def split_array(arr): """Split array into two halves along 0th axis. @@ -387,7 +381,7 @@ def compute_bayesian_weights(particle, slices): Parameters ---------- particle : arr - Shape (n_pix // 2,n_pix,n_pix) + Shape (n_pix, n_pix) slices : complex64 arr Shape (n_slices, n_pix, n_pix) diff --git a/tests/test_expectation_maximization.py b/tests/test_expectation_maximization.py index 7a17b24..37ee222 100644 --- a/tests/test_expectation_maximization.py +++ b/tests/test_expectation_maximization.py @@ -120,7 +120,7 @@ def test_compute_bayesian_weights(test_ir, n_particles, n_pix): For use under Gaussian white noise model. """ - particle = np.ones((n_pix // 2, n_pix, n_pix)) + particle = np.ones((n_pix, n_pix)) slices = np.ones((n_particles, n_pix, n_pix)) bayesian_weights = test_ir.compute_bayesian_weights(particle, slices) From e76d8a29169861dc472d27fcf41e1849abef429b Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Thu, 31 Mar 2022 00:09:01 +0000 Subject: [PATCH 126/148] Format code with black --- .../expectation_maximization.py | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index cf95b0c..72c5d35 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -167,10 +167,16 @@ def iterative_refinement(self, wiener_small_number=0.01, count_norm_const=1): # normalize maps by slice counts to account # for spherical density differences - map_3d_f_norm_1 = IterativeRefinement.normalize_map(map_3d_f_updated_1, counts_3d_updated_1, count_norm_const) - map_3d_f_norm_2 = IterativeRefinement.normalize_map(map_3d_f_updated_2, counts_3d_updated_2, count_norm_const) + map_3d_f_norm_1 = IterativeRefinement.normalize_map( + map_3d_f_updated_1, counts_3d_updated_1, count_norm_const + ) + map_3d_f_norm_2 = IterativeRefinement.normalize_map( + map_3d_f_updated_2, counts_3d_updated_2, count_norm_const + ) - half_map_3d_f_1, half_map_3d_f_2 = apply_noise_model(map_3d_f_norm_1, map_3d_f_norm_2) + half_map_3d_f_1, half_map_3d_f_2 = apply_noise_model( + map_3d_f_norm_1, map_3d_f_norm_2 + ) fsc_1d = IterativeRefinement.compute_fsc(half_map_3d_f_1, half_map_3d_f_2) fsc_3d = IterativeRefinement.expand_1d_to_3d(fsc_1d) @@ -184,7 +190,7 @@ def iterative_refinement(self, wiener_small_number=0.01, count_norm_const=1): @staticmethod def normalize_map(map, counts, norm_const): """Normalize map by slice counts per voxel. - + Parameters ---------- map : arr @@ -196,7 +202,7 @@ def normalize_map(map, counts, norm_const): norm_const : float A small number used as part of the wiener-filter-like normalization. - + Returns ------- norm_map : arr @@ -208,7 +214,7 @@ def normalize_map(map, counts, norm_const): @staticmethod def apply_noise_model(map_3d_f_norm_1, map_3d_f_norm_2): """Apply noise model to normalized maps in fourier space. - + Parameters ---------- map_3d_f_norm_1 : arr @@ -217,7 +223,7 @@ def apply_noise_model(map_3d_f_norm_1, map_3d_f_norm_2): map_3d_f_norm_2 : arr Shape (n_pix, n_pix, n_pix) Normalized fourier space half-map 2. - + Returns ------- (map_3d_f_filtered_1, map_3d_f_filtered_2) : (arr, arr) From 6e7ed3249e9d919d0f18f1a3f68a4da41257f721 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Wed, 30 Mar 2022 17:14:57 -0700 Subject: [PATCH 127/148] Infrastructure files --- dev-requirements.txt | 5 ++++ environment.yml | 18 +++++++++++++++ pytest.ini | 4 ++++ reconstructSPI/__init__.py | 1 + .../iterative_refinement/__init__.py | 1 + setup.py | 23 +++++++++++++++++++ tests/__init__.py | 0 7 files changed, 52 insertions(+) create mode 100644 dev-requirements.txt create mode 100644 environment.yml create mode 100644 pytest.ini create mode 100644 reconstructSPI/__init__.py create mode 100644 reconstructSPI/iterative_refinement/__init__.py create mode 100644 setup.py create mode 100644 tests/__init__.py diff --git a/dev-requirements.txt b/dev-requirements.txt new file mode 100644 index 0000000..663cad4 --- /dev/null +++ b/dev-requirements.txt @@ -0,0 +1,5 @@ +black +flake8 +flake8-docstrings +isort +pre-commit diff --git a/environment.yml b/environment.yml new file mode 100644 index 0000000..e52e0d7 --- /dev/null +++ b/environment.yml @@ -0,0 +1,18 @@ +name: reconstructSPI +channels: + - conda-forge + - defaults +dependencies: + - codecov + - coverage + - gemmi + - numpy + - numba + - pillow>=8.2.0 + - pip + - pytest + - pytest-cov + - pytorch + - pip : + - git+https://github.com/compSPI/simSPI.git + diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..cf93d60 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,4 @@ +[pytest] +testpaths = tests +python_files = *.py +python_functions = test_* \ No newline at end of file diff --git a/reconstructSPI/__init__.py b/reconstructSPI/__init__.py new file mode 100644 index 0000000..a16d370 --- /dev/null +++ b/reconstructSPI/__init__.py @@ -0,0 +1 @@ +"""Various particle reconstruction methods.""" diff --git a/reconstructSPI/iterative_refinement/__init__.py b/reconstructSPI/iterative_refinement/__init__.py new file mode 100644 index 0000000..5500bfe --- /dev/null +++ b/reconstructSPI/iterative_refinement/__init__.py @@ -0,0 +1 @@ +"""Iterative refinement reconstruction methods.""" diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..9631ff3 --- /dev/null +++ b/setup.py @@ -0,0 +1,23 @@ +"""Create instructions to build the reconstructSPI package.""" + +import setuptools + +requirements = [] + +setuptools.setup( + name="reconstructSPI", + maintainer="Frederic Poitevin", + version="0.0.1", + maintainer_email="frederic.poitevin@stanford.edu", + description="Reconstruction methods and tools for SPI", + long_description=open("README.md", encoding="utf8").read(), + long_description_content_type="text/markdown", + url="https://github.com/compSPI/reconstructSPI.git", + packages=setuptools.find_packages(), + install_requires=requirements, + classifiers=[ + "Programming Language :: Python :: 3", + "Operating System :: OS Independent", + ], + zip_safe=False, +) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 From 6a9ee6963c82c93b2190944f5fa64b55ad5f63dc Mon Sep 17 00:00:00 2001 From: thisTyler Date: Wed, 30 Mar 2022 17:16:24 -0700 Subject: [PATCH 128/148] Infrastructure files --- .codecov.yml | 24 +++++++++++++++ .coveragerc | 3 ++ .deepsource.toml | 10 ++---- .flake8 | 6 ++++ .github/auto-assign.yml | 4 +-- .github/workflows/lint.yml | 54 +++++++++++++++++++++++++++++++++ .github/workflows/test.yml | 62 ++++++++++++++++++++++++++++++++++++++ .pre-commit-config.yaml | 42 ++++++++++++++++++++++++++ 8 files changed, 195 insertions(+), 10 deletions(-) create mode 100644 .codecov.yml create mode 100644 .coveragerc create mode 100644 .flake8 create mode 100644 .github/workflows/lint.yml create mode 100644 .github/workflows/test.yml create mode 100644 .pre-commit-config.yaml diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 0000000..c576999 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,24 @@ +coverage: + round: up + precision: 2 + status: + patch: + default: + # basic + target: 90% + threshold: 2% + base: auto + flags: + - unit + # advanced + branches: + - master + if_no_uploads: error + if_not_found: error + if_ci_failed: error + only_pulls: false + +# Files to ignore +ignore: + - "data" + - "notebooks" diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..94a9d5a --- /dev/null +++ b/.coveragerc @@ -0,0 +1,3 @@ +[report] +fail_under = 90 +show_missing = True diff --git a/.deepsource.toml b/.deepsource.toml index 93a9d6f..b4f754a 100644 --- a/.deepsource.toml +++ b/.deepsource.toml @@ -1,6 +1,6 @@ version = 1 -test_patterns = ["test/**"] +test_patterns = ["tests/**"] exclude_patterns = ["README.md"] @@ -8,17 +8,11 @@ exclude_patterns = ["README.md"] name = "python" enabled = true - [analyzers.meta] - runtime_version = "3.x.x" - [[analyzers]] name = "test-coverage" enabled = true -[[transformers]] -name = "isort" -enabled = true - [[transformers]] name = "black" enabled = true + diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..ece9ae4 --- /dev/null +++ b/.flake8 @@ -0,0 +1,6 @@ +[flake8] +docstring-convention = numpy +import_order_style = smarkets +max-line-length = 88 +extend-ignore = E203,I202,I100 +exclude = reconstructSPI/__init__.py,reconstructSPI/iterative_refinement/__init.py,tests/__init__.py diff --git a/.github/auto-assign.yml b/.github/auto-assign.yml index 115af98..a0a74eb 100644 --- a/.github/auto-assign.yml +++ b/.github/auto-assign.yml @@ -5,10 +5,10 @@ addReviewers: true addAssignees: false # A list of reviewers to be added to pull requests (GitHub user name) -reviewers: +reviewers: - fredericpoitevin -# A list of keywords to be skipped the process that add reviewers if pull requests include it +# A list of keywords to be skipped the process that add reviewers if pull requests include it skipKeywords: - wip diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..cfc5574 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,54 @@ +name: "Lint" + +on: + push: + branches: [master,github-actions-test] + paths-ignore: + - 'README.md' + - '.deepsource.toml' + - '.gitignore' + - 'setup.py' + + + pull_request: + branches: [master] + paths-ignore: + - 'README.md' + - '.deepsource.toml' + - '.gitignore' + - 'setup.py' + + +jobs: + build: + + runs-on: ${{matrix.os}} + strategy: + matrix: + os: [ubuntu-18.04] + python-version: [3.9] + test-folder : ['tests'] + fail-fast: false + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Build using Python ${{matrix.python-version}} + uses: actions/setup-python@v2 + with: + python-version: ${{matrix.python-version}} + - name: install dependencies [pip] + run: | + pip install --upgrade pip setuptools wheel + for req in dev-requirements.txt; do + pip install -q -r $req + done + pip install -e . + - name: linting [black and isort] + run: | + black . --check + isort --profile black --check . + - name: linting [flake8] + run: | + flake8 reconstructSPI tests diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..49cc26f --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,62 @@ +name: "Test" + +on: + push: + branches: [master,github-actions-test] + paths-ignore: + - 'README.md' + - '.deepsource.toml' + - '.gitignore' + - 'setup.py' + + + pull_request: + branches: [master] + paths-ignore: + - 'README.md' + - '.deepsource.toml' + - '.gitignore' + - 'setup.py' + + +jobs: + build: + + runs-on: ${{matrix.os}} + strategy: + matrix: + os: [ubuntu-18.04] + python-version: [3.7,3.8,3.9] + test-folder : ['tests'] + fail-fast: false + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Build using Python ${{matrix.python-version}} + uses: actions/setup-python@v2 + with: + python-version: ${{matrix.python-version}} + + - name: cache conda + uses: actions/cache@v1 + with: + path: $CONDA + key: ${{ runner.os }}-conda-${{ hashFiles('environment.yml') }} + restore-keys: | + ${{ runner.os }}-conda- + - name: install dependencies [conda] + run: | + # $CONDA is an environment variable pointing to the root of the miniconda directory + $CONDA/bin/conda env update --file environment.yml --name base + $CONDA/bin/pip install -e . + - name: unit testing [pytest] + env: + TEST_TOKEN: ${{ secrets.TEST_TOKEN }} + run: | + $CONDA/bin/pytest --cov-report term --cov-report xml:coverage.xml --cov=reconstructSPI ${{matrix.test-folder}} + - name: uploading code coverage [codecov] + if: ${{matrix.python-version == 3.7}} + run: | + bash <(curl -s https://codecov.io/bash) -c diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..e0b31c6 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,42 @@ +default_language_version : + python : python3 +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.1.0 + hooks: + - id: check-byte-order-marker + - id: check-case-conflict + - id: check-merge-conflict + - id: check-yaml + - id: mixed-line-ending + args: + - --fix=no + - id: no-commit-to-branch + args: + - --branch=master + - id: check-added-large-files + args: + - --maxkb=2048 + - id: trailing-whitespace + - repo: https://github.com/psf/black + rev: 21.8b0 + hooks: + - id: black + - repo: https://github.com/pycqa/isort + rev: 5.9.3 + hooks: + - id : isort + args : ["--profile", "black", "--filter-files"] + - repo: https://github.com/asottile/blacken-docs + rev: v1.8.0 + hooks: + - id: blacken-docs + additional_dependencies: [black==20.8b0] + - repo: https://gitlab.com/pycqa/flake8 + rev: 3.7.9 + hooks: + - id: flake8 + additional_dependencies: + - flake8-docstrings + - flake8-import-order + exclude: reconstructSPI/__init__.py,tests/__init__.py \ No newline at end of file From fd9cefafd6604748cf0f8b25b832e40507d75284 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Wed, 30 Mar 2022 17:24:28 -0700 Subject: [PATCH 129/148] Missed a name --- reconstructSPI/iterative_refinement/expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index cf95b0c..7e01774 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -170,7 +170,7 @@ def iterative_refinement(self, wiener_small_number=0.01, count_norm_const=1): map_3d_f_norm_1 = IterativeRefinement.normalize_map(map_3d_f_updated_1, counts_3d_updated_1, count_norm_const) map_3d_f_norm_2 = IterativeRefinement.normalize_map(map_3d_f_updated_2, counts_3d_updated_2, count_norm_const) - half_map_3d_f_1, half_map_3d_f_2 = apply_noise_model(map_3d_f_norm_1, map_3d_f_norm_2) + half_map_3d_f_1, half_map_3d_f_2 = IterativeRefinement.apply_noise_model(map_3d_f_norm_1, map_3d_f_norm_2) fsc_1d = IterativeRefinement.compute_fsc(half_map_3d_f_1, half_map_3d_f_2) fsc_3d = IterativeRefinement.expand_1d_to_3d(fsc_1d) From bb8b274c1846d9b5e9c5d9350edf24848573a6c5 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Wed, 30 Mar 2022 17:25:02 -0700 Subject: [PATCH 130/148] Removed last few comments, will re-add if needed --- .../iterative_refinement/expectation_maximization.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index 7e01774..8d0bc49 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -77,9 +77,6 @@ def iterative_refinement(self, wiener_small_number=0.01, count_norm_const=1): ctfs = self.build_ctf_array() ctfs_1, ctfs_2 = IterativeRefinement.split_array(ctfs) - # work in Fourier space. So particles can stay in Fourier - # space the whole time. They are experimental measurements - # and are fixed in the algorithm particles_f_1 = IterativeRefinement.fft_3d(particles_1) particles_f_2 = IterativeRefinement.fft_3d(particles_2) @@ -107,8 +104,7 @@ def iterative_refinement(self, wiener_small_number=0.01, count_norm_const=1): slices_1, xyz_rotated = IterativeRefinement.generate_slices( half_map_3d_f_1, xy0_plane, n_pix, rots ) - # Here rots are the same for the half maps, - # but could be different in general. + slices_2, xyz_rotated = IterativeRefinement.generate_slices( half_map_3d_f_2, xy0_plane, n_pix, rots ) @@ -165,8 +161,6 @@ def iterative_refinement(self, wiener_small_number=0.01, count_norm_const=1): map_3d_f_updated_2 += inserted_slice_3d_r + 1j * inserted_slice_3d_i counts_3d_updated_2 += count_3d_r + count_3d_i - # normalize maps by slice counts to account - # for spherical density differences map_3d_f_norm_1 = IterativeRefinement.normalize_map(map_3d_f_updated_1, counts_3d_updated_1, count_norm_const) map_3d_f_norm_2 = IterativeRefinement.normalize_map(map_3d_f_updated_2, counts_3d_updated_2, count_norm_const) From 66c0ef30bba87a4105eccbca23b12807d1ed905b Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Thu, 31 Mar 2022 00:25:59 +0000 Subject: [PATCH 131/148] Format code with black --- .../expectation_maximization.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index a9f951b..31f31e6 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -104,7 +104,7 @@ def iterative_refinement(self, wiener_small_number=0.01, count_norm_const=1): slices_1, xyz_rotated = IterativeRefinement.generate_slices( half_map_3d_f_1, xy0_plane, n_pix, rots ) - + slices_2, xyz_rotated = IterativeRefinement.generate_slices( half_map_3d_f_2, xy0_plane, n_pix, rots ) @@ -161,10 +161,16 @@ def iterative_refinement(self, wiener_small_number=0.01, count_norm_const=1): map_3d_f_updated_2 += inserted_slice_3d_r + 1j * inserted_slice_3d_i counts_3d_updated_2 += count_3d_r + count_3d_i - map_3d_f_norm_1 = IterativeRefinement.normalize_map(map_3d_f_updated_1, counts_3d_updated_1, count_norm_const) - map_3d_f_norm_2 = IterativeRefinement.normalize_map(map_3d_f_updated_2, counts_3d_updated_2, count_norm_const) + map_3d_f_norm_1 = IterativeRefinement.normalize_map( + map_3d_f_updated_1, counts_3d_updated_1, count_norm_const + ) + map_3d_f_norm_2 = IterativeRefinement.normalize_map( + map_3d_f_updated_2, counts_3d_updated_2, count_norm_const + ) - half_map_3d_f_1, half_map_3d_f_2 = IterativeRefinement.apply_noise_model(map_3d_f_norm_1, map_3d_f_norm_2) + half_map_3d_f_1, half_map_3d_f_2 = IterativeRefinement.apply_noise_model( + map_3d_f_norm_1, map_3d_f_norm_2 + ) fsc_1d = IterativeRefinement.compute_fsc(half_map_3d_f_1, half_map_3d_f_2) fsc_3d = IterativeRefinement.expand_1d_to_3d(fsc_1d) From 579f716fd5159ca303602cf9b46223e80c14a43a Mon Sep 17 00:00:00 2001 From: thisTyler Date: Wed, 30 Mar 2022 17:36:11 -0700 Subject: [PATCH 132/148] Fixed variable name --- .../iterative_refinement/expectation_maximization.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index a9f951b..46529b9 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -176,12 +176,12 @@ def iterative_refinement(self, wiener_small_number=0.01, count_norm_const=1): return map_3d_r_final, half_map_3d_r_1, half_map_3d_r_2, fsc_1d @staticmethod - def normalize_map(map, counts, norm_const): + def normalize_map(map_3d, counts, norm_const): """Normalize map by slice counts per voxel. Parameters ---------- - map : arr + map_3d : arr Shape (n_pix, n_pix, n_pix) The map to be normalized. counts : arr @@ -197,7 +197,7 @@ def normalize_map(map, counts, norm_const): Shape (n_pix, n_pix, n_pix) map normalized by counts. """ - return map * counts / (norm_const + counts**2) + return map_3d * counts / (norm_const + counts**2) @staticmethod def apply_noise_model(map_3d_f_norm_1, map_3d_f_norm_2): From 55ee718db684b04c348d8f1205bb9dbea620d369 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Wed, 30 Mar 2022 17:39:27 -0700 Subject: [PATCH 133/148] Changed codecov parameters --- .codecov.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.codecov.yml b/.codecov.yml index c576999..2a942b9 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -22,3 +22,5 @@ coverage: ignore: - "data" - "notebooks" + - "reconstructSPI/__init__.py" + - "reconstructSPI/iterative_refinement/__init__.py" From 0d18f66b662f5bbb0ac11f8be3395d0fe5ba8e5d Mon Sep 17 00:00:00 2001 From: thisTyler Date: Wed, 30 Mar 2022 17:48:43 -0700 Subject: [PATCH 134/148] Removed __init__.py files --- .codecov.yml | 2 -- reconstructSPI/__init__.py | 1 - reconstructSPI/iterative_refinement/__init__.py | 1 - 3 files changed, 4 deletions(-) delete mode 100644 reconstructSPI/__init__.py delete mode 100644 reconstructSPI/iterative_refinement/__init__.py diff --git a/.codecov.yml b/.codecov.yml index 2a942b9..c576999 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -22,5 +22,3 @@ coverage: ignore: - "data" - "notebooks" - - "reconstructSPI/__init__.py" - - "reconstructSPI/iterative_refinement/__init__.py" diff --git a/reconstructSPI/__init__.py b/reconstructSPI/__init__.py deleted file mode 100644 index a16d370..0000000 --- a/reconstructSPI/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Various particle reconstruction methods.""" diff --git a/reconstructSPI/iterative_refinement/__init__.py b/reconstructSPI/iterative_refinement/__init__.py deleted file mode 100644 index 5500bfe..0000000 --- a/reconstructSPI/iterative_refinement/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Iterative refinement reconstruction methods.""" From 7f8145686e1e416aa0fc9d4438b296516e172456 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Wed, 30 Mar 2022 17:50:36 -0700 Subject: [PATCH 135/148] Revert "Removed __init__.py files" This reverts commit 0d18f66b662f5bbb0ac11f8be3395d0fe5ba8e5d. --- .codecov.yml | 2 ++ reconstructSPI/__init__.py | 1 + reconstructSPI/iterative_refinement/__init__.py | 1 + 3 files changed, 4 insertions(+) create mode 100644 reconstructSPI/__init__.py create mode 100644 reconstructSPI/iterative_refinement/__init__.py diff --git a/.codecov.yml b/.codecov.yml index c576999..2a942b9 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -22,3 +22,5 @@ coverage: ignore: - "data" - "notebooks" + - "reconstructSPI/__init__.py" + - "reconstructSPI/iterative_refinement/__init__.py" diff --git a/reconstructSPI/__init__.py b/reconstructSPI/__init__.py new file mode 100644 index 0000000..a16d370 --- /dev/null +++ b/reconstructSPI/__init__.py @@ -0,0 +1 @@ +"""Various particle reconstruction methods.""" diff --git a/reconstructSPI/iterative_refinement/__init__.py b/reconstructSPI/iterative_refinement/__init__.py new file mode 100644 index 0000000..5500bfe --- /dev/null +++ b/reconstructSPI/iterative_refinement/__init__.py @@ -0,0 +1 @@ +"""Iterative refinement reconstruction methods.""" From e583a54eaf9469bc74cb2fdafbb88211b6d3cead Mon Sep 17 00:00:00 2001 From: thisTyler Date: Wed, 30 Mar 2022 18:01:51 -0700 Subject: [PATCH 136/148] Revert "Changed codecov parameters" This reverts commit 55ee718db684b04c348d8f1205bb9dbea620d369. --- .codecov.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.codecov.yml b/.codecov.yml index 2a942b9..c576999 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -22,5 +22,3 @@ coverage: ignore: - "data" - "notebooks" - - "reconstructSPI/__init__.py" - - "reconstructSPI/iterative_refinement/__init__.py" From 15840cfcf45e877cd069b7677bdb5ebaefc2ed30 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Wed, 30 Mar 2022 18:06:26 -0700 Subject: [PATCH 137/148] Refactoring library format --- .flake8 | 2 +- reconstructSPI/__init__.py => __init__.py | 0 .../iterative_refinement => iterative_refinement}/__init__.py | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename reconstructSPI/__init__.py => __init__.py (100%) rename {reconstructSPI/iterative_refinement => iterative_refinement}/__init__.py (100%) diff --git a/.flake8 b/.flake8 index ece9ae4..78fd79a 100644 --- a/.flake8 +++ b/.flake8 @@ -3,4 +3,4 @@ docstring-convention = numpy import_order_style = smarkets max-line-length = 88 extend-ignore = E203,I202,I100 -exclude = reconstructSPI/__init__.py,reconstructSPI/iterative_refinement/__init.py,tests/__init__.py +exclude = __init__.py,iterative_refinement/__init.py,tests/__init__.py diff --git a/reconstructSPI/__init__.py b/__init__.py similarity index 100% rename from reconstructSPI/__init__.py rename to __init__.py diff --git a/reconstructSPI/iterative_refinement/__init__.py b/iterative_refinement/__init__.py similarity index 100% rename from reconstructSPI/iterative_refinement/__init__.py rename to iterative_refinement/__init__.py From 2f91bc6686048ac4f39c619e38b006e4268499f0 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Wed, 30 Mar 2022 18:10:42 -0700 Subject: [PATCH 138/148] Directory fixes --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e0b31c6..5af6583 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -39,4 +39,4 @@ repos: additional_dependencies: - flake8-docstrings - flake8-import-order - exclude: reconstructSPI/__init__.py,tests/__init__.py \ No newline at end of file + exclude: __init__.py,iterative_refinement/__init__.py,tests/__init__.py \ No newline at end of file From bccb41ce29d8055ea02a632d8f1e3b8ef51080eb Mon Sep 17 00:00:00 2001 From: thisTyler Date: Wed, 30 Mar 2022 18:13:43 -0700 Subject: [PATCH 139/148] Revert "Refactoring library format" This reverts commit 15840cfcf45e877cd069b7677bdb5ebaefc2ed30. --- .flake8 | 2 +- __init__.py => reconstructSPI/__init__.py | 0 .../iterative_refinement}/__init__.py | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename __init__.py => reconstructSPI/__init__.py (100%) rename {iterative_refinement => reconstructSPI/iterative_refinement}/__init__.py (100%) diff --git a/.flake8 b/.flake8 index 78fd79a..ece9ae4 100644 --- a/.flake8 +++ b/.flake8 @@ -3,4 +3,4 @@ docstring-convention = numpy import_order_style = smarkets max-line-length = 88 extend-ignore = E203,I202,I100 -exclude = __init__.py,iterative_refinement/__init.py,tests/__init__.py +exclude = reconstructSPI/__init__.py,reconstructSPI/iterative_refinement/__init.py,tests/__init__.py diff --git a/__init__.py b/reconstructSPI/__init__.py similarity index 100% rename from __init__.py rename to reconstructSPI/__init__.py diff --git a/iterative_refinement/__init__.py b/reconstructSPI/iterative_refinement/__init__.py similarity index 100% rename from iterative_refinement/__init__.py rename to reconstructSPI/iterative_refinement/__init__.py From 2c42e8d884fc1cc2074e409eaebb1b5885590a24 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Wed, 30 Mar 2022 18:13:56 -0700 Subject: [PATCH 140/148] Revert "Directory fixes" This reverts commit 2f91bc6686048ac4f39c619e38b006e4268499f0. --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5af6583..e0b31c6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -39,4 +39,4 @@ repos: additional_dependencies: - flake8-docstrings - flake8-import-order - exclude: __init__.py,iterative_refinement/__init__.py,tests/__init__.py \ No newline at end of file + exclude: reconstructSPI/__init__.py,tests/__init__.py \ No newline at end of file From 9126f8684d07489d447b7b699f12c1e042d715d8 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Wed, 30 Mar 2022 18:41:00 -0700 Subject: [PATCH 141/148] Removed dependencies --- environment.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/environment.yml b/environment.yml index e52e0d7..ef85386 100644 --- a/environment.yml +++ b/environment.yml @@ -5,9 +5,6 @@ channels: dependencies: - codecov - coverage - - gemmi - - numpy - - numba - pillow>=8.2.0 - pip - pytest From 3330c11dbd0cbd2573dd6e19468f9ebaea19f8fd Mon Sep 17 00:00:00 2001 From: thisTyler Date: Wed, 30 Mar 2022 18:59:02 -0700 Subject: [PATCH 142/148] Testing something --- .codecov.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.codecov.yml b/.codecov.yml index c576999..92ae275 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -13,6 +13,7 @@ coverage: # advanced branches: - master + - reconstructSPI_infrastructure if_no_uploads: error if_not_found: error if_ci_failed: error From ef10249ed795c7f5c46e73a640ca8928a0545fb0 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Wed, 30 Mar 2022 19:02:13 -0700 Subject: [PATCH 143/148] Testing things --- .github/workflows/lint.yml | 2 +- .github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index cfc5574..b4d4102 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -2,7 +2,7 @@ name: "Lint" on: push: - branches: [master,github-actions-test] + branches: [master,github-actions-test,reconstructSPI_infrastructure] paths-ignore: - 'README.md' - '.deepsource.toml' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 49cc26f..b9f6232 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,7 +2,7 @@ name: "Test" on: push: - branches: [master,github-actions-test] + branches: [master,github-actions-test,reconstructSPI_infrastructure] paths-ignore: - 'README.md' - '.deepsource.toml' From 8dda440a8e0dd86f8c60d8de8e5592d43535f7bc Mon Sep 17 00:00:00 2001 From: thisTyler Date: Wed, 30 Mar 2022 19:06:08 -0700 Subject: [PATCH 144/148] Force checks --- reconstructSPI/iterative_refinement/expectation_maximization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index 14c5010..27d2a4d 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -47,7 +47,7 @@ def iterative_refinement(self, wiener_small_number=0.01, count_norm_const=1): """Perform iterative refinement. Acts in a Bayesian expectation maximization setting, - i.e. maximum a posteriori estimation. + i.e. using maximum a posteriori estimation. Parameters ---------- From 749bcd2d58d468878b246f37fdf74c80402eecd5 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Wed, 30 Mar 2022 19:08:27 -0700 Subject: [PATCH 145/148] Testing things --- .github/workflows/lint.yml | 2 +- .github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index b4d4102..884911e 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -11,7 +11,7 @@ on: pull_request: - branches: [master] + branches: [master,reconstructSPI_infrastructure] paths-ignore: - 'README.md' - '.deepsource.toml' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b9f6232..04b6e1f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ on: pull_request: - branches: [master] + branches: [master,reconstructSPI_infrastructure] paths-ignore: - 'README.md' - '.deepsource.toml' From 2ea9459adc3d0783bfafbcb6fd05786b7c985468 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Wed, 30 Mar 2022 19:12:02 -0700 Subject: [PATCH 146/148] Added library requirements --- environment.yml | 3 +++ .../iterative_refinement/expectation_maximization.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/environment.yml b/environment.yml index ef85386..e52e0d7 100644 --- a/environment.yml +++ b/environment.yml @@ -5,6 +5,9 @@ channels: dependencies: - codecov - coverage + - gemmi + - numpy + - numba - pillow>=8.2.0 - pip - pytest diff --git a/reconstructSPI/iterative_refinement/expectation_maximization.py b/reconstructSPI/iterative_refinement/expectation_maximization.py index 27d2a4d..14c5010 100644 --- a/reconstructSPI/iterative_refinement/expectation_maximization.py +++ b/reconstructSPI/iterative_refinement/expectation_maximization.py @@ -47,7 +47,7 @@ def iterative_refinement(self, wiener_small_number=0.01, count_norm_const=1): """Perform iterative refinement. Acts in a Bayesian expectation maximization setting, - i.e. using maximum a posteriori estimation. + i.e. maximum a posteriori estimation. Parameters ---------- From 5152547989399bbf5de59089676176ef88a6e17e Mon Sep 17 00:00:00 2001 From: thisTyler Date: Wed, 30 Mar 2022 19:16:15 -0700 Subject: [PATCH 147/148] Added dev branch to branches on which tests will run. --- .codecov.yml | 2 +- .github/workflows/lint.yml | 4 ++-- .github/workflows/test.yml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.codecov.yml b/.codecov.yml index 92ae275..fd85925 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -13,7 +13,7 @@ coverage: # advanced branches: - master - - reconstructSPI_infrastructure + - dev if_no_uploads: error if_not_found: error if_ci_failed: error diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index b4d4102..38ca19a 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -2,7 +2,7 @@ name: "Lint" on: push: - branches: [master,github-actions-test,reconstructSPI_infrastructure] + branches: [master,github-actions-test,dev] paths-ignore: - 'README.md' - '.deepsource.toml' @@ -11,7 +11,7 @@ on: pull_request: - branches: [master] + branches: [master,dev] paths-ignore: - 'README.md' - '.deepsource.toml' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b9f6232..0d86279 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,7 +2,7 @@ name: "Test" on: push: - branches: [master,github-actions-test,reconstructSPI_infrastructure] + branches: [master,github-actions-test,dev] paths-ignore: - 'README.md' - '.deepsource.toml' @@ -11,7 +11,7 @@ on: pull_request: - branches: [master] + branches: [master,dev] paths-ignore: - 'README.md' - '.deepsource.toml' From 9d05c1a2da059da4c56e4934579d8328b1f66244 Mon Sep 17 00:00:00 2001 From: thisTyler Date: Thu, 31 Mar 2022 12:42:46 -0700 Subject: [PATCH 148/148] removed itr_ref_docstrings branch checks --- .codecov.yml | 1 - .github/workflows/lint.yml | 4 ++-- .github/workflows/test.yml | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.codecov.yml b/.codecov.yml index ca076ae..fd85925 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -14,7 +14,6 @@ coverage: branches: - master - dev - - reconstructSPI_infrastructure if_no_uploads: error if_not_found: error if_ci_failed: error diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index c8fd042..38ca19a 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -2,7 +2,7 @@ name: "Lint" on: push: - branches: [master,github-actions-test,dev,reconstructSPI_infrastructure] + branches: [master,github-actions-test,dev] paths-ignore: - 'README.md' - '.deepsource.toml' @@ -11,7 +11,7 @@ on: pull_request: - branches: [master,dev,reconstructSPI_infrastructure] + branches: [master,dev] paths-ignore: - 'README.md' - '.deepsource.toml' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cf4e538..0d86279 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,7 +2,7 @@ name: "Test" on: push: - branches: [master,github-actions-test,dev,reconstructSPI_infrastructure] + branches: [master,github-actions-test,dev] paths-ignore: - 'README.md' - '.deepsource.toml' @@ -11,7 +11,7 @@ on: pull_request: - branches: [master,dev,reconstructSPI_infrastructure] + branches: [master,dev] paths-ignore: - 'README.md' - '.deepsource.toml'