Skip to content

Testing fast implementations of A* A where A is the projection operator #1131

@09pouchol

Description

@09pouchol

Dear Aspire Team,

to start off, let me say how grateful I am for all your work in developing and maintaining Aspire!

Problem description

I am currently testing built-in implementations for fast computations (via convolution) of the operator A* A where A is the projection operator, as given in the class mean.MeanEstimator with the reconstruction.mean module. When comparing such an implementation with the successive application of A and then the adjoint A*, I consistently find relative $L^2$ errors of the order of 1.

Note that the operators A and A* are checked to be adjoint of one another, as I have corrected for the bug uncovered thanks to a recent bug report (made by a colleague of mine). Hence the problem could come

  • either from me incorrectly using the class MeanEstimator,
  • or an actual bug within the corresponding code.

To Reproduce:

The code below defines A, A*, as well as A* A as obtained by the aforementioned method, and compares applying A* A or A and then A*, to randomly chosen vectors.

import numpy as np
import os, aspire
import matplotlib.pyplot as plt
from scipy.ndimage import correlate
from aspire.image import Image
from aspire.utils import Rotation
from aspire.volume import Volume
from aspire.operators import CTFFilter, RadialCTFFilter, FunctionFilter, DualFilter
from aspire.basis import FBBasis3D, FFBBasis3D
from aspire.reconstruction import MeanEstimator
from aspire.source.simulation import Simulation

print("****************\n* Check the identity (A^ast A) u = A^ast (A u)\n*****************")


#########################################################
# define direct & adjoint operators as lambda functions #
#########################################################
L = 16 # Image size
n_img = 1 # number of projections to simulate
dtype = np.float32
def_val= 1e4 # defocus value
rots = Rotation.generate_random_rotations(n=n_img, seed=31415) # random rotations
angles = rots.angles # Retrieve corresponding angles
basis = FFBBasis3D(L, ell_max = None) # Choose a basis to represent volumes (required for mean_kernel function),

# Compute A and A^ast 
ctf = RadialCTFFilter(defocus=def_val) # Ctf function
dual_ctf = DualFilter(ctf) # Define adjoint of ctf, i.e., x \mapsto ctf(-x) #
EMproj = lambda vol,rots : vol.project(rots).filter(ctf) # vol is an aspire volume and rots is an aspire rotation
EMbackproj = lambda proj,rots : (proj.filter(dual_ctf)).backproject(rots.matrices) # proj is an aspire im and rots is an aspire rot

A = lambda x_arr : EMproj(aspire.volume.Volume(x_arr),rots).asnumpy()
adjA = lambda y_arr : EMbackproj(aspire.image.Image(y_arr),rots).asnumpy()[0]

# Set simulation and estimators, necessary to compute B = A^ast A

sim = Simulation(
   L=L,
   n=n_img,
   unique_filters=[RadialCTFFilter(defocus=d) for d in np.linspace(def_val, def_val, 1)],
   offsets = 0,
   dtype=dtype,
   angles = angles
)

mean_estimator = MeanEstimator(sim, basis=basis)
mean_kernel = mean_estimator.kernel
B = lambda x_arr :  n_img*mean_kernel.convolve_volume(x_arr).asnumpy()[0]    


# Lauch unitary test
Nsimu = 100
relmax = -np.inf


for id in range(Nsimu):
   
   # sample random signals 
   u = np.random.rand(L,L,L).astype(np.float32)
   
   # compute A^ast (A u) 
   test1 =  adjA(A(u))
   
   # compute (A^\ast A) u
   test2 = B(u)
   
   
   # compute the L^2 relative error between A^ast (A u) and (A^\ast A) u
   rel = np.sqrt(np.sum((test1-test2)**2)/(np.sum(test1**2)))
   relmax = np.max([rel,relmax])
       
   # display result
   print("simulation %d/%d : relative error = %.3e, max. relative error = %.3e" % (id+1,Nsimu,rel,relmax))

Further discussion

Of course, there are many parameters involved here (such as the volume size, the number of images, the basis chosen to represent volumes, etc), but the problem seems to be quite robust with respect to changing these.

This leads me to a related question: does Aspire host implementations of A* A that do not rely on Fourier-Bessel bases? I could not find one, but I might be wrong.

Thank you in advance for your help,
Camille Pouchol

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions