Scattering#
- class deepinv.physics.Scattering(img_width, receivers, transmitters, background_wavenumber=10.0, solver_config=None, box_length=1.0, wave_type='circular_wave', device='cpu', dtype=torch.complex128, verbose=False)[source]#
Bases:
PhysicsInverse scattering physics model in 2 dimensions.
For each of the \(i=1,\dots,T\) transmitters, the 2D forward model is given by inhomogeneous Helmholtz equation (see e.g., Soubies et al. [97]):
\[\nabla^2 u_i(\mathbf{r}) + k^2(\mathbf{r}) u_i(\mathbf{r}) = - (k^2(\mathbf{r}) - k^2_b) v_i(\mathbf{r}) \quad \mathbf{r} \in \mathbb{R}^2\]where \(u_i\) is the (unknown) scattered field, \(k_b\) is the (known scalar) wavenumber of the incident wave in the background medium, \(k(\mathbf{r})\) is the (unknown) spatially-varying wavenumber of the object to be recovered, and \(v_i\) is the incident field generated by the ith transmitter in the absence of the object. The total field (scattered + incident) is measured at \(R\) different receiver locations surrounding the object.
Parametrizing the unknown spatially-varying wavenumber as \(k^2(\mathbf{r}) = k_b^2 (x(\mathbf{r})+1)\), where \(k_b\) is the background wavenumber, and \(x = k^2/k_b^2 - 1\) is the scattering potential of the object to be recovered, the problem can be reformulated in the Lippmann-Schwinger integral equation form:
\[\begin{split}u_i &= g * \left( x \circ (u_i+v_i) \right) \\ y_i &= G_s \left( x \circ (u_i+v_i) \right)\end{split}\]where \(g(\mathbf{r}) = k_b^2 \frac{i}{4} H_0^1(k_b\|\mathbf{r}\|)\) is Greenβs function in 2D (normalized by \(k_b^2\)), \(y \in \mathbb{C}^{R}\) are the measurements at the receivers for the ith transmitter, and \(G_s\) denotes the convolution with Greenβs operator plus sampling at the \(R\) different receiver locations.
Tip
This parametrization ensures that the scattering potential \(x\) is dimensionless, and can be used for different physical modalities: In microwave tomography applications, the scattering potential is related to the objectβs relative permittivity \(\epsilon_r\) as \(x(\mathbf{r}) = \epsilon_r(\mathbf{r}) - 1\). In optical diffraction tomography, the scattering potential is related to the refractive index \(n\) as \(x(\mathbf{r}) = n^2(\mathbf{r}) - 1\).
Moreover, the wavenumber can be also provided in a dimensionless form by normalizing it with respect to the box length \(L\) as \(k_b = 2 \pi L / \lambda\), where \(\lambda\) is the wavelength of the incident wave.
All spatial quantities are discretized in a square grid of size
img_width x img_widthpixels, which is assumed to cover a square domain of size \(L \times L\) in space, where \(L\) is the box length.Note
The forward operator uses a linear solver to compute the total field for each transmitter by solving the Lippmann-Schwinger equation. The default solver configuration can be modified by passing a custom
deepinv.physics.Scattering.SolverConfigobject to the constructor. If the objectsβ contrast and/or the wavenumber is very large, the linear solver may not converge, leading to inaccurate results. Settingverbose=Truein the constructor will print solver convergence information, which can help diagnose convergence issues.Note
The forward operator uses the adjoint state method to compute gradients, e.g.,
deepinv.physics.Scattering.A_jvp()anddeepinv.physics.Scattering.A_vjp()by default, which is more memory efficient than using autograd, especially for large-scale problems. The adjoint state method requires one additional solve of the Lippmann-Schwinger equation per gradient computation, and avoids storing the entire computational graph (done by standard autograd). This can be disabled by settingadjoint_state=Falsein thedeepinv.physics.Scattering.SolverConfigobject passed to the constructorNote
Greenβs function has a singularity at r=0. In practice, we can avoid this issue following Vico et al. [112] which convolves Greenβs function with a sinc kernel to account for the bounded domain:
\[g_L(\mathbf{r}) = g(\mathbf{r}) * \text{rect}\left(\frac{\|\mathbf{r}\|}{L}\right),\]- Parameters:
img_width (int) β Number of pixels per image side (
H=W). The minimum required number of pixels depends on the background wavenumber to avoid spatial aliasing. We require at least \(\text{image width} \geq 2 k_b L/(2 \pi)\), where \(k_b\) is the background wavenumber and \(L\) is the box length.receivers (torch.Tensor) β Tensor of shape
(2, R)(shared receivers) or(2, T, R)(per-transmitter receivers) with receiver x/y positions (torch.floatdtype).transmitters (torch.Tensor) β Tensor of shape
(2, T)with transmitter x/y positions (torch.floatdtype)background_wavenumber (float, torch.Tensor) β background wavenumber \(k_b\), which can be real (
floatortorch.float) or complex (torch.complex). It controls the frequency of the incident wave, and thus the maximum achievable resolution of the contrast image.solver_config (deepinv.physics.scattering.Scattering.SolverConfig) β Configuration for the Lippmann-Schwinger solver used to compute the total field (first equation in the forward model). If
None, default configuration is used which uses the LSQR solver with a maximum of 500 iterations and tolerance 1e-5.box_length (float) β Physical side length of the square domain \(L\), which can be specified relative to the wavenumber.
wave_type (str) β incident wave type. Options are
'circular_wave'or'plane_wave'. The circular wave corresponds to a point source transmitter, while the plane wave corresponds to a far-field transmitter.device (str) β Torch device string, e.g.
'cpu'or'cuda'.dtype (torch.dtype) β Torch
dtypefor tensors which must be of complex type (e.g.torch.complex128).verbose (bool) β Enable verbose/debug printing.
- A(x, receivers=None, transmitters=None, **kwargs)[source]#
Forward operator wrapper: updates parameters, solves for total field and returns measurements.
- Parameters:
x (torch.Tensor) β Scattering potential
(B,1,H,W).receivers (torch.Tensor) β Optional new receiver positions of shape
(2, R)or(2, T, R).transmitters (torch.Tensor) β Optional new transmitter positions of shape
(2, T), where T is the number of transmitters.
- Returns:
(
torch.Tensor) Measurements tensor of shape(B,T,R).- Return type:
- A_dagger(y, linear=False, x_init=None, max_iter=2, use_init=True, rel_tol=1e-3, **kwargs)[source]#
Pseudo-inverse for the scattering operator.
If
linear=True, uses the Born approximation (single iteration).If
linear=False, alternates between updating the total field estimate with a fixed scattering potential, and updating the scattering potential estimate with a fixed total field, for a maximum ofmax_iteriterations.- Parameters:
y (torch.Tensor) β Measurements tensor (B,T,R).
linear (bool) β If True, use Born approximation (single iteration).
x_init (torch.Tensor) β Initial guess for the reconstruction (B,1,H,W).
max_iter (int) β Maximum number of alternating optimization iterations.
use_init (bool) β If True, use previous total field estimate to initialize the next iteration.
rel_tol (float) β Relative tolerance for convergence.
- A_jvp(x, u, **kwargs)[source]#
Jacobian-vector product for the forward operator.
- Parameters:
x (torch.Tensor) β Scattering potential
(B,1,H,W).u (torch.Tensor) β Vector to multiply with the Jacobian
(B,1,H,W).
- A_vjp(x, v, **kwargs)[source]#
Vector-Jacobian product for the forward operator.
- Parameters:
x (torch.Tensor) β Scattering potential of shape
(B,1,H,W).v (torch.Tensor) β Vector to multiply with the Jacobian, a tensor of shape
(B,T,R).dtype (torch.dtype) β torch.dtype used for internal computations and returned result
- class SolverConfig(min_iter=1, max_iter=500, solver='lsqr', tol=1e-05, green_imaginary_part=0.0, adjoint_state=True, verbose=False)[source]#
Bases:
objectConfiguration class for the Lippmann-Schwinger solver.
Defines the parameters for the iterative solver used to solve the Lippmann-Schwinger equation.
- Parameters:
min_iter (int) β Minimum number of iterations. By default, 1.
max_iter (int) β Maximum number of iterations. By default, 500.
solver (str) β Linear solver to use (
'lsqr','BiCGStab'or'CG'). By default,'lsqr'.tol (float) β Stopping criterion for the solver. By default, 1e-5.
adjoint_state (bool) β If True, use adjoint state method for gradients, else use autograd. By default
True.green_imaginary_part (float) β Small imaginary part to add to the wavenumber in the Greenβs function to improve solver convergence. By default, 0 (no modification).
verbose (bool) β If True, print solver convergence information. By default, False.
- compute_field_out(x, total_field)[source]#
Compute sensor outputs \(y = G_s * ext{diag}(x) u\).
- Parameters:
x (torch.Tensor) β Scattering potential
(B,1,H,W).total_field (torch.Tensor) β Total field u
(B,T,H,W).
- compute_total_field(x, **kwargs)[source]#
Solve for the total field given scattering potential
xusing the provided solver.- Parameters:
x (torch.Tensor) β Scattering potential tensor of shape
(B,1,H,W).- Returns:
(
torch.Tensor) Total field tensor of shape(B,T,H,W).- Return type:
- generate_incident_field(device, dtype)[source]#
Generate incident fields on the image grid and at receiver positions.
Plane waves are computed as:
\[v_i(\mathbf{r}) = \exp\left( \mathrm{i} \mathbf{k}_i^{\top} \mathbf{r} \right)\]where \(\mathbf{k}_i\) is the wavevector for the ith transmitter.
Circular waves are computed using Greenβs function as:
\[v_i(\mathbf{r}) = g(\|\mathbf{r} - \mathbf{r}_i\|)\]where \(\mathbf{r}_i\) is the position of the ith transmitter.
- Returns:
(
torch.Tensor) Incident field tensor of shape(1,T,H,W)whereTis the number of transmitters.- Return type:
- normalize(x)[source]#
Normalize the operator to have unitary Jacobian spectral norm around the input contrast incident field and noise model by the operator norm.
- Parameters:
x (torch.Tensor) β Scattering potential of size
(B,1,H,W).
- set_solver(solver_config)[source]#
Update the Lippmann-Schwinger solver configuration.
- Parameters:
solver_config (deepinv.physics.Scattering.SolverConfig) β New solver configuration.
- set_verbose(verbose)[source]#
Toggle verbosity.
- Parameters:
verbose (bool) β Enable verbose/debug printing.
- update_parameters(receivers=None, transmitters=None, **kwargs)[source]#
Update transmitter and receiver parameters and recompute dependent fields/operators.
- Parameters:
receivers (torch.Tensor) β New receiver positions of shape
(2, R)or(2, T, R).transmitters (torch.Tensor) β New transmitter positions of shape
(2, T), whereTis the number of transmitters.