Setting the NDArray Backend
Since version 0.5.0, qpretrieve allows the user to leverage their CUDA GPU
via the FFTFilterCupy fft interface and the CuPy library.
However, the data up/download from/to the GPU was inefficient.
This was due to the use of CPU-bound numpy
arrays between GPU-based Fourier Transforms.
Since version 0.6.0, you can control the desired ndarray backend.
An “ndarray backend” is defined as the library used to define the
ndarrays in qpretrieve during runtime. By default it is set to 'numpy'.
If you are using the FFTFilterCupy fft interface, it is
recommended to set the backend to 'cupy'. See the script below and
the benchmarking example
fft_batch_speeds.py in the Examples folder
for details on how to do this.
For more info, see the CuPy library.
There are currently two available ndarray backends: 'numpy' and 'cupy'.
Controlling the ndarray backend
qpretrieve allows users to swap between these backends with the
qpretrieve.set_ndarray_backend() function. To check which backend is
currently in use just run qpretrieve.get_ndarray_backend().
Matching the NDArray Backend with the FFTFilter
Always try to match the NDArray Backend with the FFTFilter, as shown in the example below, otherwise you will run into warnings or errors.
- To summarise:
'numpy'(default) backend works as expected with theFFTFilterNumpyandFFTFilterPyFFTWclasses.'cupy'backend works as expected with theFFTFilterCupyandFFTFilterNumpyclasses. This is because NumPy is quite clever. TheFFTFilterPyFFTWclass will raise an Error if used with the'cupy'backend.
import qpretrieve
print(qpretrieve.get_ndarray_backend())
# <module 'numpy' from '~\\numpy\\__init__.py'>
qpretrieve.set_ndarray_backend('cupy') # swap to the 'cupy' backend
print(qpretrieve.get_ndarray_backend())
# <module 'cupy' from '~\\cupy\\__init__.py'>
Example use of ‘cupy’ backend for Off-Axis Hologram
import numpy as np
import qpretrieve
from qpretrieve.data_array_layout import convert_data_to_3d_array_layout
# load your experimental data (data from the qpretrieve repository)
edata = np.load("qpretrieve/examples/data/hologram_cell.npz")
data_2d, data_2d_bg = edata["data"].copy(), edata["bg_data"].copy()
input_data_3d, _ = convert_data_to_3d_array_layout(data_2d)
input_data_bg_3d, _ = convert_data_to_3d_array_layout(data_2d_bg)
# stack in 3D
data_3d = np.repeat(input_data_3d, repeats=4, axis=0)
data_3d_bg = np.repeat(input_data_bg_3d, repeats=4, axis=0)
# set 'cupy' backend and fft_interface
qpretrieve.set_ndarray_backend("cupy")
fft_interface = qpretrieve.fourier.FFTFilterCupy
# process the 3D stack of holograms
holo = qpretrieve.OffAxisHologram(
data_3d, fft_interface=fft_interface, padding=True)
kwargs = dict(filter_name="disk", filter_size=1 / 3)
holo_field = holo.run_pipeline(**kwargs)
# process the 3D stack of background holograms
bg = qpretrieve.OffAxisHologram(data_3d_bg, fft_interface=fft_interface)
bg.process_like(holo)