Optimization & automatic differentiation
Sound-speed maps, aberration laws and QUS parameter fits are now computed by iterating on a cost: beamform with the current guess, score the image or the residual, differentiate, step. This module rebuilds the optimizer's side of that loop (step size, conditioning, Newton-type methods, Adam), the reason imaging costs trap optimizers (cycle skipping), and the derivative machinery (dual numbers, reverse mode, complex step, Wirtinger calculus) that differentiable beamforming runs on.
You will be able to
- Predict the iteration count of gradient descent and momentum from \(\kappa\), and fix slow convergence by scaling rather than by tuning
- Choose between gradient descent, Gauss–Newton, Levenberg–Marquardt and Adam for a given fit, and set up a line search
- Quantify cycle skipping (basin \(\pm T/2\), minima every period) and design envelope, low-frequency or multiscale costs that widen the basin
- Build an autofocus sound-speed estimator: a time-tied ROI, a metric that is actually sensitive to focus, an exact gradient, and a safe ascent
- Compute exact derivatives with dual numbers, reverse mode or the complex step, and take gradients of real losses of complex IQ data correctly
Assumes
- Module 05 · Matrices as operators: conditioning \(\kappa\), least squares, QR instead of normal equations
- Module 07 · Random vectors, covariance & correlation: coherence vs assumed sound speed; Module 11 · Correlation & time-delay estimation: false correlation peaks at multiples of the RF period
- Module 12 · Estimation theory & the Cramér–Rao bound: CRLB, Fisher information; Module 13 · Inverse problems and regularization: Tikhonov, ISTA, proximal steps
- Plane-wave DAS on IQ data: delay, interpolate, rotate by \(e^{j\omega_0\tau}\), sum
m14_autofocus_sound_speed.m.14.1Gradient descent, the step size and the condition number
Every smooth cost looks quadratic near its minimum, so the quadratic tells you how every gradient method behaves in the end game. Take
- \(\mathbf{A}\)
- Hessian (symmetric positive definite)
- \(\mu,\ L\)
- smallest and largest Hessian eigenvalues: strong-convexity constant and gradient Lipschitz constant
- \(\kappa\)
- condition number of the Hessian, dimensionless
- \(f(\mathbf{x})\)
- quadratic objective
- \(\mathbf{x}\)
- parameter vector
- \(\mathbf{b}\)
- linear term; the minimizer solves \(\mathbf{A}\mathbf{x} = \mathbf{b}\)
- \(\nabla f\)
- gradient of \(f\)
- \(\lambda_{\min},\ \lambda_{\max}\)
- smallest and largest eigenvalues of \(\mathbf{A}\)
- \(^{T}\)
- matrix transpose
Gradient descent \(\mathbf{x}_{k+1} = \mathbf{x}_k - \alpha\nabla f(\mathbf{x}_k)\) acts on the error \(\mathbf{e} = \mathbf{x} - \mathbf{x}^*\) one eigen-direction at a time:
- \(e_{k,i}\)
- error of eigen-mode i after k steps
- \(\alpha\)
- fixed step size, units of 1/curvature
- \(\lambda_i\)
- eigenvalue (curvature) of mode i, between \(\mu\) and \(L\)
- \(\mu,\ L\)
- as in (14.1)
- \(\rho(\alpha)\)
- worst-case contraction factor per iteration
- \(\lvert\cdot\rvert\)
- absolute value
- \(\alpha^*\)
- optimal fixed step, in units of 1/curvature
- \(\rho^*\)
- error contraction factor per iteration
- \(\varepsilon\)
- target error reduction, e.g. \(10^{-6}\)
- \(k(\varepsilon)\)
- iterations to reduce the error by \(\varepsilon\)
- \(\kappa,\ \mu,\ L\)
- as in (14.1)
- \(\ln\)
- natural logarithm
Momentum adds a fraction of the previous step. Both classical forms reach a rate in \(\sqrt{\kappa}\) instead of \(\kappa\):
- \(\mathbf{x}_k\)
- iterate after k steps
- \(\nabla f\)
- gradient, as in (14.1)
- \(\alpha\)
- step size, units of 1/curvature
- \(\beta\)
- momentum coefficient, dimensionless
- \(\mu,\ L,\ \kappa\)
- as in (14.1)
- \(\rho\)
- asymptotic contraction factor per iteration
- \(\beta\)
- momentum coefficient, dimensionless, \(0 \le \beta < 1\)
- \(\mathbf{y}\)
- look-ahead point where Nesterov evaluates the gradient
- \(\mathbf{x}_k\)
- iterate after k steps
- \(\nabla f\)
- gradient, as in (14.1)
- \(\alpha\)
- step size, \(1/L\)
- \(L,\ \kappa\)
- as in (14.1)
- \(\rho\)
- asymptotic contraction factor per iteration
Gradient descent and momentum on a quadratic
SimulationIterations of each method run in JS on a 2-D quadratic; the pink reference curve is the exact spectral radius of the 2 × 2 recursions.A joint fit of sound speed (≈ 1540 m/s, sensitivity per m/s) and an amplitude or attenuation term (order 1) mixes column norms that differ by \(10^3\), so \(\kappa(\mathbf{J}^{T}\mathbf{J}) \approx 10^6\): the MATLAB lab gets \(1.22 \times 10^6\), which would cost gradient descent about \(8 \times 10^6\) iterations. Dividing each column by its norm (Jacobi scaling, a diagonal change of variables) drops it to 1.17. The Gaussian-pulse fit of m14_gauss_newton_lm.m has \(\kappa = 1.8 \times 10^3\) in (1, µs, µs, MHz) units, \(2 \times 10^{22}\) in SI units, and 3.7 after scaling. Units are a preconditioner whether you choose them or not.
\(\lVert\mathbf{x} - \mathbf{x}^*\rVert \le \lVert\nabla f(\mathbf{x})\rVert/\mu\). With \(\kappa = 10^4\), stopping at \(\lVert\nabla f\rVert = 10^{-6} L\) can leave an error of \(10^{-2}\) in the flat direction. Stop on the relative change of the parameters you care about (m/s, dB/cm/MHz) and report it in those units.
14.2Line search, Newton, Gauss–Newton and Levenberg–Marquardt
Without \(L\) you cannot pick \(\alpha\) in advance. A backtracking line search finds a step that makes enough progress along a descent direction \(\mathbf{d}\) (\(\nabla f^{T}\mathbf{d} < 0\)):
- \(f\)
- objective function, as in (14.1)
- \(\mathbf{x}\)
- current iterate
- \(\mathbf{d}\)
- descent direction (\(\nabla f^{T}\mathbf{d} < 0\))
- \(\alpha\)
- trial step length along \(\mathbf{d}\); halved until accepted
- \(c_1\)
- sufficient-decrease fraction, dimensionless
- \(\nabla f\)
- gradient at \(\mathbf{x}\)
- \(\leftarrow\)
- assignment: replace \(\alpha\) by \(\alpha/2\)
Newton's method uses the curvature itself, which makes it affine invariant: scaling the parameters changes nothing, and \(\kappa\) disappears from the rate.
- \(\mathbf{d}\)
- Newton step
- \(\mathbf{H}\)
- Hessian matrix \(\nabla^2 f\) at the current iterate
- \(\nabla f\)
- gradient, as in (14.1)
- \(\mathbf{e}_k\)
- error \(\mathbf{x}_k - \mathbf{x}^*\) after k steps
- \(\mathbf{x}^*\)
- minimizer
- \(C\)
- constant set by the third derivative and the Hessian
- \(\lVert\cdot\rVert\)
- Euclidean norm
Most estimation problems in ultrasound are nonlinear least squares: echo-model fits, backscatter and attenuation spectra (Module 27), delay and phase-screen fits. Write the residual \(\mathbf{r}(\mathbf{\theta})\) = model − data and \(f = \tfrac12\lVert\mathbf{r}\rVert^2\):
- \(\mathbf{r}\)
- residual vector, model minus data, in data units
- \(\mathbf{J}\)
- Jacobian \(\partial r_i/\partial\theta_j\)
- \(\mathbf{\theta}\)
- parameter vector
- \(f\)
- least-squares objective, \(\tfrac12\lVert\mathbf{r}\rVert^2\)
- \(r_i\)
- element i of \(\mathbf{r}\)
- \(\nabla^{2}\)
- Hessian (matrix of second derivatives) with respect to \(\mathbf{\theta}\)
- \(^{T}\)
- matrix transpose
- \(\mathbf{\delta}\)
- parameter update
- \(\lambda\)
- Levenberg–Marquardt damping, dimensionless
- \(\sigma^2\)
- noise variance per data sample
- \(\mathrm{cov}(\hat{\mathbf{\theta}})\)
- linearized covariance of the estimate at convergence
- \(\mathbf{J},\ \mathbf{r},\ \mathbf{\theta}\)
- as in (14.8)
- \(\hat{\mathbf{\theta}}\)
- estimate at convergence
- \(\mathrm{diag}(\cdot)\)
- matrix keeping only the diagonal
- \(\mathrm{cov}\)
- covariance matrix
- \(^{T},\ ^{-1}\)
- matrix transpose, matrix inverse
The MATLAB lab fits \(A\,e^{-(t-t_0)^2/2\sigma^2}\cos\!\left(2\pi f(t - t_0)\right)\) to a noisy 4.5 MHz echo (peak SNR 26 dB). From a start 40 ns and 0.3 MHz off, Gauss–Newton converges in 13 iterations and LM in 12; gradient descent with Armijo needs 10 828 in raw units and 51 after Jacobi scaling. The linearized standard deviation of \(t_0\) at the solution is 0.69 ns. From a start 100 ns off (more than a quarter period, 56 ns) scaled gradient descent, Gauss–Newton and LM all land at \(t_0\) = 2.106 µs with \(A < 0\): half a period late with the polarity flipped, a local minimum whose residual is 2.6 times the noise level. Small gradient steps from the same start happen to follow the valley back to the right answer: basins of attraction belong to the algorithm, not only to the cost.
14.3Curved valleys, stochastic gradients and Adam
The Rosenbrock function \(f = (1 - x)^2 + 100(y - x^2)^2\) is the standard caricature of a fit with strongly coupled parameters (delay and frequency, sound speed and depth). Its minimum at (1, 1) has a Hessian \(\begin{bmatrix} 802 & -400 \\ -400 & 200 \end{bmatrix}\) with \(\kappa \approx 2508\), and the valley floor curves, so no fixed linear rescaling straightens it. It is a sum of squares with \(\mathbf{r} = \left(10(y - x^2),\ 1 - x\right)\), so Gauss–Newton applies.
When the cost is an average over many samples (pixels, frames, training images), the gradient of a random subset is an unbiased but noisy estimate. With a constant step the iterates do not converge; they settle into a noise ball. For one mode of curvature \(\lambda\) and gradient-noise variance \(\sigma^2\):
- \(n_k\)
- gradient noise, zero mean, variance \(\sigma^2\)
- \(\lambda\)
- curvature of the mode
- \(e_k\)
- error of one mode after k steps
- \(\alpha\)
- step size, units of 1/curvature
- \(\sigma^{2}\)
- variance of \(n_k\)
- \(\mathrm{E}[\cdot]\)
- expected value
- \(e_\infty\)
- error after the iteration has reached steady state
Adam (Kingma & Ba, 2015) keeps exponential averages of the gradient and of its square, per parameter:
- \(g_k\)
- gradient (or stochastic gradient) at step \(k\)
- \(\hat m_k,\ \hat v_k\)
- bias-corrected averages \(m_k/(1 - \beta_1^k)\), \(v_k/(1 - \beta_2^k)\)
- \(\eta\)
- learning rate, in parameter units per step
- \(\beta_1, \beta_2, \epsilon\)
- defaults 0.9, 0.999, \(10^{-8}\)
- \(m_k,\ v_k\)
- running averages of the gradient and of its square
- \(x_k\)
- parameter after k steps
- \(g_k^{2}\)
- element-wise square of the gradient
- \(\sqrt{\cdot}\)
- element-wise square root
Six optimizers in the Rosenbrock valley
SimulationGradient descent with Armijo, heavy ball, Adam, Newton, Gauss–Newton and LM iterated in JS on the exact Rosenbrock function.A QUS fit with 2–6 smooth parameters (attenuation slope, BSC intercept, effective scatterer size) is a job for Gauss–Newton or LM with scaled parameters: a few iterations, and the error bars come free from \((\mathbf{J}^{T}\mathbf{J})^{-1}\). A differentiable beamformer with \(10^3\)–\(10^5\) sound-speed or phase parameters, a reverse-mode gradient and a cost averaged over patches is where Adam and its relatives are used: the Hessian is too large to form and the per-parameter normalization handles mixed sensitivities. Neural-network beamformers are the same machinery with millions of weights.
14.4Convexity, and why oscillatory data skip cycles
A function is convex if \(f(\theta\mathbf{a} + (1-\theta)\mathbf{b}) \le \theta f(\mathbf{a}) + (1-\theta)f(\mathbf{b})\) for \(0 \le \theta \le 1\); then every local minimum is global and any descent method with a sane step finds it. Least squares with a linear model, Tikhonov, L1 and TV regularizers (Module 13) are convex. As soon as the unknown sits inside the phase of an oscillating signal (a delay, a sound speed, an aberration law), the cost is not.
Fit a delay \(\tau\) to an echo \(d(t) = s(t)\) with the model \(s(t - \tau)\). Expanding the square:
- \(E\)
- pulse energy \(\int s^2\,dt\)
- \(R_{ss}(\tau)\)
- autocorrelation of the pulse at lag \(\tau\)
- \(\mathcal{L}(\tau)\)
- loss as a function of the delay error
- \(d(t),\ s(t)\)
- measured echo and reference pulse, arbitrary units
- \(t\)
- time, µs
- \(\tau\)
- trial delay, ns
- \(\int\)
- integral over the whole record
- \(\sigma\)
- envelope standard deviation, µs (0.125 µs here)
- \(f_0\)
- centre frequency, MHz
- \(\tau\)
- delay error, ns
- \(T_0 = 1/f_0\)
- carrier period, 200 ns at 5 MHz
- \(s(t)\)
- Gaussian-windowed cosine pulse
- \(t\)
- time, µs
- \(R_{ss}(0)\)
- pulse energy \(E\), as in (14.12)
- \(R_{ss}(\tau)\)
- pulse autocorrelation, as in (14.12)
- \(e\)
- exponential function
Three ways to widen the basin, each with a price:
- \(\mathcal{L}_{\mathrm{env}}(\tau)\)
- loss computed on the envelope instead of the RF
- \(\tau\)
- delay error, ns
- \(\sigma\)
- envelope standard deviation, as in (14.13)
- \(\propto\)
- proportional to (scale set by the signal energy)
- \(f_l\)
- Gaussian low-pass corner (standard deviation), MHz
- \(\sigma_f\)
- standard deviation of the pulse amplitude spectrum, MHz
- \(f_c\)
- centre frequency of the filtered (still Gaussian) pulse, MHz
- \(f\)
- frequency, MHz
- \(f_0\)
- centre frequency, as in (14.13), MHz
- \(\sigma\)
- envelope standard deviation, µs, as in (14.13)
- \(\hat\tau_1,\ \hat\tau\)
- coarse (envelope) and final (RF) delay estimates, ns
- \(\mathcal{L}_{\mathrm{env}}\)
- envelope loss, as in (14.14)
- \(\mathcal{L}_{\mathrm{RF}}\)
- RF loss, as in (14.12)
- \(\tau\)
- trial delay, ns
- \(\arg\min\)
- the \(\tau\) that minimizes the loss
- \(\longrightarrow\)
- result of the first stage used as the starting point of the second
Cycle skipping in a delay fit
Exact formulaClosed-form misfits of a Gaussian-modulated pulse (checked against sampled signals in the MATLAB script); the descent path is iterated on them.m14_cycle_skipping.m), gradient descent with step \(1/\mathcal{L}''(0)\). Try: RF from 150 ns lands on the 194 ns minimum; the staircase below shows every start at once. Lower \(f_0\) to 2.5 MHz: the steps widen to 400 ns. Choose envelope, then RF: one flat step through 0. Lower \(f_l\) and watch the readout: the basin grows while the surviving energy collapses.In transmission tomography and full-waveform inversion the data are traces across a path of length \(L\). A starting model that predicts the travel time to within half a period, \(\lvert\Delta t\rvert = L\lvert\Delta c\rvert/c^2 < 1/(2f)\), stays in the basin: \(\lvert\Delta c\rvert/c < \lambda/(2L)\). Breast-scale USCT (\(L\) = 200 mm, \(c\) = 1500 m/s) at 0.5 MHz tolerates 11 m/s, at 1 MHz 5.6 m/s. That is why ultrasound FWI starts from a travel-time (bent-ray) tomogram and from the lowest usable frequency. Pulse-echo autofocus has its own version, in Section 14.5.
If the model amplitude may change sign, \(A\cos(2\pi f(t - t_0))\) and \(-A\cos(2\pi f(t - t_0 - T/2))\) are the same curve: false minima appear every \(T/2\), and the basin shrinks to \(\pm T/4\). The MATLAB echo fit shows it: a 100 ns start (\(T/4\) = 56 ns) converges to \(t_0\) + 106 ns with \(A < 0\). Constrain the sign, or give the model a free carrier phase so that \(t_0\) is set by the envelope alone (the free-phase fit recovers \(t_0\) = 1.994 µs from the same start).
14.5Autofocus: sound speed by gradient ascent on a focus metric
Module 07 showed that coherence peaks at the true sound speed. Turning that into an estimator means choosing what to maximize, over which pixels, and how to climb. The beamformer is a differentiable function of the assumed speed \(\hat c\). For plane-wave angle \(\theta\), element \(e\) and a pixel placed in time coordinates \((x, t)\), with depth \(\hat z = \hat c t/2\):
- \(\hat c\)
- assumed sound speed, m/s
- \(x,\ t\)
- pixel lateral position (m) and two-way time (s)
- \(x_e\)
- lateral position of receive element \(e\), m
- \(\theta\)
- plane-wave steering angle
- \(\mathrm{IQ}_{\theta e}\)
- baseband channel signal of transmit \(\theta\), element \(e\)
- \(\omega_0\)
- demodulation angular frequency \(2\pi f_0\), rad/s
- \(b\)
- beamformed complex pixel value
- \(\tau_{\theta e}\)
- two-way delay for transmit angle \(\theta\), receive element e, s
- \(\sum_\theta\sum_e\)
- sum over transmit angles and receive elements
- \(j\)
- imaginary unit
- \(b,\ \hat c,\ \mathrm{IQ}_{\theta e},\ \tau,\ \omega_0,\ x,\ x_e,\ t,\ \theta\)
- as in (14.17)
- \(\mathrm{IQ}'_{\theta e}\)
- derivative of the baseband signal with respect to its time argument, 1/s
- \(\partial\tau/\partial\hat c\)
- sensitivity of the delay to the assumed sound speed, s per m/s
- \(\frac{db}{d\hat c}\)
- sensitivity of the beamformed pixel to \(\hat c\)
- \(j\omega_0\mathrm{IQ}\)
- derivative of the phase rotation \(e^{j\omega_0\tau}\); dominates over \(\mathrm{IQ}^{\prime}\) for a narrowband signal
m14_autofocus_sound_speed.m check it against central differences (ratio 1.000000).
Candidate metrics, all functions of \(\hat c\):
| Metric | Definition | Behaviour (c = 1480 m/s, z = 30 mm, 5 MHz, 64 elements) |
|---|---|---|
| Compounded energy | \(\sum_{\mathrm{ROI}} \lvert b\rvert^2\), ROI fixed in two-way time, 5 angles (−8° to 8°) | one peak over 1350–1750 m/s, at 1482–1488 m/s over scenes and depths; every start converges there |
| Energy, one angle | same, 0° only | nearly flat, peaks at 1540–1570 m/s: useless (see below) |
| Sharpness | \(P \sum I^2 / \left(\sum I\right)^2\), \(I = \lvert b\rvert^2\), \(P\) pixels | within about 20 m/s with a point target (mostly low, worst at 40 mm); erratic on pure speckle (peaks 1590–1660 m/s) |
| Envelope at fixed depth | \(\lvert b(x{=}0, z)\rvert^2\), pixel tied to depth | right peak, basin roughly ±20 m/s; only where a bright scatterer sits |
| RF at fixed depth | \(\mathrm{Re}\, b(x{=}0, z)\) | oscillates with period \(\hat c^2/(2zf_0)\) = 7.3 m/s: cycle skips from every start |
- \(z\)
- pixel depth, m
- \(f_0\)
- centre frequency, Hz
- \(\Delta\hat c\)
- sound-speed change that slides the echo by one period, m/s: 7.9 m/s at 1540 m/s, 30 mm, 5 MHz
- \(t(\hat c)\)
- two-way time of the echo from depth z, s
- \(\Delta t\)
- echo time shift caused by \(\Delta\hat c\), s
- \(\hat c\)
- assumed sound speed, m/s
Why a single plane wave fails on energy: receive-only DAS over a region is close to an energy-preserving (unitary-like) migration of the channel data, so \(\sum\lvert b\rvert^2\) barely changes when the focusing law is wrong; the energy just moves between pixels. Sharpness, which rewards concentrating energy, and coherent compounding across angles, which rewards angles that agree, both break that invariance. This is the ultrasound version of Muller & Buffington's result for telescope image sharpening (JOSA, 1974): \(\int I\) is invariant to the aberration, \(\int I^2\) is maximized by the correct correction.
Autofocus: gradient ascent on a focus metric
SimulationPlane-wave IQ channel data of random point scatterers synthesized and beamformed by DAS in JS (straight rays, homogeneous medium); exact chain-rule gradient, Armijo ascent.In this simulation the compounded energy peaks 2–10 m/s above the true speed and sharpness up to about 20 m/s below it (0.1–1.3 %); the bias comes from the finite ROI and the angle geometry (it does not change with finer sampling), not from noise. The bias is small next to the ±20–40 m/s spread of tissue speeds, but it is a systematic error: calibrate the estimator on phantoms of known speed, and report the bias next to the precision.
Pulse-echo estimation of the average sound speed from the received wavefronts goes back at least to Anderson & Trahey (JASA, 1998); sweeping \(\hat c\) to maximize a focus or coherence metric is the common global form. Differentiable-beamforming autofocus (Simson, Dahl, Hyun and colleagues, MICCAI 2023) parameterizes a slowness map, computes delays by integrating slowness along straight rays, scores focus with an image-quality loss, and back-propagates through the beamformer to every map parameter with reverse-mode AD; a 2024 follow-up uses a common-midpoint phase-error loss and reports in vivo liver results (see Figures from the literature). The lessons above carry over unchanged: tie the patches to time, use metrics that are not energy-invariant, start from a sensible global \(\hat c\), regularize the map (Module 13), and expect a path-averaged answer in layered media.
Differentiable-beamforming autofocus as a computational graph. Forward (blue): slowness map → ray-integrated delays → interpolated, phase-rotated IQ → coherent sum → patch focus metric → loss with a map regularizer. Reverse (pink): the adjoint of the sum is a broadcast, the adjoint of the delay step needs IQ′ + jω₀IQ (the hand-derived db/dĉ of this section), and the adjoint of the ray integral back-projects ∂L/∂τ along each ray onto the map.
14.6Automatic differentiation
Four ways to get \(f'(x)\), in increasing order of reliability:
- \(h\)
- step, in units of \(x\)
- \(\varepsilon\)
- machine epsilon, \(2.2 \times 10^{-16}\) in double precision
- \(f(x)\)
- function to differentiate
- \(x\)
- evaluation point
- \(f^{\prime\prime}\)
- second derivative
- \(h^*\)
- step that minimizes the total error (truncation plus round-off)
- \(\lvert\cdot\rvert\)
- absolute value
- \(f(x)\)
- function to differentiate
- \(x\)
- evaluation point
- \(h\)
- step, in units of \(x\), as in (14.20)
- \(\varepsilon\)
- machine epsilon, as in (14.20)
- \(h^*\)
- step that minimizes the total error
- \(O(\cdot)\)
- error scales as the given power
- \(i\)
- imaginary unit
- \(h\)
- imaginary step; any \(h \le 10^{-8}\), even \(10^{-20}\) or \(10^{-100}\)
- \(f\)
- real-analytic function implemented with complex-safe operations
- \(\mathrm{Im}\)
- imaginary part
- \(x\)
- real evaluation point
- \(f^{\prime},\ f^{\prime\prime},\ f^{\prime\prime\prime}\)
- first, second and third derivatives at \(x\)
- \(\epsilon\)
- dual unit, nilpotent (\(\epsilon^2 = 0\)); not machine epsilon
- \(\dot x\)
- tangent (seed direction) carried with the value
- \(x\)
- value (real number)
- \(f(x),\ f^{\prime}(x)\)
- function value and its derivative at \(x\)
Reverse mode records the computation (the tape) on a forward sweep, then propagates adjoints \(\bar v = \partial y/\partial v\) backwards with the chain rule. The standard example (Baydin et al., 2018), \(y = \ln x_1 + x_1x_2 - \sin x_2\) at (2, 5):
| Forward sweep (primal) | value | Forward tangent (\(\dot x_1 = 1,\ \dot x_2 = 0\)) | value | Reverse sweep (adjoint, \(\bar y = 1\)) | value |
|---|---|---|---|---|---|
| \(v_1 = \ln x_1\) | 0.6931 | \(\dot v_1 = \dot x_1/x_1\) | 0.5 | \(\bar v_4 = \bar y,\ \bar v_3 = -\bar y\) | 1, −1 |
| \(v_2 = x_1x_2\) | 10 | \(\dot v_2 = \dot x_1x_2 + x_1\dot x_2\) | 5 | \(\bar v_1 = \bar v_4,\ \bar v_2 = \bar v_4\) | 1, 1 |
| \(v_3 = \sin x_2\) | −0.9589 | \(\dot v_3 = \cos x_2\,\dot x_2\) | 0 | \(\bar x_1 = \bar v_1/x_1 + \bar v_2x_2\) | 5.5 |
| \(v_4 = v_1 + v_2\) | 10.6931 | \(\dot v_4 = \dot v_1 + \dot v_2\) | 5.5 | \(\bar x_2 = \bar v_2x_1 + \bar v_3\cos x_2\) | 1.7163 |
| \(y = v_4 - v_3\) | 11.6521 | \(\dot y = \dot v_4 - \dot v_3\) | 5.5 = ∂y/∂x₁ | one sweep gives both partials | (5.5, 1.7163) |
The computational graph, forward values and reverse adjoints
SchematicGraph layout drawn for intuition; the node values and adjoints printed on it are exact.- \(\mathbf{J}\)
- Jacobian matrix, as in (14.8)
- \(m\)
- number of outputs
- \(n\)
- number of inputs
- \(\mathbb{R}^{m\times n}\)
- real matrices with m rows and n columns
Derivative error versus step: differences, complex step, dual numbers
SimulationEach method evaluated in double-precision JS arithmetic and compared with the analytic derivative; the error is real floating-point behaviour, not a model.Writing this demo, a textbook complex square root, \(\mathrm{Im}\sqrt{z} = \sqrt{(\lvert z\rvert - \mathrm{Re}\,z)/2}\), returned an imaginary part of exactly 0 for \(z = a + 10^{-20}i\): \(\lvert z\rvert - \mathrm{Re}\,z\) cancels. The fix is \(\mathrm{Im}\sqrt{z} = \mathrm{Im}\,z / (2\,\mathrm{Re}\sqrt{z})\), with \(\mathrm{Re}\sqrt{z} = \sqrt{(\lvert z\rvert + \mathrm{Re}\,z)/2}\). Every elementary function in the chain must be implemented without cancellation in the imaginary part, which MATLAB's built-ins are, except abs and the ' operator (the MATLAB lab shows x'*x giving a derivative of 0).
14.7Complex parameters, Wirtinger calculus and constraints
IQ data, focusing laws and aberration estimates are complex, but losses are real. A real \(\mathcal{L}(z)\) is never complex-differentiable (unless constant), so use the Wirtinger derivatives, treating \(z\) and \(z^*\) as independent:
- \(z = x + iy\)
- complex parameter (IQ weight, focusing-law entry)
- \(z^*\)
- complex conjugate, treated as an independent variable
- \(\mathcal{L}\)
- real-valued loss
- \(x,\ y\)
- real and imaginary parts of \(z\)
- \(i\)
- imaginary unit
- \(\partial/\partial z,\ \partial/\partial z^*\)
- Wirtinger derivatives
- \(\mathcal{L},\ z\)
- as in (14.25)
- \(a\)
- complex target value
- \(\lvert\cdot\rvert\)
- complex magnitude
- \(\mathbf{y}\)
- complex data vector
- \(\mathbf{A}\)
- complex model matrix
- \(\mathbf{w}\)
- complex weight vector (unknown)
- \(\mathbf{R}\)
- Hermitian matrix (covariance)
- \(\lVert\cdot\rVert\)
- Euclidean norm
- \(^{H}\)
- conjugate (Hermitian) transpose
- \(\partial\mathcal{L}/\partial\mathbf{w}^*\)
- Wirtinger gradient with respect to the conjugate weights
Aberration correction as optimization: find element phases \(\theta_e\) that maximize the coherent energy of the beamformed sum, \(b = \mathbf{w}^{H}\mathbf{s}\) with \(w_e = e^{j\theta_e}\), averaged over \(K\) speckle realizations, \(\mathcal{L}(\mathbf{\theta}) = \mathbf{w}^{H}\mathbf{R}\mathbf{w}\), \(\mathbf{R} = \mathbf{S}\mathbf{S}^{H}/K\):
- \(\mathbf{w}\)
- unit-modulus focusing law, one entry per element
- \(\mathbf{S}\)
- element signals (columns = \(K\) realizations)
- \(\mathbf{R}\)
- sample covariance across elements
- \(\mathcal{L}\)
- coherent output energy to be maximized
- \(w_e\)
- weight of element e, \(e^{j\theta_e}\)
- \(\theta_e\)
- phase correction of element e, rad
- \((\mathbf{R}\mathbf{w})_e\)
- element e of the vector \(\mathbf{R}\mathbf{w}\)
- \(\mathrm{Re},\ \mathrm{Im}\)
- real and imaginary parts
- \(^*\)
- complex conjugate
- \(j\)
- imaginary unit
Constraints enter through projection or a proximal step. Projected gradient keeps \(\hat c\) inside physical bounds (the autofocus demo projects onto [1350, 1750] m/s); the proximal gradient step \(\mathbf{x} \leftarrow \mathrm{prox}_{\alpha g}(\mathbf{x} - \alpha\nabla f)\) handles a nonsmooth regularizer \(g\), e.g. soft thresholding for L1 (ISTA, Module 13).
Aberration law by Wirtinger gradient ascent
SimulationApprox. modelNarrowband focal-plane model, thin phase screen at the array on transmit and receive, K = 64 speckle realizations.14.8Visual summary
Each card is a question you should answer in five seconds, the answer with its key number, and the picture to keep in mind. Every curve is computed from the formula in the answer; the badge says whether it is a closed form, a numerical run or a simplified model.
14.9How experts think about it
Near a solution every cost is a quadratic, so \(\kappa\) of the Hessian decides how first-order methods converge and scaling or curvature (Newton, Gauss–Newton) is the cure. Far from the solution the shape of the basin decides everything: oscillatory data (RF, phase, a fixed-depth pixel) cut the basin to half a period, and the remedy is a cost whose basin is wider (envelope, low frequency, time-tied ROI, compounding) used first, then the precise cost. Derivatives are exact or they are suspect: write them by hand or by AD, check them against central differences once, and for complex parameters step along \(-\partial\mathcal{L}/\partial z^*\).
In the Hessian's eigenbasis each error component is multiplied by \(1 - \alpha\lambda\). The best fixed \(\alpha\) makes the extreme factors equal and opposite, \(\lvert 1 - \alpha\mu\rvert = \lvert 1 - \alpha L\rvert\), giving \(\rho = (\kappa-1)/(\kappa+1)\), (14.3). Iterations to \(\varepsilon\): \(\ln(1/\varepsilon)/\ln\frac{\kappa+1}{\kappa-1} \approx (\kappa/2)\ln(1/\varepsilon)\): 69, 691, 6908 at \(\kappa\) = 10, 100, 1000, matched exactly by the MATLAB lab.
The stiff mode is multiplied by \(1 - \alpha L\), which leaves the unit circle when \(\alpha > 2/L\), (14.2). At \(\alpha = 2.05/L\) it grows by 1.05 per step, \(1.05^{50} = 11.5\) after 50 steps (MATLAB lab). With \(\alpha = 1/L\) the stiff mode is killed in one step and the flat mode decays as \(1 - 1/\kappa\): slower than \(\alpha^*\) by a factor of about 2, but robust when \(L\) is only estimated (power iteration on the Hessian, Module 06).
Heavy ball with \(\alpha = 4/(\sqrt{L} + \sqrt{\mu})^2\), \(\beta = \left(\frac{\sqrt\kappa-1}{\sqrt\kappa+1}\right)^2\) has per-mode characteristic roots of modulus \(\sqrt\beta\), so \(\rho = (\sqrt\kappa-1)/(\sqrt\kappa+1) = 0.818\) at \(\kappa\) = 100 against 0.980 for gradient descent, (14.4). The tuned parameters create a double root, so the error decays as \(k\rho^k\): 93 measured iterations against 69 from the rate alone. Nesterov (\(\alpha = 1/L\), \(\beta = (\sqrt\kappa-1)/(\sqrt\kappa+1)\)) gives \(1 - 1/\sqrt\kappa\), 155 iterations.
Mixing m/s with order-1 parameters makes the Jacobian columns differ by \(10^3\), so \(\kappa(\mathbf{J}^{T}\mathbf{J}) \approx 10^6\) and gradient descent needs ~\(10^7\) iterations. Dividing each column by its norm is a diagonal change of variables (Jacobi preconditioning): the MATLAB lab drops \(\kappa\) from \(1.22 \times 10^6\) to 1.17. The echo fit has \(\kappa = 1.8 \times 10^3\) in (1, µs, µs, MHz), \(2 \times 10^{22}\) in SI units, 3.7 scaled; scaled gradient descent needs 51 iterations instead of 10 828.
Newton's step minimizes the local quadratic model exactly, so the error after a step is set by the third-derivative term: \(e_{k+1} \propto e_k^2\), (14.7). \(10^{-2}\) becomes \(10^{-4}\), then \(10^{-8}\). Because \(\mathbf{d} = -\mathbf{H}^{-1}\nabla f\) transforms with the parameters, rescaling \(\mathbf{\theta}\) changes nothing (affine invariance). On Rosenbrock from (−1.2, 1), 5 steps against thousands for first-order methods.
For \(f = \tfrac12\lVert\mathbf{r}\rVert^2\), \(\nabla^2 f = \mathbf{J}^{T}\mathbf{J} + \sum_i r_i\nabla^2 r_i\) (14.8); the second term is small for small or nearly linear residuals, and \(\mathbf{J}^{T}\mathbf{J}\) is positive semidefinite, so the Gauss–Newton step is always a descent direction. Solve \(\mathbf{J}\mathbf{\delta} \approx -\mathbf{r}\) by QR (Module 05), not via \(\mathbf{J}^{T}\mathbf{J}\). At convergence \(\sigma^2(\mathbf{J}^{T}\mathbf{J})^{-1}\) is the Gaussian CRLB at the estimate (Module 12): 0.69 ns on \(t_0\) for the 26 dB echo fit.
Small \(\lambda\) gives the Gauss–Newton step; large \(\lambda\) gives a short step along the scaled negative gradient, \(-(\lambda\mathbf{D})^{-1}\nabla f\), which always decreases \(f\) for \(\lambda\) large enough, (14.9). The accept/reject rule makes LM a trust-region method in disguise: it never accepts an uphill step. With \(\mathbf{D} = \mathrm{diag}(\mathbf{J}^{T}\mathbf{J})\) (Marquardt) the method is insensitive to parameter units. Rosenbrock: 22 accepted steps from \(\lambda_0 = 10^{-3}\).
Armijo (14.6): \(f(\mathbf{x} + \alpha\mathbf{d}) \le f(\mathbf{x}) + c_1\alpha\nabla f^{T}\mathbf{d}\). For small \(\alpha\) the condition holds whenever \(\mathbf{d}\) is a descent direction, so backtracking terminates; requiring a fraction of the linear prediction rules out the vanishing gains that stall naive "accept any decrease" rules. It replaces the unknown \(L\): the autofocus demo uses it with a sign step, so the same code works for metrics whose curvature differs by \(10^3\).
\(\hat m\) estimates \(\mathrm{E}[g]\) and \(\hat v\) estimates \(\mathrm{E}[g^2]\); for a steady gradient \(\hat m/\sqrt{\hat v} = \mathrm{sign}(g)\), so each parameter moves by \(\eta\) whatever its units, (14.11): invariant to diagonal rescaling of the gradient, which is why it tolerates mixed sensitivities in large models. On Rosenbrock (\(\eta\) = 0.02) it needs 3034 iterations, close to heavy ball with \(\alpha = 5 \times 10^{-4}\), \(\beta\) = 0.9 (3301): it is not a curvature method.
With noisy gradients, \(e_{k+1} = (1 - \alpha\lambda)e_k - \alpha n_k\); the variance recursion settles where shrinkage balances injected noise: \(\alpha\sigma^2/(\lambda(2 - \alpha\lambda)) \approx \alpha\sigma^2/(2\lambda)\), (14.10). Halve \(\alpha\): half the floor, half the speed. Decay the step (\(\sum\alpha = \infty\), \(\sum\alpha^2 < \infty\)) or grow the batch to converge.
\(\mathcal{L}(\tau) = 2E - 2R(\tau)\) and \(R(\tau) \approx E\,e^{-\tau^2/4\sigma^2}\cos(2\pi f_0\tau)\), (14.12)–(14.13): the cosine creates minima at nearly every multiple of \(T_0\) (the envelope pulls them in to 194, 388 ns) separated by maxima near \(\pm T_0/2\). It is Module 11's false correlation peak seen from the optimizer's side. In the MATLAB lab RF converges only from \(\lvert\tau_0\rvert \le\) 90 ns (19 of 121 starts).
A speed error \(\Delta c\) on a path \(L\) shifts the arrival by \(\Delta t = L\Delta c/c^2\). The cycle-skipping bound \(\lvert\Delta t\rvert < T/2\) gives \(\lvert\Delta c\rvert/c < \lambda/(2L)\), one over twice the path length in wavelengths. \(c\) = 1500 m/s: 11.2 m/s at 0.5 MHz over 200 mm, 1.1 m/s at 5 MHz. Hence FWI's travel-time starting models and low starting frequencies.
The envelope misfit \(1 - e^{-\tau^2/4\sigma^2}\) has one minimum (convex for \(\lvert\tau\rvert < \sqrt2\sigma\) = 177 ns), but relative to signal energy its curvature at the minimum is lower than the RF one by \(1 + 2(2\pi f_0\sigma)^2 = 31.8\), and curvature is what sets precision in noise, (14.14). A low-pass widens the RF basin (1.5 MHz corner: ±160 ns) but keeps only −29 dB of a 5 MHz, 60 % pulse, (14.15). Envelope then RF: all 121 starts converge.
A pixel at fixed depth \(z\) is sampled at \(t = 2z/\hat c\), so changing \(\hat c\) slides the echo through the pixel: \(\Delta t = 2z\Delta\hat c/\hat c^2\), one carrier period per \(\hat c^2/(2zf_0)\), (14.19). Its RF value cycle-skips with ripples every 7–8 m/s; its envelope has a ±20 m/s basin. An ROI fixed in two-way time removes the bulk shift, leaving only defocus, and the compounded energy over it has one peak over 1350–1750 m/s.
Receive DAS over a region is close to an energy-preserving migration of the channel data, so \(\sum\lvert b\rvert^2\) over an ROI is nearly independent of the focusing law; in the demo a single plane wave gives a flat energy curve peaking 60–100 m/s high. Sharpness \(\sum I^2/(\sum I)^2\) rewards concentration (Muller & Buffington, 1974), and coherent compounding over angles rewards agreement between transmits, which is focus: with 5 angles the energy peaks within 2–10 m/s of the truth.
Dual numbers propagate one tangent (\(\mathbf{J}\mathbf{v}\)); reverse mode propagates one adjoint (\(\mathbf{v}^{T}\mathbf{J}\)), (14.24). A scalar autofocus loss of \(10^4\) slowness parameters needs one reverse pass at a small constant multiple of the loss cost, against \(2 \times 10^4\) loss evaluations for central differences. The tape stores intermediates: one float64 per pixel–element–angle for a 256 × 256 image, 128 elements and 11 angles is \(9.2 \times 10^7\) values, 0.74 GB per stored quantity.
Forward differences balance truncation \(h\lvert f''\rvert/2\) against round-off \(2\varepsilon\lvert f\rvert/h\): optimum \(h \sim \sqrt\varepsilon\), error \(\sim\sqrt\varepsilon \approx 10^{-8}\), (14.20). Central differences reach \(\varepsilon^{2/3}\) (\(6 \times 10^{-12}\) at \(h = 3 \times 10^{-6}\) in the MATLAB lab). The complex step \(\mathrm{Im}\,f(x + ih)/h\) involves no subtraction, so any \(h \le 10^{-8}\) gives machine precision (\(2.2 \times 10^{-16}\) at \(h = 10^{-20}\) and \(10^{-100}\)), (14.22).
Treat \(z\) and \(z^*\) as independent; for real \(\mathcal{L}\) the gradient in (Re, Im) packed as a complex number is \(2\,\partial\mathcal{L}/\partial z^*\), (14.25). For \(\mathcal{L} = \mathbf{w}^{H}\mathbf{R}\mathbf{w}\), \(\partial\mathcal{L}/\partial\mathbf{w}^* = \mathbf{R}\mathbf{w}\), and the normalized ascent is a power iteration toward the time-reversal focusing law. The conjugate, \(\partial\mathcal{L}/\partial\mathbf{w} = (\mathbf{R}\mathbf{w})^*\), flips every imaginary part: in the demo it lowers the coherent energy and leaves 1.5 rad of phase error; on least squares it diverges.
14.10Cheat sheet
| Concept | Formula | Ultrasound use |
|---|---|---|
| GD stability and rate | \(0 < \alpha < 2/L\); \(\rho(\alpha) = \max_i\lvert 1 - \alpha\lambda_i\rvert\); \(\alpha^* = 2/(L+\mu)\), \(\rho^* = (\kappa-1)/(\kappa+1)\) | Iteration budgets for any first-order fit or reconstruction |
| Iterations to \(\varepsilon\) | GD \(\approx (\kappa/2)\ln(1/\varepsilon)\); heavy ball \(\approx (\sqrt\kappa/2)\ln(1/\varepsilon)\) | κ = 100: 691 vs 69 (93 measured) |
| Heavy ball / Nesterov | \(\alpha = 4/(\sqrt L+\sqrt\mu)^2\), \(\beta = \left(\frac{\sqrt\kappa-1}{\sqrt\kappa+1}\right)^2\); Nesterov \(\alpha = 1/L\), \(\beta = \frac{\sqrt\kappa-1}{\sqrt\kappa+1}\), \(\rho = 1 - 1/\sqrt\kappa\) | Accelerated iterative reconstruction (FISTA is Nesterov on a proximal step) |
| Jacobi scaling | \(\tilde{\mathbf{\theta}} = \mathbf{D}\mathbf{\theta}\), \(\mathbf{D} = \mathrm{diag}(\lVert\mathbf{J}_{:,j}\rVert)\) | Mixing m/s, dB/cm/MHz, µm in one parameter vector |
| Armijo | \(f(\mathbf{x}+\alpha\mathbf{d}) \le f(\mathbf{x}) + c_1\alpha\nabla f^{T}\mathbf{d}\), \(c_1 = 10^{-4}\), \(\alpha \leftarrow \alpha/2\) | Step size without knowing \(L\); robust autofocus ascent |
| Newton | \(\mathbf{d} = -\mathbf{H}^{-1}\nabla f\); \(e_{k+1} \propto e_k^2\); affine invariant | Small, smooth problems near the answer |
| Gauss–Newton | \(\mathbf{J}\mathbf{\delta} \approx -\mathbf{r}\) (QR); \(\nabla^2 f \approx \mathbf{J}^{T}\mathbf{J}\); \(\mathrm{cov} \approx \sigma^2(\mathbf{J}^{T}\mathbf{J})^{-1}\) | BSC, attenuation, echo-model fits with error bars |
| Levenberg–Marquardt | \((\mathbf{J}^{T}\mathbf{J} + \lambda\,\mathrm{diag}\,\mathbf{J}^{T}\mathbf{J})\mathbf{\delta} = -\mathbf{J}^{T}\mathbf{r}\); λ ÷10 success, ×10 failure | Default for 2–10 parameter QUS fits |
| SGD noise floor | \(\mathrm{E}\,e^2 \to \alpha\sigma^2/(\lambda(2 - \alpha\lambda))\); \(\sigma^2 \propto 1/B\) | Training learned beamformers; patch-averaged autofocus |
| Adam | \(x \mathrel{-}= \eta\,\hat m/(\sqrt{\hat v} + \epsilon)\); steady \(g\) → step \(\eta\,\mathrm{sign}(g)\) | Large differentiable beamformers and networks |
| RF misfit | \(\mathcal{L}(\tau) = 2E - 2R(\tau)\), \(R \approx E\,e^{-\tau^2/4\sigma^2}\cos 2\pi f_0\tau\) | Delay, phase-screen and waveform fits |
| Cycle-skipping basin | \(\lvert\Delta\tau\rvert < T/2\) (\(T/4\) with free polarity); \(\lvert\Delta c\rvert/c < \lambda/(2L)\) | Starting models for FWI and USCT |
| Envelope vs RF curvature | ratio \(1 + 2(2\pi f_0\sigma)^2\) (31.8 at 5 MHz, σ = 0.125 µs) | Coarse-to-fine (multiscale) schedules |
| Fixed-depth autofocus ripple | \(\Delta\hat c = \hat c^2/(2zf_0)\) | 7.9 m/s at 30 mm: use time-tied ROIs |
| Beamformer derivative | \(db/d\hat c = \sum (\mathrm{IQ}' + j\omega_0\mathrm{IQ})\,e^{j\omega_0\tau}\,\partial\tau/\partial\hat c\) | Exact gradients for sound-speed autofocus |
| Finite differences | forward \(h \sim \sqrt\varepsilon\), error \(\sim\sqrt\varepsilon\); central \(h \sim \varepsilon^{1/3}\), error \(\sim\varepsilon^{2/3}\) | Gradient checks, not production gradients |
| Complex step | \(f' \approx \mathrm{Im}\,f(x+ih)/h\), \(h = 10^{-20}\) | Exact derivatives of real MATLAB code |
| Forward vs reverse AD | \(\mathbf{J} \in \mathbb{R}^{m\times n}\): \(n\) vs \(m\) passes; reverse stores the tape | Reverse for scalar losses of SoS maps |
| Wirtinger | \(\partial/\partial z^* = \tfrac12(\partial_x + i\partial_y)\); descend along \(-\partial\mathcal{L}/\partial z^*\) | Gradients w.r.t. IQ weights, focusing and aberration laws |
| Phase-only gradient | \(\partial(\mathbf{w}^{H}\mathbf{R}\mathbf{w})/\partial\theta_e = 2\,\mathrm{Im}\left(w_e^*(\mathbf{R}\mathbf{w})_e\right)\) | Aberration correction by coherence maximization |
14.11Interview and qualifying-exam questions
I1Your gradient-descent reconstruction converges painfully slowly. Diagnose it and give the rate. · screen
Slow linear convergence means a large condition number of the Hessian. With the optimal fixed step \(2/(L + \mu)\) the error contracts by \((\kappa - 1)/(\kappa + 1)\) per iteration, about \((\kappa/2)\ln(1/\varepsilon)\) iterations: 691 for \(\kappa\) = 100 and \(\varepsilon = 10^{-6}\). First check the scaling (parameters in mixed units inflate \(\kappa\) by orders of magnitude; Jacobi scaling fixed \(1.2 \times 10^6 \to 1.17\) in the lab), then add momentum or use conjugate gradients (\(\sqrt\kappa\) dependence), or switch to Gauss–Newton if it is a least-squares problem. Follow-up: why does momentum help? Per mode it is a second-order recursion; tuned, its roots have modulus \((\sqrt\kappa-1)/(\sqrt\kappa+1)\), so iterations scale with \(\sqrt\kappa\).
I2How do you choose the step size when you do not know L? · screen
Backtracking with the Armijo condition: try \(\alpha\) (or 2× the last accepted \(\alpha\)), halve until \(f(\mathbf{x} + \alpha\mathbf{d}) \le f(\mathbf{x}) + 10^{-4}\alpha\nabla f^{T}\mathbf{d}\). Alternatively estimate \(L\) by a few power iterations on Hessian–vector products and use \(1/L\). For least squares, Gauss–Newton or LM chooses the step from curvature and needs no \(\alpha\). Follow-up: what can go wrong? Too short steps (add the Wolfe curvature condition) and, on nonconvex costs, long trial steps that jump basins; cap the trial step at a physically meaningful size (40 m/s in the autofocus demo).
I3You fit a backscatter or attenuation model with 3–5 parameters. GD, Gauss–Newton, LM or Adam? · onsite
LM: it behaves like Gauss–Newton (quadratic convergence for small residuals, about 10 iterations in the lab's echo fit) near the answer and like scaled gradient descent far from it, and never accepts an uphill step. Solve the subproblem with QR or with the scaled normal equations at this size, and scale parameters. At convergence \(\sigma^2(\mathbf{J}^{T}\mathbf{J})^{-1}\) gives the CRLB-type error bars (0.69 ns on \(t_0\)). Adam would take thousands of iterations and give no covariance. Follow-up: when does Gauss–Newton degrade? Large residuals (model error) or strong nonlinearity make the neglected \(\sum r_i\nabla^2 r_i\) term matter; convergence becomes linear.
I4Explain cycle skipping and quantify it for a 5 MHz pulse. · onsite
The L2 misfit between an echo and a delayed model is \(2E - 2R(\tau)\); for an RF pulse \(R(\tau) \approx E\,e^{-\tau^2/4\sigma^2}\cos 2\pi f_0\tau\), so local minima appear near every period (194, 388 ns for \(f_0\) = 5 MHz, \(\sigma\) = 0.125 µs) and the basin of the true minimum ends near \(\pm T_0/2\) = ±97 ns. A start beyond that converges to the wrong cycle. In travel-time form the starting model must satisfy \(\lvert\Delta c\rvert/c < \lambda/(2L)\). Remedies: envelope or correlation-based costs, low frequencies, multiscale schedules, better starting models. Follow-up: why not just low-pass the data? The energy is not there: a 1.5 MHz Gaussian low-pass keeps −29 dB of a 5 MHz, 60 % pulse, so noise dominates; you must transmit low frequencies.
I5Design a gradient-based global sound-speed estimator for a linear array. · research
Acquire several plane-wave angles; beamform IQ data on an ROI fixed in two-way time (pixel depth \(\hat ct/2\)) so that \(\hat c\) only changes focusing; maximize the coherently compounded energy or a coherence metric over ROIs with bright, diffuse echoes; get \(dM/d\hat c\) exactly (chain rule through delays and the interpolator, or AD), check it once by central differences; climb with a line search and a box constraint, from a coarse sweep or several starts. Report bias (calibrate on phantoms: the demo shows +2 to +10 m/s geometric bias) and precision across ROIs. Follow-up: why not maximize the energy of a single plane-wave image, or the RF value at the target depth? The first is nearly invariant to focusing (receive migration preserves energy); the second cycle-skips every \(\hat c^2/(2zf_0) \approx\) 8 m/s.
I6Forward or reverse mode for a differentiable beamformer with a 100 × 100 slowness map and a scalar loss? What limits it? · onsite
Reverse mode: \(10^4\) inputs, 1 output, so one backward pass at a small constant multiple of the forward cost, against \(10^4\) forward-mode passes. The limit is memory: the tape stores delays, interpolation indices and weights per pixel, element and transmit; for 256 × 256 pixels, 128 elements and 11 angles that is \(9.2 \times 10^7\) values, 0.74 GB per float64 quantity. Use checkpointing (recompute delays on the backward pass), float32 and patch-wise losses. Follow-up: when is forward mode better? Few parameters (a single \(\hat c\), a handful of layer speeds): one dual-number pass per parameter, no tape.
I7What is the complex-step derivative, why can h be 10⁻²⁰, and when does it fail? · screen
\(f'(x) \approx \mathrm{Im}\,f(x + ih)/h\). The Taylor series gives \(\mathrm{Im}\,f(x + ih) = hf'(x) - h^3f'''/6 + \dots\), with no subtraction of nearly equal numbers, so round-off does not grow as \(h\) shrinks and \(h = 10^{-20}\) yields machine precision (\(2.2 \times 10^{-16}\) in the lab) against ~\(10^{-8}\) for forward differences. It fails for non-analytic operations (abs, conj, MATLAB's ', max/min branches) and for code that already uses complex numbers, such as an IQ beamformer. Follow-up: what would you use for the IQ beamformer? Dual numbers or reverse-mode AD, or the hand-derived chain rule, verified once by central differences.
I8Derive the gradient of ‖y − Aw‖² with respect to complex w, and say how you would test it. · research
\(\mathcal{L} = (\mathbf{y} - \mathbf{A}\mathbf{w})^{H}(\mathbf{y} - \mathbf{A}\mathbf{w})\). Treating \(\mathbf{w}\) and \(\mathbf{w}^*\) as independent, \(\partial\mathcal{L}/\partial\mathbf{w}^* = -\mathbf{A}^{H}(\mathbf{y} - \mathbf{A}\mathbf{w})\); the steepest-descent update is \(\mathbf{w} \leftarrow \mathbf{w} + \mu\mathbf{A}^{H}(\mathbf{y} - \mathbf{A}\mathbf{w})\), which is the LMS/Landweber step. Test: perturb \(\mathrm{Re}\,w_k\) and \(\mathrm{Im}\,w_k\) separately by \(\pm h\); \(d\mathcal{L}/d\mathrm{Re} + i\,d\mathcal{L}/d\mathrm{Im}\) must equal \(2\,\partial\mathcal{L}/\partial w_k^*\) (agreement \(2 \times 10^{-10}\) in the lab). Follow-up: what happens with the conjugate direction? The imaginary parts of all steps flip sign; the lab's run diverges to \(10^{62}\).
I9Adam converged to a slightly different answer each run and never quite settles. Why? · onsite
With a constant learning rate \(\eta\), Adam moves each parameter by about \(\eta\) per step (\(\hat m/\sqrt{\hat v} \approx \mathrm{sign}\,g\)), so it hovers in a band of order \(\eta\) around the minimum; with stochastic gradients there is also a noise floor proportional to the step (\(\alpha\sigma^2/(2\lambda)\) for SGD). Decay \(\eta\), increase the batch, or finish with a deterministic second-order method on the full cost. Follow-up: is Adam scale invariant? To a diagonal rescaling of the gradient yes (though a step of η still means different physical changes in different parameter units), to rotations no: a rotated narrow valley still slows it (Rosenbrock: 3034 iterations).
I10You maximize coherent energy over element phases on speckle. The optimizer converges, the energy exceeds that of the true correction, but the estimated screen is off by 0.14 rad rms. Explain. · research
The cost is the sample coherent energy \(\mathbf{w}^{H}\hat{\mathbf{R}}\mathbf{w}\) with \(\hat{\mathbf{R}}\) from \(K\) = 64 realizations. Speckle has no unique focal point: \(\hat{\mathbf{R}}\) is far from rank one (\(\lambda_1/\lambda_2 \approx 2.4\) in the demo, against about 80 with a point 10 dB above the speckle) and its top eigenvector fits the sampling fluctuations of \(\hat{\mathbf{R}}\), so the optimizer finds a law that beats the true one on this data set yet differs from it. A point-like target (guide star) makes \(\hat{\mathbf{R}}\) nearly rank one: at 10 dB target-to-speckle ratio the error drops to about 0.02 rad. More independent realizations, isoplanatic averaging or the distortion-matrix approach (Module 06) also help. Follow-up: why remove tilt before scoring? A linear phase only steers the beam (Module 07); it is not identifiable from coherence.
14.12MATLAB lab
Base MATLAB only (no toolboxes); each script runs as-is in MATLAB R2019b+ and GNU Octave 8. Paste into the editor and press Run.
m14_gd_conditioning.mIterations to 10⁻⁶ for GD (2/(L+μ) and 1/L), heavy ball and Nesterov vs the rate formulas at κ = 10, 100, 1000; divergence above 2/L; Jacobi scaling of an m/s + amplitude problem · change kappa_list, th · runtime ≈ 2 s
%% m14_gd_conditioning.m -- gradient descent, momentum and the condition number
% Purpose : minimize the quadratic f(x) = 1/2 x'*A*x (minimum at 0) with eigenvalues
% mu = 1 and L = kappa, and count iterations to reduce the error norm by 1e-6.
% Compares gradient descent with the optimal fixed step 2/(L+mu), with step 1/L,
% Polyak heavy-ball with its optimal (alpha, beta), and Nesterov's method
% (step 1/L, momentum (sqrt(kappa)-1)/(sqrt(kappa)+1)) against the rate formulas.
% Then shows that diagonal rescaling of badly scaled parameters (here a sound
% speed in m/s and a dimensionless amplitude) removes most of kappa.
% Change : kappa_list, tol, the rotation angle th, the momentum settings.
% Expect : kappa = 100: GD(2/(L+mu)) 691 iterations (theory 691), GD(1/L) 1341 (1375; this
% start puts only 1/sqrt(2) of the error on the slow mode), heavy ball 93 and
% Nesterov 155 (rate-only predictions 69 and 131: both tuned iterations have a
% double root, so the error decays as k*rho^k, not rho^k). Jacobi scaling of a
% (m/s, amplitude) problem drops kappa(J'J) from about 1e6 to about 1.
% Base MATLAB only; runs in MATLAB R2019b+ and GNU Octave 8.
rng(1);
%% 1. Iterations to tolerance vs kappa
tol = 1e-6; kappa_list = [10 100 1000]; maxit = 1e5;
th = pi/6; Q = [cos(th) -sin(th); sin(th) cos(th)]; % rotate so the axes are not the eigenvectors
fprintf('kappa | GD 2/(L+mu) theory | GD 1/L theory | heavy ball theory | Nesterov theory\n');
hist100 = {};
for kappa = kappa_list
mu = 1; L = kappa; A = Q * diag([mu L]) * Q.';
x0 = Q * [1; 1];
sk = sqrt(kappa);
cfg = { 2/(L+mu), 0, 'hb'; % {alpha, beta, type}
1/L, 0, 'hb';
4/(sqrt(L)+sqrt(mu))^2, ((sk-1)/(sk+1))^2, 'hb';
1/L, (sk-1)/(sk+1), 'nest' };
its = zeros(1, 4);
for m = 1:4
a = cfg{m,1}; b = cfg{m,2}; x = x0; xp = x0; e = zeros(1, maxit);
for k = 1:maxit
e(k) = norm(x) / norm(x0);
if e(k) < tol, break; end
if strcmp(cfg{m,3}, 'nest')
y = x + b*(x - xp); xn = y - a*(A*y);
else
xn = x - a*(A*x) + b*(x - xp);
end
xp = x; x = xn;
end
its(m) = k - 1;
if kappa == 100, hist100{m} = e(1:k); end
end
th_gd = log(1/tol) / log((kappa+1)/(kappa-1));
th_1L = log(1/tol) / -log(1 - 1/kappa);
th_hb = log(1/tol) / log((sk+1)/(sk-1));
th_ne = log(1/tol) / -log(1 - 1/sk);
fprintf('%5d | %6d %6.0f | %6d %6.0f | %6d %6.0f | %6d %6.0f\n', ...
kappa, its(1), th_gd, its(2), th_1L, its(3), th_hb, its(4), th_ne);
end
%% 2. Divergence threshold: step just above 2/L
kappa = 100; A = Q * diag([1 kappa]) * Q.'; x = Q * [1; 1];
for k = 1:50, x = x - (2.05/kappa) * (A*x); end
fprintf('step 2.05/L after 50 iterations: |x| = %.3g (diverges; factor |1 - 2.05| = 1.05 per step)\n', norm(x));
%% 3. Badly scaled parameters: sound speed (m/s) and amplitude (unitless)
% Linearized model y = J*[dc; dA] with column sensitivities that differ by 3 orders.
J = [randn(50,1) * 1e-3, randn(50,1)]; % d y / d c (per m/s) and d y / d A
H = J.' * J; D = diag(1 ./ sqrt(diag(H))); % Jacobi (diagonal) preconditioner
fprintf('kappa(J''J) raw %.3g -> after column scaling %.3g\n', cond(H), cond(D*H*D));
kr = cond(H); ks = cond(D*H*D);
fprintf('GD iterations to 1e-6 (optimal step): raw %.3g, scaled %.0f\n', ...
log(1e6)/log((kr+1)/(kr-1)), log(1e6)/log((ks+1)/(ks-1)));
%% 4. Figures: error histories (kappa = 100) and paths on the contours
figure('Position', [100 100 1050 420]);
subplot(1,2,1);
cl = {[0 0.3 0.8], [0.6 0.6 0.6], [0.8 0.2 0.5], [0 0.55 0.4]}; hh = zeros(1,6);
for m = 1:4
hh(m) = semilogy(0:numel(hist100{m})-1, hist100{m}, 'Color', cl{m}, 'LineWidth', 1.5); hold on;
end
kk = 0:700;
hh(5) = semilogy(kk, ((100-1)/(100+1)).^kk, '--', 'Color', cl{1});
hh(6) = semilogy(kk, ((10-1)/(10+1)).^kk, '--', 'Color', cl{3});
grid on; ylim([1e-7 2]); xlim([0 700]); xlabel('iteration k'); ylabel('||x_k|| / ||x_0||');
legend(hh, {'GD 2/(L+\mu)', 'GD 1/L', 'heavy ball', 'Nesterov', '((\kappa-1)/(\kappa+1))^k', '((\surd\kappa-1)/(\surd\kappa+1))^k'}, 'Location', 'northeast');
title('\kappa = 100: measured error vs rate formulas');
subplot(1,2,2);
kappa = 30; A = Q * diag([1 kappa]) * Q.';
[g1, g2] = meshgrid(linspace(-1.6, 1.6, 200), linspace(-1.6, 1.6, 200));
F = 0.5 * (A(1,1)*g1.^2 + 2*A(1,2)*g1.*g2 + A(2,2)*g2.^2);
contour(g1, g2, log10(F + 1e-3), 14); hold on; colormap(gray);
x0 = Q * [1.2; 0.25]; hp = zeros(1,2);
for m = 1:2
if m == 1, a = 2/(kappa+1); b = 0; col = [0 0.3 0.8]; else, sk = sqrt(kappa); a = 4/(sk+1)^2; b = ((sk-1)/(sk+1))^2; col = [0.8 0.2 0.5]; end
x = x0; xp = x0; P = x0;
for k = 1:60, xn = x - a*(A*x) + b*(x - xp); xp = x; x = xn; P(:, end+1) = x; end
hp(m) = plot(P(1,:), P(2,:), '.-', 'Color', col, 'LineWidth', 1.2, 'MarkerSize', 10);
end
axis image; xlabel('x_1'); ylabel('x_2'); title('\kappa = 30: GD zig-zags, heavy ball spirals in');
legend(hp, {'GD 2/(L+\mu)', 'heavy ball (optimal \alpha, \beta)'}, 'Location', 'southoutside');
What to look for: GD with \(2/(L + \mu)\) matches theory exactly (69, 691, 6908); \(1/L\) gives 1341 at \(\kappa\) = 100 (theory 1375 for an error fully on the slow mode); heavy ball 27, 93, 315 and Nesterov 43, 155, 508 against rate-only 21, 69, 218 and 36, 131, 430 (the double root adds a \(k\) factor); \(\alpha = 2.05/L\) reaches \(\lvert x\rvert\) = 11.5 after 50 steps; \(\kappa(\mathbf{J}^{T}\mathbf{J})\) \(1.22 \times 10^6 \to 1.17\).
m14_gauss_newton_lm.mGaussian-pulse echo fit by GD + Armijo (raw and scaled), Gauss–Newton and LM; error bars from (JᵀJ)⁻¹; a start beyond T/4 that lands half a period late; free-phase fix · change th_true, sn, starts · runtime ≈ 7 s
%% m14_gauss_newton_lm.m -- gradient descent vs Gauss-Newton vs Levenberg-Marquardt
% Purpose : fit a Gaussian-pulse echo model to a noisy RF echo by nonlinear least squares
% s(t; A, t0, sg, f) = A exp(-(t-t0)^2/(2 sg^2)) cos(2 pi f (t - t0))
% (units: t, t0, sg in us; f in MHz) and compare
% GD + Armijo backtracking, Gauss-Newton (J'J d = -J'r), and
% Levenberg-Marquardt ((J'J + lam diag(J'J)) d = -J'r, lam /10 on success, x10 on failure).
% Prints the iterations, the estimates, the linearized standard deviations
% sqrt(diag(sn^2 (J'J)^-1)) (the Gaussian-noise CRLB of Module 12 evaluated at the
% solution), kappa(J'J) before and after column scaling, and shows that a start
% more than half a period off in t0 converges to the wrong cycle unless the model
% has a free carrier phase.
% Change : theta_true, sn (noise), the starts, lam0.
% Expect : good start: GN and LM about 10 iterations, GD + Armijo about 10^4 in raw units
% and tens after Jacobi scaling; bad start (t0 off by 100 ns, more than T/4 = 56 ns):
% LM lands half a period late with the polarity flipped (A < 0, t0 near 2.11 us,
% |r| three times the noise level); the free-phase model recovers t0 = 2.00 us.
% Base MATLAB only; runs in MATLAB R2019b+ and GNU Octave 8. Runtime about 2 s.
rng(1);
%% 1. Data
fs = 50; % sampling rate (MHz) -> dt = 0.02 us
t = (1:1/fs:3).'; % time axis (us), 101 samples
th_true = [1, 2.00, 0.15, 4.5]; % [A, t0 (us), sg (us), f (MHz)]
sn = 0.05; % noise std (peak SNR = 20 log10(1/0.05) = 26 dB)
model = @(p) p(1) * exp(-(t-p(2)).^2/(2*p(3)^2)) .* cos(2*pi*p(4)*(t-p(2)));
y = model(th_true) + sn * randn(size(t));
% analytic Jacobian of the residual r = model - y (columns: A, t0, sg, f)
jac = @(p) [exp(-(t-p(2)).^2/(2*p(3)^2)) .* cos(2*pi*p(4)*(t-p(2))), ...
p(1)*exp(-(t-p(2)).^2/(2*p(3)^2)) .* ((t-p(2))/p(3)^2 .* cos(2*pi*p(4)*(t-p(2))) + 2*pi*p(4)*sin(2*pi*p(4)*(t-p(2)))), ...
p(1)*exp(-(t-p(2)).^2/(2*p(3)^2)) .* cos(2*pi*p(4)*(t-p(2))) .* (t-p(2)).^2/p(3)^3, ...
-p(1)*exp(-(t-p(2)).^2/(2*p(3)^2)) .* sin(2*pi*p(4)*(t-p(2))) .* 2*pi.*(t-p(2))];
cost = @(p) 0.5 * sum((model(p) - y).^2);
% check the Jacobian against central differences
p = [0.9 2.03 0.13 4.8]; Jn = zeros(numel(t), 4);
for k = 1:4, e = zeros(1,4); e(k) = 1e-6; Jn(:,k) = (model(p+e) - model(p-e)) / 2e-6; end
fprintf('Jacobian check: max |analytic - numeric| / max|J| = %.1e\n', max(max(abs(jac(p)-Jn))) / max(abs(Jn(:))));
%% 2. Solvers from a good start and from a start beyond a quarter period
% GD+Armijo in raw units (step capped at 0.01), GD+Armijo after Jacobi (column) scaling,
% Gauss-Newton (least-squares solve of J d = -r) and Levenberg-Marquardt.
starts = {[0.8, 2.04, 0.12, 4.8], [0.8, 2.10, 0.12, 4.5]};
labels = {'good start (dt0 = +40 ns, df = +0.3 MHz)', 'bad start (dt0 = +100 ns > T/4)'};
nm = {'GD+Armijo', 'GD+Armijo+scale', 'Gauss-Newton', 'LM'};
hist = cell(2, 4);
for s = 1:2
fprintf('\n%s; period T = %.0f ns\n', labels{s}, 1e3/th_true(4));
for meth = 1:4
p = starts{s}; lam = 1e-3; al = 1e-2; h = cost(p); it = 0;
if meth == 2, Dsc = 1 ./ sqrt(sum(jac(p).^2, 1)); amax = 1; else, Dsc = ones(1,4); amax = 1e-2; end
while it < 20000
r = model(p) - y; J = jac(p); g = (J.' * r).';
if meth <= 2 % (scaled) GD + Armijo backtracking
d = -(Dsc.^2) .* g; al = min(amax, 2*al);
while cost(p + al*d) > cost(p) + 1e-4*al*(g*d.'), al = al/2; end
pn = p + al*d;
elseif meth == 3 % Gauss-Newton
pn = p - (J \ r).'; % least-squares solve, J'J never formed
else % Levenberg-Marquardt
H = J.'*J;
while true
pn = p - ((H + lam*diag(diag(H))) \ g.').';
if cost(pn) < cost(p), lam = lam/10; break; end
lam = lam*10; if lam > 1e12, pn = p; break; end
end
end
it = it + 1; step = norm(pn - p); p = pn; h(end+1) = cost(p);
if step < 1e-9, break; end
end
hist{s, meth} = h;
fprintf('%-16s %5d its A = %7.4f t0 = %.4f us sg = %.4f us f = %.4f MHz |r| = %.4f\n', ...
nm{meth}, it, p(1), p(2), abs(p(3)), abs(p(4)), sqrt(2*cost(p)));
if s == 1 && meth == 4, p_hat = p; end
end
end
fprintf('expected |r| for a correct fit: sn*sqrt(n) = %.3f\n', sn*sqrt(numel(t)));
%% 3. Precision at the solution and conditioning
J = jac(p_hat); H = J.'*J; sd = sqrt(diag(sn^2 * (H \ eye(4))));
fprintf('\nlinearized std (CRLB at the fit): A %.4f, t0 %.2f ns, sg %.2f ns, f %.4f MHz\n', sd(1), 1e3*sd(2), 1e3*sd(3), sd(4));
Dg = diag(1 ./ sqrt(diag(H)));
fprintf('kappa(J''J) = %.3g in (1, us, us, MHz) units; %.3g after column scaling\n', cond(H), cond(Dg*H*Dg));
Hs = diag([1 1e-6 1e-6 1e6]) * H * diag([1 1e-6 1e-6 1e6]);
fprintf('kappa(J''J) = %.3g if the same fit is written in SI units (1, s, s, Hz)\n', cond(Hs));
%% 4. Free carrier phase removes the cycle ambiguity in t0
modelp = @(q) q(1) * exp(-(t-q(2)).^2/(2*q(3)^2)) .* cos(2*pi*q(4)*(t-q(2)) + q(5));
costp = @(q) 0.5 * sum((modelp(q) - y).^2);
q = [starts{2} 0]; lam = 1e-3;
for it = 1:200
r = modelp(q) - y; Jq = zeros(numel(t), 5);
for k = 1:5, e = zeros(1,5); e(k) = 1e-7; Jq(:,k) = (modelp(q+e) - modelp(q-e)) / 2e-7; end
H = Jq.'*Jq; g = Jq.'*r;
while true
qn = q - ((H + lam*diag(diag(H))) \ g).';
if costp(qn) < costp(q), lam = lam/10; break; end
lam = lam*10; if lam > 1e12, qn = q; break; end
end
if norm(qn - q) < 1e-10, q = qn; break; end
q = qn;
end
fprintf('free-phase LM from the bad start: t0 = %.4f us, phase = %.3f rad, |r| = %.4f (%d its)\n', q(2), angle(exp(1j*q(5))), sqrt(2*costp(q)), it);
%% 5. Figure
figure('Position', [100 100 1050 420]);
subplot(1,2,1);
cols = {[0.6 0.6 0.6], [0.85 0.55 0], [0 0.3 0.8], [0.8 0.2 0.5]}; hh = zeros(1,4);
for meth = 1:4
hh(meth) = semilogy(1:numel(hist{1,meth}), hist{1,meth} - min(hist{1,4}) + 1e-12, 'Color', cols{meth}, 'LineWidth', 1.5); hold on;
end
set(gca, 'XScale', 'log'); grid on; xlim([1 2e4]);
xlabel('iteration'); ylabel('cost - final cost');
legend(hh, {'GD + Armijo (raw units)', 'GD + Armijo (Jacobi scaled)', 'Gauss-Newton', 'Levenberg-Marquardt'}, 'Location', 'southwest');
title('good start: linear vs quadratic convergence');
subplot(1,2,2);
plot(t, y, 'Color', [0.6 0.6 0.6]); hold on;
plot(t, model(p_hat), 'b', 'LineWidth', 1.5);
plot(t, model(starts{2}), 'r--');
xlabel('time (\mus)'); ylabel('amplitude'); grid on; xlim([1.4 2.6]);
legend('data', 'LM fit (good start)', 'bad start model', 'Location', 'southwest');
title('echo, fit, and a start that lands half a period late');
What to look for: good start: GD 10 828 iterations raw, 51 scaled, Gauss–Newton 13, LM 12, all at \(t_0\) = 1.9997 µs with \(\lvert\mathbf{r}\rvert\) = 0.520 (noise level 0.502); linearized std of \(t_0\) 0.69 ns; \(\kappa = 1.82 \times 10^3\) raw, 3.73 scaled, \(2.1 \times 10^{22}\) in SI units. Bad start (+100 ns): scaled GD, Gauss–Newton and LM all converge to \(A\) = −0.75, \(t_0\) = 2.106 µs, \(\lvert\mathbf{r}\rvert\) = 1.29; unscaled small-step GD happens to reach the right answer; the free-phase model recovers \(t_0\) = 1.994 µs.
m14_cycle_skipping.mRF, envelope and low-passed misfits of a 5 MHz pulse from sampled signals; local minima; GD from 121 starts; envelope-then-RF schedule; the FWI travel-time criterion · change f0, sg, fl · runtime ≈ 3 s
%% m14_cycle_skipping.m -- why RF misfits trap gradient descent, and how to widen the basin
% Purpose : delay estimation by minimizing L(tau) = sum (d(t) - s(t - tau))^2 for a 5 MHz
% Gaussian pulse (60 % bandwidth). Compares three misfits computed numerically
% on sampled signals: RF, envelope (|analytic signal|, FFT Hilbert), and RF after
% a Gaussian low-pass at 1.5 or 1.0 MHz. Lists the local minima, runs gradient
% descent from 121 starting delays, and shows that a multiscale schedule (envelope
% first, then RF) converges from starts the RF misfit alone cannot handle. Ends with
% the travel-time form of the criterion used in full-waveform inversion.
% Change : f0, sg, the low-pass corner fl, the starting delays.
% Expect : RF local minima at 0, +-194, +-388 ns (just inside multiples of T0 = 200 ns);
% RF basin |tau0| < 97 ns; envelope converges from every start (no local minima);
% low-pass at 1.5 MHz: basin about +-160 ns at -29 dB of the pulse energy;
% envelope first, then RF: converges from every start with RF curvature (32x
% the envelope curvature at the minimum).
% Base MATLAB only; runs in MATLAB R2019b+ and GNU Octave 8. Runtime about 5 s.
rng(1);
%% 1. Signals
f0 = 5e6; sg = 0.125e-6; % centre frequency (Hz), envelope std (s)
fs = 1e9; t = (-3e-6:1/fs:3e-6).'; % fine grid so delays are continuous (1 ns)
nT = numel(t); nf = 2^nextpow2(2*nT);
fax = [0:nf/2-1, -nf/2:-1].' * fs / nf; % FFT frequency axis (Hz)
pulse = @(tau) exp(-(t-tau).^2/(2*sg^2)) .* cos(2*pi*f0*(t-tau));
fl = 1.5e6; % Gaussian low-pass corner (Hz)
lowp = @(x) real(ifft(fft(x, nf) .* exp(-fax.^2/(2*fl^2))));
H = @(x) ifft(fft(x, nf) .* (1 + sign(fax))); % analytic signal (x + j Hilbert x)
crop = @(x) x(1:nT);
envl = @(x) abs(crop(H(x)));
d = pulse(0); % data: true delay 0
fprintf('low-pass at %.1f MHz keeps %.1f dB of the pulse energy\n', fl/1e6, 10*log10(sum(crop(lowp(d)).^2)/sum(d.^2)));
%% 2. Misfit curves, computed numerically from the sampled signals
% L(tau) = sum (a - b_tau)^2 = sum a^2 + sum b^2 - 2 R_ab(tau): all shifts at once by FFT
% correlation (a shift commutes with the low-pass filter and with taking the envelope).
xc = @(a, b) real(ifft(fft(a, nf) .* conj(fft(b, nf))));
lagk = -600:600; taus = lagk * 1e-9; % 1 ns lags
mis = @(a, b) sum(a.^2) + sum(b.^2) - 2 * subsref(xc(b, a), struct('type', '()', 'subs', {{mod(lagk, nf) + 1}})).';
dE = envl(d); dL = crop(lowp(d));
fl2 = 1.0e6; lowp2 = @(x) real(ifft(fft(x, nf) .* exp(-fax.^2/(2*fl2^2)))); dL2 = crop(lowp2(d));
L = [mis(d, d); mis(dE, dE); mis(dL, dL); mis(dL2, dL2)];
L = L ./ (2*[sum(d.^2); sum(dE.^2); sum(dL.^2); sum(dL2.^2)]); % normalize by 2 x energy: L = 1 - R(tau)/R(0)
fprintf('low-pass at %.1f MHz keeps %.1f dB of the pulse energy\n', fl2/1e6, 10*log10(sum(dL2.^2)/sum(d.^2)));
names = {'RF', 'envelope', 'LP 1.5 MHz', 'LP 1.0 MHz'};
for m = 1:4
loc = find(L(m,2:end-1) < L(m,1:end-2) & L(m,2:end-1) < L(m,3:end)) + 1;
fprintf('%-11s local minima (ns): %s\n', names{m}, sprintf('%g ', round(taus(loc)*1e9)));
end
mx = find(L(1,2:end-1) > L(1,1:end-2) & L(1,2:end-1) > L(1,3:end)) + 1;
fprintf('RF local maxima nearest 0 (basin edges): %s ns; T0/2 = %.0f ns\n', sprintf('%g ', round(taus(mx(abs(taus(mx)) < 150e-9))*1e9)), 0.5e9/f0);
%% 3. Gradient descent from many starts on the tabulated misfits
% dL/dtau by central differences on the 1 ns table; step = 1/L''(0) (Newton step at the minimum)
dLdt = zeros(size(L)); dLdt(:,2:end-1) = (L(:,3:end) - L(:,1:end-2)) / 2e-9;
curv = (L(:,602) - 2*L(:,601) + L(:,600)) / 1e-18; % L'' at tau = 0
fprintf('curvature at the minimum, RF / envelope = %.1f (theory 1 + 2 (2 pi f0 sg)^2 = %.1f)\n', curv(1)/curv(2), 1 + 2*(2*pi*f0*sg)^2);
starts = (-600:10:600) * 1e-9;
plan = {1, 2, 3, 4, [2 1]}; % misfits used in sequence
lbl = {'RF', 'envelope', 'LP 1.5 MHz', 'LP 1.0 MHz', 'envelope -> RF'};
final = zeros(numel(plan), numel(starts));
for m = 1:numel(plan)
tau = starts;
for q = plan{m}
for it = 1:400
g = interp1(taus, dLdt(q,:), min(max(tau, taus(2)), taus(end-1)));
tau = min(max(tau - g / curv(q), taus(1)), taus(end)); % stay on the table
end
end
final(m, :) = tau;
end
ok = abs(final) < 2e-9;
for m = 1:numel(plan)
k0 = find(starts == 0); lo = k0; hi = k0;
while lo > 1 && ok(m, lo-1), lo = lo - 1; end
while hi < numel(starts) && ok(m, hi+1), hi = hi + 1; end
fprintf('%-15s converges to 0 from %3d of %d starts; contiguous basin [%4.0f, %4.0f] ns\n', ...
lbl{m}, sum(ok(m,:)), numel(starts), starts(lo)*1e9, starts(hi)*1e9);
end
%% 4. Travel-time form of the criterion (transmission FWI / tomography)
% the starting model must predict the travel time within T/2: |dt| = L |dc| / c^2 < 1/(2 f)
fprintf('\nmax tolerable starting error |dc| (m/s) for c = 1500 m/s\n f (MHz) | L = 50 mm | L = 100 mm | L = 200 mm\n');
for f = [0.5 1 2 5]
fprintf(' %5.1f | %8.1f | %9.1f | %9.1f\n', f, 1500^2./(2*f*1e6*[0.05 0.1 0.2]));
end
%% 5. Figures
figure('Position', [100 100 1050 420]);
subplot(1,2,1);
h = plot(taus*1e9, L(1,:), 'b', taus*1e9, L(2,:), 'r', taus*1e9, L(3,:), 'g');
set(h(3), 'Color', [0 0.55 0.4]); set(h, 'LineWidth', 1.4); grid on;
xlabel('trial delay \tau (ns), true delay 0'); ylabel('misfit / (2 x signal energy)');
xlim([-600 600]); legend(h, names(1:3), 'Location', 'southeast'); title('RF misfit: a minimum every period');
subplot(1,2,2);
h2 = plot(starts*1e9, final(1,:)*1e9, 'b.-', starts*1e9, final(3,:)*1e9, 'g.-', starts*1e9, final(5,:)*1e9 + 8, 'k.-');
set(h2(2), 'Color', [0 0.55 0.4]); grid on; xlim([-600 600]); xlabel('starting delay (ns)'); ylabel('converged delay (ns)');
legend(h2, {'RF', 'LP 1.5 MHz', 'envelope -> RF (offset +8 ns)'}, 'Location', 'northwest');
title('where gradient descent ends');
What to look for: RF minima at 0, ±194, ±388, ±583 ns and basin edges at ±97 ns; low-pass at 1.5 MHz keeps −29.2 dB, at 1.0 MHz −43.5 dB, with minima at ±327 and ±486 ns; RF converges from 19 of 121 starts (±90 ns), LP 1.5 MHz from 33 (±160 ns), LP 1.0 MHz from 49 (±240 ns), envelope and envelope → RF from all 121; curvature ratio 31.8 against theory 31.8; the \(\lvert\Delta c\rvert\) table (11.2 m/s at 0.5 MHz over 200 mm).
m14_complex_step_ad.mFinite-difference vs complex-step error vs h; dual-number forward mode; reverse mode by hand; two silent complex-step failures; Wirtinger gradient check and the conjugate-step bug · change x0, the test functions · runtime ≈ 1 s
%% m14_complex_step_ad.m -- finite differences, complex step, dual numbers, reverse mode, Wirtinger
% Purpose : 1) relative error of forward/central differences and of the complex-step
% derivative Im f(x + i h)/h versus h, for f(x) = e^x / sqrt(sin^3 x + cos^3 x)
% at x0 = 1.5 (a standard test function in the complex-step literature);
% 2) forward-mode AD with dual numbers [value, derivative] built from anonymous
% functions; 3) reverse mode by hand on y = ln(x1) + x1 x2 - sin(x2) at (2, 5);
% 4) two ways the complex step silently fails (abs and the ' operator);
% 5) Wirtinger gradient dL/dw* = -A^H (y - A w) for L = ||y - A w||^2 checked
% against finite differences, and what happens if you step along its conjugate.
% Change : x0, the step list, the test functions.
% Expect : best forward difference 1e-8 to 1e-9 relative error near h = 1e-8; best central
% about 1e-11 near h = 3e-6; complex step at machine precision for every
% h <= 1e-8 (including 1e-20 and 1e-100); dual number exact; reverse-mode
% gradient (5.5, 1.7163); Wirtinger check about 1e-9; the conjugate step diverges.
% Base MATLAB only; runs in MATLAB R2019b+ and GNU Octave 8. Runtime about 1 s.
rng(1);
%% 1. Finite differences vs complex step
f = @(x) exp(x) ./ sqrt(sin(x).^3 + cos(x).^3);
x0 = 1.5;
df_exact = f(x0) * (1 - (3*sin(x0)^2*cos(x0) - 3*cos(x0)^2*sin(x0)) / (2*(sin(x0)^3 + cos(x0)^3)));
fprintf('f(1.5) = %.6f, exact f''(1.5) = %.12f\n', f(x0), df_exact);
hs = 10.^(-1:-0.25:-20);
f0v = f(x0 + 0*hs); % same vector code path as f(x0+hs): avoids a 1-ulp mismatch
efd = abs((f(x0+hs) - f0v) ./ hs - df_exact) / abs(df_exact);
ecd = abs((f(x0+hs) - f(x0-hs)) ./ (2*hs) - df_exact) / abs(df_exact);
ecs = abs(imag(f(x0 + 1i*hs)) ./ hs - df_exact) / abs(df_exact);
pfd = max(efd, 1e-17); pcd = max(ecd, 1e-17); pcs = max(ecs, 1e-17); % floor zeros for the log plot
fprintf(' h | forward diff | central diff | complex step\n');
for h = [1e-2 1e-4 1e-6 1e-8 1e-10 1e-12 1e-16 1e-20]
k = find(abs(log10(hs) - log10(h)) < 1e-9);
fprintf(' %7.0e | %8.1e | %8.1e | %8.1e\n', h, efd(k), ecd(k), ecs(k));
end
[m1, i1] = min(efd); [m2, i2] = min(ecd);
fprintf('best forward h = %.0e (err %.1e; sqrt(eps) = %.1e); best central h = %.0e (err %.1e; eps^(1/3) = %.1e)\n', ...
hs(i1), m1, sqrt(eps), hs(i2), m2, eps^(1/3));
fprintf('complex step with h = 1e-100: relative error %.1e\n', abs(imag(f(x0 + 1e-100i))/1e-100 - df_exact)/df_exact);
%% 2. Forward mode with dual numbers a = [value, derivative]
dmul = @(a, b) [a(1)*b(1), a(1)*b(2) + a(2)*b(1)];
ddiv = @(a, b) [a(1)/b(1), (a(2)*b(1) - a(1)*b(2)) / b(1)^2];
dadd = @(a, b) a + b;
dexp = @(a) [exp(a(1)), exp(a(1))*a(2)];
dsin = @(a) [sin(a(1)), cos(a(1))*a(2)];
dcos = @(a) [cos(a(1)), -sin(a(1))*a(2)];
dsqrt = @(a) [sqrt(a(1)), a(2) / (2*sqrt(a(1)))];
dcube = @(a) dmul(dmul(a, a), a);
fdual = @(a) ddiv(dexp(a), dsqrt(dadd(dcube(dsin(a)), dcube(dcos(a)))));
r = fdual([x0, 1]); % seed dx/dx = 1
fprintf('\ndual number: f = %.12f, f'' = %.15f, relative error %.1e\n', r(1), r(2), abs(r(2) - df_exact)/df_exact);
%% 3. Reverse mode by hand: y = ln(x1) + x1*x2 - sin(x2) at (2, 5)
x1 = 2; x2 = 5;
v1 = log(x1); v2 = x1*x2; v3 = sin(x2); v4 = v1 + v2; y = v4 - v3; % forward sweep (the tape)
yb = 1; % reverse sweep: adjoints
v4b = yb; v3b = -yb; v1b = v4b; v2b = v4b;
x1b = v1b/x1 + v2b*x2; x2b = v2b*x1 + v3b*cos(x2);
fprintf('reverse mode: y = %.4f, grad = (%.4f, %.4f); analytic (1/x1 + x2, x1 - cos x2) = (%.4f, %.4f)\n', ...
y, x1b, x2b, 1/x1 + x2, x1 - cos(x2));
fprintf(' one reverse sweep gave both partials; forward mode needs one sweep per input (2 here)\n');
%% 4. Silent failures of the complex step
g1 = @(x) x .* abs(x - 1); % abs is not analytic: abs(z) returns |z|, a real number
fprintf('\nx|x-1| at 1.5: exact 2.0, complex step %.4f, dual %.4f\n', imag(g1(1.5 + 1e-20i))/1e-20, ...
subsref(dmul([1.5 1], [abs(0.5), sign(0.5)*1]), struct('type', '()', 'subs', {{2}})));
g2 = @(x) x' * x; % ' is the CONJUGATE transpose: x'*x = |x|^2 for complex x
fprintf('x''*x at 1.5: exact 3.0, complex step %.4f; with .'' : %.4f\n', imag(g2(1.5 + 1e-20i))/1e-20, ...
imag((1.5 + 1e-20i).' * (1.5 + 1e-20i))/1e-20);
%% 5. Wirtinger gradient for a real loss of complex parameters
A = (randn(8,4) + 1i*randn(8,4)) / sqrt(2); y = (randn(8,1) + 1i*randn(8,1)) / sqrt(2);
L = @(w) real((y - A*w)' * (y - A*w));
w = (randn(4,1) + 1i*randn(4,1)) / sqrt(2);
g = -A' * (y - A*w); % dL/dw* (the steepest-ascent direction is 2*g in R^8)
h = 1e-6; gfd = zeros(4,1);
for k = 1:4
e = zeros(4,1); e(k) = h;
gfd(k) = (L(w+e) - L(w-e))/(2*h) + 1i*(L(w+1i*e) - L(w-1i*e))/(2*h); % dL/dRe + i dL/dIm
end
fprintf('\nWirtinger check: max |(dL/dRe + i dL/dIm) - 2 dL/dw*| / max|.| = %.1e\n', max(abs(gfd - 2*g))/max(abs(gfd)));
mu = 0.5 / max(eig(A'*A)); wa = zeros(4,1); wb = zeros(4,1);
for it = 1:200
wa = wa - mu * (-A' * (y - A*wa)); % correct: step along -dL/dw*
wb = wb - mu * conj(-A' * (y - A*wb)); % bug: step along -dL/dw = -conj(dL/dw*)
end
wls = A \ y;
fprintf('after 200 steps: L(correct) = %.4f (LS optimum %.4f); L(conjugated step) = %.4g\n', L(wa), L(wls), L(wb));
%% 6. Figure
figure('Position', [100 100 640 440]);
loglog(hs, pfd, 'b.-', hs, pcd, 'r.-', hs, pcs, 'k.-'); hold on;
loglog([1e-20 1e-1], [1 1]*eps, 'k:');
set(gca, 'XDir', 'reverse'); grid on; ylim([1e-17 10]);
xlabel('step h'); ylabel('relative error of f''(1.5)');
legend('forward difference', 'central difference', 'complex step', 'machine epsilon', 'Location', 'northwest');
title('truncation error falls with h, round-off grows as eps/h; the complex step has no subtraction');
What to look for: best forward difference at \(h = 10^{-8}\) (error \(2.6 \times 10^{-9}\) here; typically ~\(10^{-8}\)), best central at \(h = 3 \times 10^{-6}\) (\(6.4 \times 10^{-12}\)), complex step 0 to \(2.2 \times 10^{-16}\) for every \(h \le 10^{-8}\) including \(10^{-100}\); dual number exact; reverse-mode gradient (5.5000, 1.7163); complex step gives 0.5 instead of 2 for \(x\lvert x - 1\rvert\) and 0 instead of 3 for x'*x; Wirtinger check \(2.2 \times 10^{-10}\); the conjugated step reaches \(\mathcal{L} = 2.4 \times 10^{62}\).
m14_autofocus_sound_speed.mPlane-wave IQ channel data at c = 1480 m/s; four focus metrics vs assumed ĉ with exact chain-rule derivatives; projected gradient ascent from five starts; 1 vs 5 angles · change c_true, z, angle_sets · runtime ≈ 15 s
%% m14_autofocus_sound_speed.m -- sound speed by gradient ascent on a focus metric
% Purpose : simulate plane-wave IQ channel data (64 elements, 5 MHz) from a point
% target in speckle in a medium with c_true = 1480 m/s, beamform at an
% assumed speed ch, and estimate c by gradient ascent on four metrics:
% energy : sum of |b|^2 over an ROI fixed in two-way time (coherently compounded)
% sharp : P*sum(I.^2)/sum(I)^2 over the same ROI
% envz : |b|^2 at a pixel fixed in depth (x = 0, z = 30 mm)
% rfz : real part of b (the RF value) at that fixed pixel
% The derivative dM/dch is computed exactly by the chain rule (forward mode):
% d/dch [IQ(tau) e^{j w0 tau}] = (IQ'(tau) + j w0 IQ(tau)) e^{j w0 tau} dtau/dch.
% Change : c_true, z, angles_deg (try a single 0 deg plane wave), starts, the scene.
% Expect : 5 angles: energy peaks at 1484-1486 m/s (bias about +5 m/s) and every start
% in 1380-1720 converges there; rfz oscillates with period ch^2/(2 z f0)
% (7.3 m/s here) and every start stops on its nearest local maximum (cycle
% skipping). 1 angle: energy is nearly flat and peaks far from c_true.
% Base MATLAB only; runs in MATLAB R2019b+ and GNU Octave 8. Runtime about 10 s.
rng(1);
%% 1. Geometry, pulse and scatterers
c_true = 1480; % true sound speed (m/s)
z = 30e-3; % target depth (m)
f0 = 5e6; w0 = 2*pi*f0; % centre frequency (Hz)
sig = 0.125e-6; % Gaussian envelope std (s): -6 dB bandwidth 3 MHz (60 %)
fs = 40e6; % IQ sampling rate (Hz)
N = 64; pitch = 0.3e-3; % elements, pitch (m): aperture 19.2 mm, F/1.56 at 30 mm
xe = ((0:N-1) - (N-1)/2) * pitch; % 1 x N element positions (m)
K = 500; % speckle scatterers
sx = [0; (rand(K,1) - 0.5) * 8e-3]; % lateral (m), first one is the point
sz = [z; z + (rand(K,1) - 0.5) * 3e-3]; % depth (m)
amp = [3; 0.25 * (randn(K,1) + 1j*randn(K,1)) / sqrt(2)];
angle_sets = {0, (-8:4:8)}; % plane-wave angles (deg)
for set_id = 1:2
th = angle_sets{set_id} * pi/180; A = numel(th);
%% 2. IQ channel data: IQ(t) = sum_k a_k g(t - t_k) e^{-j w0 t_k}
tk = zeros(K+1, N, A);
for a = 1:A
tk(:,:,a) = (sz*cos(th(a)) + sx*sin(th(a)))/c_true + sqrt((sx - xe).^2 + sz.^2)/c_true;
end
t0 = min(tk(:)) - 1.5e-6; T = ceil((max(tk(:)) + 1.5e-6 - t0) * fs);
tg = t0 + (0:T-1).' / fs; % T x 1 fast time (s)
IQ = zeros(T, N, A);
for a = 1:A
for e = 1:N
tt = tk(:, e, a).'; % 1 x (K+1)
IQ(:, e, a) = exp(-(tg - tt).^2 / (2*sig^2)) * (amp .* exp(-1j*w0*tt.'));
end
end
%% 3. Beamformer with exact derivative (anonymous functions only)
% ROI in time coordinates: pixel (x, t), depth at assumed speed = ch*t/2
nx = 21; nt = 25;
[PX, PT] = meshgrid(((0:nx-1) - (nx-1)/2) * 0.15e-3, 2*z/c_true + ((0:nt-1) - (nt-1)/2) * 50e-9);
Px = PX(:); Pt = PT(:); P = numel(Px);
U = @(tau) (tau - t0) * fs;
I0 = @(tau) min(max(floor(U(tau)), 0), T-2);
% core: q0 = IQ at the sample before tau, dq = next minus q0, fr = fraction, E = e^{j w0 tau}
core = @(q0, dq, fr, E, dt) [sum((q0 + dq.*fr) .* E, 2), sum((dq*fs + 1j*w0*(q0 + dq.*fr)) .* E .* dt, 2)];
bfi = @(k, fr, tau, dt) core(IQ(k), IQ(k+1) - IQ(k), fr, exp(1j*w0*tau), dt); % k = linear index
bfa = @(tau, dt, a) bfi(I0(tau) + 1 + (0:N-1)*T + (a-1)*T*N, U(tau) - I0(tau), tau, dt);
% time-ROI delays and d tau / d ch
tauT = @(ch, a) Pt/2*cos(th(a)) + Px*sin(th(a))/ch + sqrt((Px - xe).^2/ch^2 + (Pt/2).^2);
dtauT = @(ch, a) -Px*sin(th(a))/ch^2 - ((Px - xe).^2/ch^3) ./ sqrt((Px - xe).^2/ch^2 + (Pt/2).^2);
% fixed-depth pixel (x = 0, z)
tauZ = @(ch, a) (z*cos(th(a)) + sqrt(xe.^2 + z^2)) / ch;
BT = @(ch) cell2mat(arrayfun(@(a) bfa(tauT(ch,a), dtauT(ch,a), a), 1:A, 'UniformOutput', false));
BZ = @(ch) cell2mat(arrayfun(@(a) bfa(tauZ(ch,a), -tauZ(ch,a)/ch, a), 1:A, 'UniformOutput', false));
bsum = @(Bm) [sum(Bm(:,1:2:end), 2), sum(Bm(:,2:2:end), 2)]; % compound angles: [b, db]
% metrics: each returns [M, dM/dch]
mE = @(bd) [sum(abs(bd(:,1)).^2), sum(2*real(conj(bd(:,1)).*bd(:,2)))];
mS = @(bd) P * [sum(abs(bd(:,1)).^4) / sum(abs(bd(:,1)).^2)^2, ...
(sum(4*abs(bd(:,1)).^2 .* real(conj(bd(:,1)).*bd(:,2))) * sum(abs(bd(:,1)).^2) ...
- 2*sum(abs(bd(:,1)).^4) * sum(2*real(conj(bd(:,1)).*bd(:,2)))) / sum(abs(bd(:,1)).^2)^3];
metric = {@(ch) mE(bsum(BT(ch))), @(ch) mS(bsum(BT(ch))), ...
@(ch) mE(bsum(BZ(ch))), @(ch) real(bsum(BZ(ch)))};
names = {'energy', 'sharp', 'envz', 'rfz'};
S0 = [40 40 8 1.5]; % initial trial step of the line search (m/s)
cmin = 1350; cmax = 1750; % box constraint: every step is projected onto [cmin, cmax]
%% 4. Derivative check at 1540 m/s (exact chain rule vs central difference)
if set_id == 2
for m = 1:4
r = metric{m}(1540); h = 1e-3;
fd = (metric{m}(1540+h) - metric{m}(1540-h)) / (2*h);
fprintf('dM/dc check %-6s chain rule / central difference = %.6f\n', names{m}, r(2)/fd(1));
end
end
%% 5. Sweep and gradient ascent from several starts
fprintf('\n%d plane wave(s): c_true = %d m/s, z = %d mm\n', A, c_true, round(z*1e3));
fprintf('metric | argmax (m/s) | gradient ascent: start -> end (iterations)\n');
starts = [1380 1440 1540 1620 1720];
cs_roi = 1350:5:1750; cs_pix = 1350:1:1750;
curves = cell(1,4); ends = zeros(4, numel(starts));
for m = 1:4
if m <= 2, cs = cs_roi; else, cs = cs_pix; end
M = zeros(size(cs)); for i = 1:numel(cs), r = metric{m}(cs(i)); M(i) = r(1); end
curves{m} = [cs; M]; [~, im] = max(M);
s_out = '';
for j = 1:numel(starts)
c = starts(j); r = metric{m}(c); n = 0;
while n < 60
g = r(2); if g == 0, break; end
s = S0(m); ok = false;
while s > 0.05
cn = min(max(c + sign(g)*s, cmin), cmax); rn = metric{m}(cn); % projected step
if cn ~= c && rn(1) >= r(1) + 1e-4*abs(cn-c)*abs(g), c = cn; r = rn; ok = true; break; end
s = s/2;
end
if ~ok, break; end
n = n + 1;
end
ends(m, j) = c;
s_out = [s_out sprintf('%d->%.1f(%d) ', starts(j), c, n)];
end
fprintf('%-6s | %7.1f | %s\n', names{m}, cs(im), s_out);
end
fprintf('RF-at-fixed-depth period predicted ch^2/(2 z f0) = %.1f m/s at ch = c_true\n', c_true^2/(2*z*f0));
if set_id == 1, curves1 = curves; ends1 = ends; else, curves5 = curves; ends5 = ends; end
end
%% 6. Figures
figure('Position', [80 80 1100 460]);
cols = {[0 0.3 0.8], [0.8 0.2 0.5], [0.85 0.55 0], [0 0.55 0.4]};
for set_id = 1:2
subplot(1,2,set_id);
if set_id == 1, cv = curves1; en = ends1; ttl = '1 plane wave (0 deg)'; else, cv = curves5; en = ends5; ttl = '5 plane waves (-8:4:8 deg)'; end
h = zeros(1,4);
for m = 1:4
y = cv{m}(2,:); y = y / max(abs(y));
h(m) = plot(cv{m}(1,:), y, 'Color', cols{m}, 'LineWidth', 1.3); hold on;
yi = interp1(cv{m}(1,:), y, en(m,:));
plot(en(m,:), yi, 'o', 'Color', cols{m}, 'MarkerSize', 7, 'LineWidth', 1.5);
end
plot([c_true c_true], [-1 1.05], 'k--');
xlim([1350 1750]); ylim([-1 1.05]); grid on;
xlabel('assumed sound speed c_h (m/s)'); ylabel('metric / max |metric|');
title([ttl ': circles = gradient-ascent end points']);
legend(h, {'energy (time ROI)', 'sharpness (time ROI)', '|b|^2 at fixed depth', 'Re b at fixed depth'}, 'Location', 'southwest');
end
What to look for: derivative check ratios 1.000000; with 5 angles the compounded energy peaks at 1485 m/s and all five starts end at 1484.1 m/s, sharpness at 1482.5 m/s except the 1720 start; with 1 angle the energy peaks at 1550 m/s (wrong) while sharpness still works for this point-in-speckle scene; RF at fixed depth stops near every start (1380.7, 1445.9, 1541.5, …), period 7.3 m/s; envelope at fixed depth only helps within about ±20 m/s.
14.13Self-check
Q1κ = 400, tolerance 10⁻⁶. How many iterations for GD with the optimal step, and for tuned heavy ball (rate only)?
GD: \(\ln(10^6)/\ln(401/399)\) = 2763. Heavy ball: \(\sqrt\kappa\) = 20, \(\ln(10^6)/\ln(21/19)\) = 138 (expect somewhat more from the double-root transient). Ratio \(\approx\sqrt\kappa\) = 20.
Q2You run GD with α = 2.05/L. What happens to the stiff mode after 50 steps?
Its factor is \(1 - 2.05 = -1.05\): it alternates sign and grows by \(1.05^{50}\) = 11.5. Any \(\alpha > 2/L\) diverges, however small the excess.
Q3At 3 MHz, what is the RF delay basin, and how far apart are the ripples of an RF-at-fixed-depth autofocus metric at 30 mm (ĉ = 1540 m/s)?
Basin \(\approx \pm T_0/2\) = ±167 ns. Ripple period \(\hat c^2/(2zf_0) = 1540^2/(2 \times 0.03 \times 3 \times 10^6)\) = 13.2 m/s. Lower frequency widens both, which is the multiscale idea.
Q4A transmission path of 200 mm, c = 1500 m/s, f = 0.5 MHz. What starting-model error in c avoids cycle skipping?
\(\lambda\) = 3 mm, \(L/\lambda\) = 66.7, so \(\lvert\Delta c\rvert/c < 1/(2 \times 66.7)\) = 0.75 %: \(\lvert\Delta c\rvert\) < 11.25 m/s. Equivalently \(\lvert\Delta t\rvert = L\Delta c/c^2 < T/2\) = 1 µs.
Q5The complex step applied to g(x) = x·|x − 1| at x = 1.5 returns what, and what is the true derivative?
abs(z − 1) returns the real modulus, so \(\mathrm{Im}\,g(x + ih)/h = \lvert x - 1\rvert\) = 0.5. The true derivative is \(2x - 1\) = 2. A dual number (or AD) with abs overloaded as \(\mathrm{sign}(a)\,\dot a\) returns 2.
Q6L(z) = |z − a|². Which step μ in z ← z − μ ∂L/∂z* lands on a in one step, and what does the conjugated update do?
\(\partial\mathcal{L}/\partial z^* = z - a\), so \(\mu\) = 1 lands exactly on \(a\) (in real coordinates the gradient is \(2(z - a)\) and the Newton step is 1/2 of it). The conjugated update \(z \leftarrow z - (z - a)^*\) moves the imaginary part the wrong way: from \(z = a + i\) it goes to \(a + 2i\).
Q7Why does the total energy of a single plane-wave image fail as an autofocus metric while the compounded energy over five angles works?
Receive DAS over a region nearly preserves the channel energy whatever the focusing law, so \(\sum\lvert b\rvert^2\) is almost flat in \(\hat c\) (the demo's 1-angle curve peaks 60–100 m/s high). Summing angles coherently before squaring adds cross terms that are maximal only when the angle images align, which happens at the right \(\hat c\): the 5-angle energy peaks within 2–10 m/s of the truth.
14.14Figures from the literature
Published figures with real data or benchmark runs that show the claims of this module outside a toy model. Links only; no figures are reproduced.
Differentiable-beamforming autofocus in vivo. Fig. 5 shows CMPE autofocusing on rat liver (B-modes before and after, phase-error maps, channel data and coherence factor), with the mean common-midpoint phase error falling from 55.8 to 42.1 mrad; Fig. 7 compares estimated with calibrated sound speeds in simulation, bilayer and commercial phantoms. Look for the residual bias in Fig. 7: it is the "integrated metrics are biased" pitfall of 14.5 on real data.
W. Simson, L. Zhuang, B. N. Frey, S. J. Sanabria, J. J. Dahl, D. Hyun, "Ultrasound autofocusing: common midpoint phase error optimization via differentiable beamforming," arXiv:2410.03008 (2024, revised 2025; published in IEEE Trans. Med. Imaging, 2025, doi:10.1109/TMI.2025.3607875), Figs. 5 and 7 (arXiv numbering). doi:10.48550/arXiv.2410.03008The complex step against finite differences. Fig. 1 plots the relative derivative error versus step size for forward, central and complex-step differences on \(f(x) = e^x/\sqrt{\sin^3x + \cos^3x}\) at \(x\) = 1.5, the default of Demo 14.5. Compare the V-shaped finite-difference curves and the flat complex-step floor with the demo.
J. R. R. A. Martins, P. Sturdza, J. J. Alonso, "The complex-step derivative approximation," ACM Trans. Math. Softw. 29(3):245–262, 2003, Fig. 1. doi:10.1145/838250.838251Forward and reverse mode on the same example. Fig. 4 is the computational graph of \(y = \ln x_1 + x_1x_2 - \sin x_2\); Tables 2 and 3 evaluate the forward tangent and reverse adjoint sweeps at (2, 5). Figure 14.6 and the table in 14.6 reproduce the same numbers (5.5, 1.7163).
A. G. Baydin, B. A. Pearlmutter, A. A. Radul, J. M. Siskind, "Automatic differentiation in machine learning: a survey," J. Mach. Learn. Res. 18:1–43, 2018, Fig. 4 and Tables 2–3. jmlr.org/papers/v18/17-468Adam on benchmark training curves. Fig. 3 shows the training cost of a convolutional network on CIFAR-10 for Adam against AdaGrad and SGD with Nesterov momentum (with and without dropout); Fig. 1 does the same for logistic regression on MNIST and IMDB. Note that the advantage is in large stochastic problems, the regime where 14.3 recommends Adam, not in small smooth least-squares fits.
D. P. Kingma, J. Ba, "Adam: a method for stochastic optimization," ICLR 2015, arXiv:1412.6980, Figs. 1 and 3. doi:10.48550/arXiv.1412.698014.15Go deeper
- J. Nocedal & S. J. Wright, Numerical Optimization, 2nd ed., Springer, 2006. Line searches (ch. 3), Newton and quasi-Newton, Gauss–Newton and Levenberg–Marquardt (ch. 10).
- S. Boyd & L. Vandenberghe, Convex Optimization, Cambridge University Press, 2004. Convexity, gradient and Newton methods with convergence proofs.
- B. T. Polyak, "Some methods of speeding up the convergence of iteration methods," USSR Comput. Math. Math. Phys., 1964 (heavy ball); Y. Nesterov, Soviet Math. Dokl., 1983 (accelerated gradient).
- D. P. Kingma & J. Ba, "Adam: A method for stochastic optimization," ICLR, 2015.
- A. G. Baydin, B. A. Pearlmutter, A. A. Radul & J. M. Siskind, "Automatic differentiation in machine learning: a survey," JMLR, 2018.
- A. Griewank & A. Walther, Evaluating Derivatives, 2nd ed., SIAM, 2008. Reverse mode, its cost and checkpointing.
- W. Squire & G. Trapp, "Using complex variables to estimate derivatives of real functions," SIAM Review, 1998; J. R. R. A. Martins, P. Sturdza & J. J. Alonso, "The complex-step derivative approximation," ACM TOMS, 2003.
- D. H. Brandwood, "A complex gradient operator and its application in adaptive array theory," IEE Proc., 1983. The basis of \(\partial/\partial z^*\) gradients.
- C. Bunks, F. M. Saleck, S. Zaleski & G. Chavent, "Multiscale seismic waveform inversion," Geophysics, 1995; J. Virieux & S. Operto, "An overview of full-waveform inversion in exploration geophysics," Geophysics, 2009. Cycle skipping and multiscale strategies.
- R. A. Muller & A. Buffington, "Real-time correction of atmospherically degraded telescope images through image sharpening," JOSA, 1974.
- M. E. Anderson & G. E. Trahey, "The direct estimation of sound speed using pulse–echo ultrasound," JASA, 1998.
- W. Simson, L. Zhuang, S. J. Sanabria, N. Antil, J. J. Dahl & D. Hyun, "Differentiable beamforming for ultrasound autofocusing," in MICCAI 2023, Lecture Notes in Computer Science 14229, Springer, 2023 (doi:10.1007/978-3-031-43999-5_41; code: github.com/waltsims/dbua). Gradient-based sound-speed maps through a differentiable beamformer.