slmsuite.holography.algorithms.Hologram
- class Hologram(target, amp=None, phase=None, slm_shape=None, dtype=<class 'numpy.float32'>)[source]
Bases:
object
Phase retrieval methods applied to holography. See
optimize()
to learn about the methods implemented for hologram optimization.Tip
The Fourier domain (
"kxy"
) of an SLM with shapeslm_shape
also has the shapeslm_shape
under discrete Fourier transform. However, the extents of this domain correspond to the edges of the farfield (\(\pm\frac{\lambda}{2\Delta x}\) radians, where \(\Delta x\) is the SLM pixel pitch). This means that resolution of the farfield \(\pm\frac{\lambda}{2N_x\Delta x}\) can be quite poor with small \(N_x\). The solution is to zero-pad the SLM nearfield — artificially increasing the width \(N_x\) and height \(N_y\) even though the extent of the non-zero nearfield data remains the same — and thus enhance the resolution of the farfield. In practice, padding is accomplished by passing ashape
ortarget
of appropriate shape (see constructor__init__()
and subclasses), potentially with the aid of the static helper functioncalculate_padded_shape()
.Note
target
,weights
,phase_ff
, andamp_ff
are all matrices of shapeshape
. To save memory, the matricesphase
andamp
are stored with the (smaller, but not strictly smaller) shapeslm_shape
. Also to save memory,phase_ff
andamp_ff
are set toNone
on construction, and only initialized if they need to be used. Any code additions should check forNone
.Tip
Due to imperfect SLM diffraction efficiency, undiffracted light will be present at the center of the
target
. This is called the zeroth order diffraction peak. To avoid this peak, consider shifting the data contained intarget
away from the center.Tip
For mixed region amplitude freedom (MRAF) capabilities, set the part of the
target
desired as a ‘noise region’ tonan
. Seeoptimize()
for more details.SpotHologram
has spot-specific methods for generated noise region pattern.Caution
By default, arguments passed to the constructor (
phase
,amp
, … ) are stored directly as attributes without copying, where possible. These will be modified in place. However,numpy
arrays passed tocupy
will naturally be copied onto the GPU and arrays of incorrectdtype
will likewise be copied and casted. This lack of copying is desired in many cases, such that external routines are accessing the same data, but the user can pass copied arrays if this behavior is undesired.- slm_shape
The shape of the near-field device producing the hologram in the far-field in
numpy
(h, w)
form. This is important to record because certain optimizations and calibrations depend on it. If multiple ofslm_shape
,phase
, oramp
are notNone
in the constructor, the shapes must agree. If all areNone
, then the shape of thetarget
is used instead (slm_shape
==shape
).- Type
(int, int)
- phase
Near-field phase pattern to optimize. Initialized to with
random.default_rng().uniform()
by default (None
). This is of shapeslm_shape
and (during optimization) padded to shapeshape
.- Type
numpy.ndarray OR cupy.ndarray
- amp
Near-field source amplitude pattern (i.e. image-space constraints). Uniform illumination is assumed by default (
None
). This is of shapeslm_shape
and (during optimization) padded to shapeshape
.- Type
numpy.ndarray OR cupy.ndarray
- shape
The shape of the computational space in the near-field and far-field in
numpy
(h, w)
form. Corresponds to the the"knm"
basis in the far-field. This often differs fromslm_shape
due to padding of the near-field.- Type
(int, int)
- target
Desired far-field amplitude in the
"knm"
basis. The goal of optimization. This is of shapeshape
.- Type
numpy.ndarray OR cupy.ndarray
- weights
The mutable far-field amplitude in the
"knm"
basis used in GS. Starts astarget
but may be modified by weighted feedback in WGS. This is of shapeshape
.- Type
numpy.ndarray OR cupy.ndarray
- phase_ff
Algorithm-constrained far-field phase in the
"knm"
basis. Stored for certain computational algorithms. (seeGS()
). This is of shapeshape
.- Type
numpy.ndarray OR cupy.ndarray
- amp_ff
Far-field amplitude in the
"knm"
basis. Used for comparing this, the computational result, with thetarget
. This is of shapeshape
.- Type
numpy.ndarray OR cupy.ndarray OR None
- dtype
Datatype for stored near- and far-field arrays, which are all real. Some internal variables are complex. The complex numbers follow
numpy
type promotion. Complex datatypes are derived fromdtype
:float32
->complex64
(assumed by default)float64
->complex128
float16
is not recommended fordtype
becausecomplex32
is not implemented bynumpy
.- Type
type
- iter
Tracks the current iteration number.
- Type
int
- method
Remembers the name of the last-used optimization method. The method used for each iteration is stored in
stats
.- Type
str
- flags
Helper flags to store custom persistent variables for optimization. These flags are generally changed by passing as a
kwarg
tooptimize()
. Contains the following keys:"method"
str
Stores the method used for optimization. See
optimize()
.
"fixed_phase"
bool
Fixes the far-field phase as mandated by certain weighted algorithms (see
GS()
).
"feedback"
str
Stores the values passed to
optimize()
.
"stat_groups"
list of str
Stores the values passed to
optimize()
.
"raw_stats"
boolWhether to store raw stats.
"blur_ij"
float
See
ijcam_to_knmslm()
.
Other user-defined flags.
- Type
dict
- stats
Dictionary of useful statistics. data is stored in lists, with indices corresponding to each iteration. Contains:
"methods"
list of str
Method used for each iteration.
"flags"
dict of lists
Each key corresponds to a flag that was used at least once. If it is
np.nan
on a given iteration, then it was undefined at that point (update functions keep track of all this).
"stats"
dict of dicts of lists
Same format as
"flags"
, except with another layer of hierarchy corresponding to the source (group) of the given stats. This is to differentiate standard deviations computed computationally and experimentally.
See
update_stats()
andplot_stats()
.- Type
dict
Methods
GPU-accelerated Gerchberg-Saxton (GS) iterative phase retrieval.
Helper function to calculate the shape of the computational space.
Uses
write_h5()
to export the statistics hierarchy to a given h5 file.Collects the current complex farfield from the GPU with
cupy.ndarray.get()
.Collects the current nearfield phase from the GPU with
cupy.ndarray.get()
.Helper function to get the cupy memory pool size.
Uses
write_h5()
to import the statistics hierarchy from a given h5 file.Optimizers to solve the "phase problem": approximating the near-field phase that transforms a known near-field source amplitude to a desired far-field target amplitude.
Plots an overview (left) and zoom (right) view of
source
.Plots the amplitude (left) and phase (right) of the nearfield (plane of the SLM).
Plots the statistics contained in the given dictionary.
Resets the hologram to an initial state.
Resets the hologram to a random state or to a provided phase.
Resets the hologram weights to the
target
defaults.Helper function to set the cupy memory pool size.
Calculate statistics corresponding to the desired
stat_groups
.Change the target to something new.
- __init__(target, amp=None, phase=None, slm_shape=None, dtype=<class 'numpy.float32'>)[source]
Initialize datastructures for optimization. When
cupy
is enabled, arrays are initialized on the GPU ascupy
arrays: take care to use class methods to access these parameters instead of editing them directly, ascupy
arrays behave slightly differently than numpy arrays in some cases.Parameters additional to class attributes are described below:
- Parameters
target (numpy.ndarray OR cupy.ndarray OR (int, int)) – Target to optimize to. The user can also pass a shape in
numpy
(h, w)
form, and this constructor will create an empty target of all zeros.calculate_padded_shape()
can be of particular help for calculating the shape that will produce desired results (in terms of precision, etc).amp (array_like OR None) – The near-field amplitude. See
amp
. Of shapeslm_shape
.phase (array_like OR None) – The near-field initial phase. See
phase
.phase
should only be passed if the user wants to precondition the optimization. Of shapeslm_shape
.slm_shape ((int, int) OR slmsuite.hardware.FourierSLM OR slmsuite.hardware.slms.SLM OR None) – The shape of the near-field of the SLM in
numpy
(h, w) form. Optionally, as a quality of life feature, the user can pass aslmsuite.hardware.FourierSLM
orslmsuite.hardware.slms.SLM
instead, andslm_shape
(andamp
if it isNone
) are populated from this. IfNone
, tries to use the shape ofamp
orphase
, but if these are not present, defaults toshape
(which is usually determined bytarget
).dtype (type) – See
dtype
; type to use for stored arrays.
- reset(reset_phase=True, reset_flags=False)[source]
Resets the hologram to an initial state. Does not restore the preconditioned
phase
that may have been passed to the constructor (as this information is lost upon optimization). Also uses the currenttarget
rather than thetarget
that may have been passed to the constructor (e.g. includes currentrefine_offset()
changes, etc).- Parameters
reset_phase (bool) – Whether to additionally call
reset_phase()
.reset_flags (bool:) – Whether to erase the information (including passed
kwargs
) stored inflags
.
- static calculate_padded_shape(slm_shape, padding_order=1, square_padding=True, precision=inf, precision_basis='kxy')[source]
Helper function to calculate the shape of the computational space. For a given base
slm_shape
, pads to the user’s requirements. If the user chooses multiple requirements, the largest dimensions for the shape are selected. By default, pads to the smallest square power of two that encapsulates the originalslm_shape
.Tip
See also the first tip in the constructor of
Hologram
for more information about the importance of padding.Note
Under development: a parameter to pad based on available memory (see
_calculate_memory_constrained_shape()
).- Parameters
slm_shape ((int, int) OR slmsuite.hardware.FourierSLM) – The original shape of the SLM in
numpy
(h, w) form. The user can pass aslmsuite.hardware.FourierSLM
instead, and should pass this when using theprecision
parameter.padding_order (int) – Scales to the
padding_order
th larger power of 2. Apadding_order
of zero does nothing. For instance, an SLM with shape(720, 1280)
would yield(720, 1280)
forpadding_order=0
,(1024, 2048)
forpadding_order=1
, and(2048, 4096)
forpadding_order=2
.square_padding (bool) – If
True
, sets the smaller shape dimension to that of the larger, yielding a square.precision (float) – Returns the shape that produces a computational k-space with resolution smaller than
precision
. The default, infinity, requests a padded shape larger than zero, sopadding_order
will dominate.precision_basis (str) – Basis for the precision. Can be
"ij"
(camera) or"kxy"
(normalized blaze).
- Returns
Shape of the computational space which satisfies the above requirements.
- Return type
(int, int)
- optimize(method='GS', maxiter=20, verbose=True, callback=None, feedback=None, stat_groups=[], **kwargs)[source]
Optimizers to solve the “phase problem”: approximating the near-field phase that transforms a known near-field source amplitude to a desired far-field target amplitude. Supported optimization methods include:
Gerchberg-Saxton (GS) phase retrieval.
'GS'
An iterative algorithm for phase retrieval, accomplished by moving back and forth between the imaging and Fourier domains, with amplitude corrections applied to each. This is implemented using fast Fourier transforms, potentially GPU-accelerated.
Weighted Gerchberg-Saxton (WGS) phase retrieval algorithms of various flavors. Improves the uniformity of GS-computed focus arrays using weighting methods and techniques from literature. The
method
keywords are:'WGS-Leonardo'
The original WGS algorithm. Weights the target amplitudes by the ratio of mean amplitude to computed amplitude, which amplifies weak spots while attenuating strong spots. Uses the following weighting function:
\[\mathcal{W} = \mathcal{W}\left(\frac{\mathcal{T}}{\mathcal{F}}\right)^p\]where \(\mathcal{W}\), \(\mathcal{T}\), and \(\mathcal{F}\) are the weight amplitudes, target (goal) amplitudes, and feedback (measured) amplitudes, and \(p\) is the power passed as
"feedback_exponent"
inflags
(seekwargs
). The power \(p\) defaults to .9 if not passed. In general, smaller \(p\) will lead to slower yet more stable optimization.'WGS-Kim'
Improves the convergence of
WGS-Leonardo
by fixing the far-field phase strictly after a desired number of net iterations specified by"fix_phase_iteration"
or after exceeding a desired efficiency (fraction of far-field energy at the desired points) specified by"fix_phase_efficiency"
'WGS-Nogrette'
Weights target intensities by a tunable gain factor.
\[\mathcal{W} = \mathcal{W}/\left(1 - f\left(1 - \mathcal{F}/\mathcal{T}\right)\right)\]where \(f\) is the gain factor passed as
"feedback_factor"
inflags
(seekwargs
). The factor \(f\) defaults to .1 if not passed.Note that while Nogrette et al compares powers, this implementation compares amplitudes for speed. These are identical to first order.
The option for Mixed Region Amplitude Freedom (MRAF) feedback. In standard iterative algorithms, the entire Fourier-domain unpatterned field is replaced with zeros. This is disadvantageous because a desired farfield pattern might not be especially compatible with a given nearfield amplitude, or otherwise. MRAF enables “noise regions” where some fraction of the given farfield is not replaced with zeros and instead is allowed to vary. In practice, MRAF is enabled by setting parts of the
target
tonan
; these regions act as the noise regions. The"mraf_factor"
flag inflags
allows the user to partially attenuate the noise regions. A factor of 0 fully attenuates the noise region (normal WGS behavior). A factor of 1 does not attenuate the noise region at all (the default). Middle ground is recommended, but is application-dependent as a tradeoff between improving pattern fidelity and maintaining pattern efficiency.As examples, consider two cases where MRAF can be useful:
Sloping a top hat. Suppose we want very flat amplitude on a beam. Requesting a sharp edge to this beam can lead to fringing effects at the boundary which mitigate flatness both inside and outside the beam. If instead a noise region is defined in a band surrounding the beam, the noise region will be filled with whatever slope best enables the desired flat beam.
Mitigating diffractive orders. Without MRAF, spot patterns with high crystallinity often have “ghost” diffractive orders which continue the pattern past the edges of requested spots. Even though these orders are attenuated during each phase retrieval iteration, they remain part of the best solution for the recovered phase. With MRAF, a noise region can help solve for retrieved phase which does not generate these undesired orders.
Caution
Requesting
stat_groups
will slow the speed of optimization due to the overhead of processing and saving statistics, especially in the case of GPU-accelerated optimization where significant time cost is incurred by moving these statistics to the CPU. This is especially apparent in the case of fully-computational holography, where this effect can slow what is otherwise a fully-GPU-contained loop by an order magnitude.Tip
This function uses a parameter naming convention borrowed from
scipy.optimize.minimize()
and other functions inscipy.optimize
. The parametersmethod
,maxiter
, andcallback
have the same functionality as the equivalently-named parameters inscipy.optimize.minimize()
.- Parameters
method (str) – Optimization method to use. See the list of optimization methods above.
maxiter (int) – Number of iterations to optimize before terminating.
verbose (bool OR int) – Whether to display
tqdm
progress bars. These bars are also not displayed formaxiter <= 1
. Ifverbose
is greater than 1, then flags are printed as a preamble.callback (callable OR None) – Same functionality as the equivalently-named parameter in
scipy.optimize.minimize()
.callback
must accept a Hologram or Hologram subclass as the single argument. Ifcallback
returnsTrue
, then the optimization exits. Ignored ifNone
.feedback (str OR None) –
Type of feedback to use during optimization, for instance when weighting in
"WGS"
. For direct instances ofHologram
, this can only be"computational"
feedback. Subclasses support more types of feedback. Supported feedback options include the following:"computational"
Uses the the projected farfield pattern (transform of the complex nearfield) as feedback."experimental"
Uses a camera contained in a passedcameraslm
as feedback. Specific to subclasses ofFeedbackHologram
."computational_spot"
Takes the computational result (the projected farfield pattern) and integrates regions around the expected positions of spots in an optical focus array. More stable than"computational"
for spots. Specific to subclasses ofSpotHologram
."experimental_spot"
Takes the experimental result (the image from a camera) and integrates regions around the expected positions of spots in an optical focus array. More stable than"experimental"
for spots. Specific to subclasses ofSpotHologram
."external_spot"
Uses some external user-provided metric for spot feedback. Seeexternal_spot_amp
. Specific to subclasses ofSpotHologram
.
stat_groups (list of str OR None) – Strings representing types of feedback (data gathering) upon which statistics should be derived. These strings correspond to valid types of feedback (see above). For instance, if
"experimental"
is passed as a stat group, statistics on the pixels in the experimental feedback image will automatically be computed and stored for each iteration of optimization. However, this comes with overhead (see above warning).**kwargs (dict, optional) – Various weight keywords and values to pass depending on the weight method. These are passed into
flags
. See options documented in the constructor.
- GS(iterations, callback)[source]
GPU-accelerated Gerchberg-Saxton (GS) iterative phase retrieval.
Solves the “phase problem”: approximates the near-field phase that transforms a known near-field source amplitude to a desired far-field target amplitude.
Caution
This function should be called through
optimize()
and not called directly. It is left as a public function exposed in documentation to clarify how the internals ofoptimize()
work.Note
FFTs are not in-place in this algorithm. In both non-
cupy
andcupy
implementations,numpy.fft
does not support in-place operations. However,scipy.fft
does in both. In the future, we may move to the scipy implementation. However, neithernumpy
orscipy
fftshift
support in-place movement (for obvious reasons). For even faster computation, algorithms should consider not shifting the FFT result, and instead shifting measurement data / etc to this unshifted basis. We might also implement get_fft_plan for even faster FFTing.- Parameters
iterations (iterable) – Number of loop iterations to run. Is an iterable to pass a
tqdm
iterable.callback (callable OR None) – See
optimize()
.
- update_target(new_target, reset_weights=False, plot=False)[source]
Change the target to something new. This method handles cleaning and normalization.
- Parameters
- extract_phase()[source]
Collects the current nearfield phase from the GPU with
cupy.ndarray.get()
. Also shifts the \([-\pi, \pi]\) range ofnumpy.arctan2()
to \([0, 2\pi]\) for faster writing to the SLM (seewrite()
).- Returns
Current nearfield phase computed by GS.
- Return type
numpy.ndarray
- extract_farfield()[source]
Collects the current complex farfield from the GPU with
cupy.ndarray.get()
.- Returns
Current farfield computed by GS.
- Return type
numpy.ndarray
- update_stats(stat_groups=[])[source]
Calculate statistics corresponding to the desired
stat_groups
.- Parameters
stat_groups (list of str) – Which groups or types of statistics to analyze.
- export_stats(file_path, include_state=True)[source]
Uses
write_h5()
to export the statistics hierarchy to a given h5 file.- Parameters
file_path (str) – Full path to the file to read the data from.
include_state (bool) – If
True
, also includes all other attributes ofHologram
except fordtype
(cannot pickle) andamp_ff
(can regenerate). These attributes are converted tonumpy
if necessary. Note that the intent is not to produce a runnableHologram
by default (as this would require pickling hardware interfaces), but rather to provide extra information for debugging.
- import_stats(file_path, include_state=True)[source]
Uses
write_h5()
to import the statistics hierarchy from a given h5 file.Tip
Enabling the
"raw_stats"
flag will export feedback data from each iteration instead of only derived statistics. Consider enabling this to save more detailed information upon export.
- plot_nearfield(title='', padded=False, figsize=(8, 4), cbar=False)[source]
Plots the amplitude (left) and phase (right) of the nearfield (plane of the SLM). The amplitude is assumed (whether uniform, or experimentally computed) while the phase is the result of optimization.
- Parameters
title (str) – Title of the plots.
padded (bool) – If
True
, shows the full computational nearfield of shapeshape
. Otherwise, shows the region at the center of the computational space of sizeslm_shape
corresponding to the unpadded SLM.figsize (tuple) – Size of the plot.
cbar (bool) – Whether to add colorbars to the plots. Defaults to
False
.
- plot_farfield(source=None, title='', limits=None, units='knm', limit_padding=0.1, figsize=(8, 4), cbar=False)[source]
Plots an overview (left) and zoom (right) view of
source
.- Parameters
source (array_like OR None) – Should have shape equal to
shape
. IfNone
, defaults toamp_ff
.title (str) – Title of the plots. If
"phase"
is a substring of title, then the source is treated as a phase.limits (((float, float), (float, float)) OR None) – \(x\) and \(y\) limits for the zoom plot in
"knm"
space. If None,limits
are autocomputed as the smallest bounds that show all non-zero values (pluslimit_padding
). Note that autocomputing ontarget
will perform well, as zero values are set to actually be zero. However, doing so on computational or experimental outputs (e.g.amp_ff
) will likely perform poorly, as values in the field deviate slightly from zero and artificially expand thelimits
.units (str) – Far-field units for plots (see
convert_blaze_vector()
for options). If units requiring a SLM are desired, the attributecameraslm
must be filled.limit_padding (float) – Fraction of the width and height to expand the limits of the zoom plot by, only if the passed
limits
isNone
(autocompute).figsize (tuple) – Size of the plot.
cbar (bool) – Whether to add colorbars to the plots. Defaults to
False
.
- Returns
Used
limits
, which may be autocomputed. Autocomputed limits are returned as integers.- Return type
((float, float), (float, float))
- plot_stats(stats_dict=None, stat_groups=[], ylim=None)[source]
Plots the statistics contained in the given dictionary.
- Parameters
stats_dict (dict OR None) – Stats to plot in dictionary form. If
None
, defaults tostats
.stat_groups (list of str OR None) – Which
ylim ((int, int) OR None) – Allows the user to pass in desired y limits. If
None
, the default y limits are used.
- static set_mempool_limit(device=0, size=None, fraction=None)[source]
Helper function to set the cupy memory pool size.
- Parameters
device (int) – Which GPU to set the limit on. Passed to
cupy.cuda.Device()
.size (int) – Desired number of bytes in the pool. Passed to
cupy.cuda.MemoryPool.set_limit()
.fraction (float) – Fraction of available memory to use. Passed to
cupy.cuda.MemoryPool.set_limit()
.
- static get_mempool_limit(device=0)[source]
Helper function to get the cupy memory pool size.
- Parameters
device (int) – Which GPU to set the limit on. Passed to
cupy.cuda.Device()
.- Returns
Current memory pool limit in bytes
- Return type
int