Method-Specific Algorithms

All of the algorithms below are Classical Algorithms. As such they can be augmented via the pulse-spectrum as an additional constraint as well as momentum to accelerated convergence. More details can be found in Overview Algorithms.

FROG

Vanilla [2]

The first published FROG algorithms. It is similar to the Generalized Projection Algorithm. However instaed of an iterative optimization of the Z-error, a simple integration along \(\tau\) is used to generate an updated guess.

\(E'(t) = \int S'(\tau, t)\, d\tau\)

Below is a minimal working example applied to SHG-FROG. There are no tunable parameters.

from pulsedjax.frog import Vanilla

vanilla = Vanilla(delay, frequency, trace, "shg")

population = vanilla.create_initial_population(5, "random")

final_result = vanilla.run(population, 50)

FROG, TDP and VAMPIRE

LSGPA [27]

The Least-Squares Generalized-Projection Algorithm is quite similar to the Vanilla- and Generalized Projection Algorithms. However it works by applying a linear least-squares approach to the Z-error in order to generate a new update. This works since the signal field \(S(\tau, t) = E(t)\cdot G(\tau, t)\) depends on the pulse only in a linear fashion. (If implicit dependencies are ignored.) Thus an update formula for the pulse can be derived:

\(E'(t) = \dfrac{\int S'(\tau, t)\cdot G^*(\tau, t)\, d\tau}{\int |G(\tau, t)|^2\, d\tau}\)

Due to its approach/assumptions the algorithm or more specifically its math is related to that of the Ptychographic Iterative Engine.
Below is a minimal working example applied to SHG-FROG. There are no tunable parameters.

from pulsedjax.frog import LSGPA

lsgpa = LSGPA(delay, frequency, trace, "shg")

population = lsgpa.create_initial_population(5, "random")

final_result = lsgpa.run(population, 50)

C-PCGPA [28, 29]

The Constraint-Principal-Component-Generalized-Projection-Algorithm (C-PCGPA) exploits the fact that a FROG trace can be obtained through an outer product of the pulse and gate. On one hand, this yields an efficient way to calculate a trace. On the other hand this implies that the reverse is also true. Namely that a nonlinear signal field \(S(\tau, t)\) can be transformed into a rank-one matrix consisting of a pulse and gate pair.
The algorithm applies this to construct an update to the pulse. In a first step a nonlinear signal is calculated and projected onto the measured trace, such that \(S'(\tau, t)\) is obtained. Subsequently, \(S'\) is transformed and factorized using the Singular-Value-Decomposition (SVD), where the column and row-vector of the first singular value correspond to the updated pulse (and gate). For the ideal solution all other singular values are zero. This factorization into the principle components provides the algorithm with its name.
Since a full SVD is numerically expensive, the power-method can be used to approximate the principal components and iteratively refine them.
Based on the power method the methodology of algorithm can be reformulated into an operator-formalism. In this reformulation the outer-product form of pulse and gate can be viewed as an operator which acts onto the pulse or gate and update them. This allows for the incorporation of additional constraints.
However, despite its elegancy, the algorithm does not seem to work for SD-FROG. It is unclear why this is.

Below is a minimal working example for SHG-FROG. The main parameters are whether the additional constraints should be applied and whether a full SVD should be used instead of single iterations of the power method.

from pulsedjax.frog import CPCGPA

pcgpa = CPCGPA(delay, frequency, trace, "shg", constraints=False, svd=False)

population = pcgpa.create_initial_population(5, "random")

final_result = pcgpa.run(population, 50)

Chirp-Scan

Basic [30]

Similarily to the Vanilla algorithm fro FROG, this algorithm produces an updated signal field \(S'(\theta, t)\) via projection. Subsequently a heuristic procedure is used to produce an update. In short, the complex conjugate of the gate is multiplied onto the nonlinear signal. Thus approximately converting any contributions from the gate into an intensity. Afterwards a cube or quintic root is applied which technically converts the nonlinear signal field into a linear signal field. Finally the inverse of the chirp is applied and the resulting linear signal is averaged along \(\theta\).

Below is a minimal example for an SHG-Chirp-Scan. There are no tunable parameters.

from pulsedjax.chirp_scan import Basic

basic = Basic(z_arr, frequency, trace, "shg", phase_type, chirp_parameters)

population = basic.create_initial_population(5, "random")

final_result = basic.run(population, 50)

2D-SI

Direct-Reconstruction [8]

For regular SFG 2D-SI this algorithm should be used. It assumes that the ancillae pulses in the interferometer are so narrowband that a CW-approximation applies. The two pulses must have entirely separate frequencies.
In such a case the nonlinear signal can be written as:

\(S(\tau, t) = (\cos(\omega_1 t) + \cos(\omega_2 (t+\tau)))\cdot E(t)\)

This results in a trace which directly contains the approximate group delay.

\(T(\tau, \omega) \propto \cos(\omega_1\tau + \phi(\omega) - \phi(\omega - \Omega)) \approx \cos(\omega_1\tau + \phi'(\omega)\cdot\Omega)\)

where \(\Omega=\omega_2-\omega_1\). Thus the algorithm needs to extract the phase of each frequency component, divide by \(\Omega\) and integrate along \(\omega\) to obtain the spectral phase.

Below is an example of an SFG 2D-SI reconstruction. Algorithm parameters include the integration type and the FFT-windowing type.

from pulsedjax.twodsi import DirectReconstruction

dr = DirectReconstruction(delay, frequency, trace, "shg", spectral_filter1, spectral_filter2)

# requires a spectrum
dr.use_measured_spectrum(frequency_spectrum, pulse_spectrum, "pulse")

dr.integration_method = "euler_maclaurin_5"     # alternatively "cumsum"
dr.windowing = False

# is deterministic and non-iterative
population = dr.create_initial_population(1, "random")
final_result = dr.run(population, 1)