virgil.imaging
Regularizers, priors and helpers for image reconstruction with
Image. Ways to choose a regularization weight are the
L-curve's corner, the discrepancy principle, classic MaxEnt (LCurve.classic_maxent) and, for
Gaussian-field images, the Laplace evidence (log_evidence).
Methods
CLEAN
Högbom's CLEAN (Högbom 1974, A&AS 15, 417) finds the peak of the residual
dirty image and adds a fraction (the loop gain) of a point source there. For
data linear in the image, that residual is proportional to \(-\partial\chi^2/\partial c_p\),
the gradient of χ² with respect to the flux of a point at each pixel. clean
uses that gradient directly, so it works for closure, kernel and DISCO phases
and V² as well. With \(J e_p\) the change in the whitened residuals per unit
flux at pixel \(p\), each iteration picks the pixel whose Gauss–Newton step
lowers χ² most, the largest \(g_p^2/|J e_p|^2\) with \(g_p<0\), and adds gain
times that step, \(-g_p/(2|J e_p|^2)\). This is matching pursuit; for linear
data, where \(|J e_p|\) is the same everywhere, it is exactly Högbom's CLEAN.
The norms \(|J e_p|\) are computed once, at the start, with one
Jacobian–vector product per pixel. Normalizing by them matters next to an
analytic star, where flux in a pixel is nearly the same as the star's: the
gradient there is small, but so is \(|J e_p|\), and an unnormalized search
would pile flux beside the star.
Components are added to a fixed base scene, with fluxes relative to its
(for a System base, to its total, as siblings of its components). They are
grey unless a spectrum is given, as the image is in SPARCO. Without a
base, the components alone make the image, starting from one at the centre
of the grid (closure phases do not fix the position, so this is also the
anchor).
Each iteration only adds flux, so an early step that overshoots cannot be
undone by later ones. Every refit_every iterations a major cycle (Clark
1980, A&A 89, 377; Cotton–Schwab, Schwab 1984, AJ 89, 1076) refits the fluxes
of all the components at once by non-negative least squares on the
linearized residuals: flux can move between components, and components whose
flux falls to zero are removed. A last major cycle runs when CLEAN stops at
the target or at max_iterations. The base is fixed unless base_priors
names some of its parameters; each major cycle then fits those together with
the fluxes with fit, since fitting them in turn would let the components
absorb an error in the base, or the base absorb flux it does not model. A
companion much closer to the star than λ/B has its flux and separation
nearly degenerate, whatever fits it.
Multi-scale CLEAN (Cornwell 2008, IEEE JSTSP 2, 793,
arXiv:0806.2228; Rau & Cornwell 2011,
arXiv:1106.2745): with several
scales_mas, each component is a pixel convolved with a circular Gaussian of
FWHM \(s\) (\(s=0\) is a point), and the image is \(\sum_s G_s * F_s\). The search
runs over every (scale, pixel) with the same score \(g^2/|J e|^2\), and the
major cycles refit all scales together. Each shape is cut to the support
and renormalized there, so a component's flux is the flux it puts in the
image. Cornwell multiplies the peak residual at each scale by a bias
\(1 - 0.6\,s/s_{\max}\) to favour small scales; the score here is the χ²
decrease of each component's step, which does not depend on its size, so no
bias is needed. scale_bias applies one, \(1-b\,s/s_{\max}\), if the broad
components grab flux that belongs to points.
Iteration stops when χ² per data point reaches target_chi2_red (the
discrepancy principle; it relies on correct error bars), after
max_iterations, or when χ² has stalled: no pixel lowers it, or it has
fallen by less than a fraction stall_tolerance over the last stall_window
iterations, and a major cycle does not help. That happens when noise puts the
truth's own χ² per point above the target; chi2_red shows how close it
came.
Evidence
With a Gaussian-field image, whitened latents \(z\sim N(0,I)\) and Jacobian \(J\) of the whitened residuals with respect to them, the Laplace evidence at the MAP is
and the Laplace posterior of the latents is \(N(z_0,(I+J^\top J)^{-1})\). The log determinant is \(\sum\log(1+s^2)\) over the singular values \(s\) of \(J\), computed from the singular values of \(J\) (QR-reduced when far from square): the Gram matrix's rounding error can exceed the identity on high signal-to-noise data and leave \(I+J^\top J\) numerically indefinite. With the thin SVD \(J=USV^\top\) a posterior draw is \(z_0 + V\,\mathrm{diag}((1+s^2)^{-1/2})\,\varepsilon_1 + (I-VV^\top)\varepsilon_2\).
\(-\tfrac12\chi^2 - L\) is
model_loglike at the MAP less its
data-only normalization, \(-\sum\log\sigma - \tfrac n2\log 2\pi\) (in its von
Mises form for unprojected phases, and with the correlated closure phases'
determinant), which is the same for every model and hyperparameter on a
given dataset and cancels in differences of \(\log Z\). For plain data
\(L=0\). Otherwise \(L\) is the rest of the likelihood's normalizer: for data
with calibration gains or closure offsets (which the likelihood marginalizes)
it is \(\tfrac12\log\det\) of their covariance factor, and for extra
observables the log ratio of effective to quoted errors. It is evaluated at
the MAP as the likelihood evaluates it, so that evidences for data with
different gain or offset widths compare; its curvature with respect to \(z\)
is left out of \(J\), as Gauss–Newton leaves out the residuals' own second
derivatives.
Error scale
In MacKay's evidence framework (MacKay 1992, eqs. 4.9–4.10; Bishop 2006, eqs. 3.91–3.95) the noise level is a hyperparameter. Write the noise precision as \(\beta=1/s^2\), so that the likelihood is \(\exp(-\beta\chi^2/2)\) with χ² on the quoted errors. The Laplace evidence is maximized when
where \(N\) is the number of independent data and the \(\lambda_i\) are the eigenvalues of the Gauss–Newton curvature \(J^\top J\) in the field's whitened latents, in which the prior's curvature is the identity. \(\gamma\) is the effective number of parameters the data measure: a direction with \(\beta\lambda\gg1\) is fixed by the data and counts as one, one with \(\beta\lambda\ll1\) is fixed by the prior and counts as none. It is solved for \(\beta\) by Newton's method, which converges monotonically from \(\beta=0\) because \(\gamma\) is concave in \(\beta\). Each measured parameter uses up one datum's worth of scatter, so χ² ≈ N − γ for honest error bars, and the ordinary \(s^2=\chi^2/N\) is biased low for the same reason as the 1/N estimate of a sample variance. Only the field's latents are counted in \(\gamma\); each other measured parameter raises \(s\) by about 1/(2N), negligible in SPARCO fits.
One scale per observable. A single \(s\) assumes every error bar is off by
the same factor. The V² and closure phases of one instrument are calibrated
differently: in a MATISSE N-band contest file the V² gave χ² per point 0.005
and the closure phases 0.49. With by_observable=True each kind of observable
\(b\) ("vis", "phi" and each kind in extras, pooled over the datasets) gets
its own precision \(\beta_b\) on its rows of the residual vector, and the
evidence is maximized where, for every block,
with \(G_b=J_b^\top J_b\) from block \(b\)'s rows. \(\gamma_b\) is the sum over the
block's rows of the diagonal of the hat matrix \(B^{1/2}JA^{-1}J^\top B^{1/2}\)
(\(B\) the diagonal matrix of each row's \(\beta_b\)): the share of the measured
parameters that block pays for. The blocks are coupled, so the equations are
solved by iterating \(\beta_b\leftarrow N_b/(\chi_b^2+\gamma_b/\beta_b)\) (the
same fixed point as MacKay's, but monotone, so it cannot oscillate) from
\(\beta_b=N_b/\chi_b^2\) to a relative change of \(10^{-10}\). With one block it
reproduces the single scale. \(N_b\) counts independent data: closure phases
from four or more telescopes count their independent combinations, and their
periodic penalty residuals belong to the block's χ² and \(\gamma_b\) but are not
counted as data. The blocks' covariances must be \(s_b^2D\): calibration gains,
closure-phase offsets, marginalized flux scales or differential phases with a
finite prior_width add nuisance covariance that does not scale with the
quoted errors, and raise a ValueError. Prefer the single scale when a block
has few data: \(s_b\) scatters by about \(1/\sqrt{2(N_b-\gamma_b)}\).
References: D. J. C. MacKay (1992), "Bayesian interpolation", Neural
Computation 4, 415 (doi); C. M.
Bishop (2006), Pattern Recognition and Machine Learning, §3.5; S. F. Gull
(1989), "Developments in maximum entropy data analysis", in Maximum Entropy
and Bayesian Methods, Kluwer, 53–71, for the same \(N-\gamma\) argument applied
to maximum entropy (LCurve.classic_maxent).
Regularizers and helpers for image reconstruction.
An image fit is a call to fit whose model contains an
Image, with a prior on its log_brightness
(see :func:image_priors) and usually a regularizer, which adds a penalty
on the pixel fluxes b to the loss:
| Regularizer | Penalty | Least squares (LM)? | A prior density? |
|---|---|---|---|
TSV |
w Σ (Δx b)² + (Δy b)² |
yes | no |
TV |
w Σ √((Δx b)² + (Δy b)² + ε²) |
no | no |
MaxEntropy |
w Σ b log(b / q) |
no | no |
Laplacian |
w Σ (∇²b)² |
yes | no |
StarletL1 |
w Σ √(s² + ε²) over starlet details s |
no | no |
LogSum |
w Σ log(1 + b / (ε b̄)) |
no | no |
Centroid |
½ |centroid / σ|² |
yes | yes |
Differences Δ are between neighbouring pixels, with zeros beyond the
edges. TSV and the Laplacian favour smooth images, TV piecewise-flat ones,
maximum entropy images close to a default q; StarletL1 (an L1 norm
of the starlet coefficients; an L1 norm of the pixels is constant, since
they sum to one) and LogSum favour sparse ones, and LogSum is not
convex, so start it from a clean model.
l_curve sweeps the weight w.
When fit's model function returns one model per
dataset, every regularizer acts on the first model only: right for
transformed copies of one scene (e.g. a Rotated
epoch), but an Image that appears only in a later model is not regularized.
Closure, kernel and DISCO phases do not fix an image's position: something
must (an analytic star at the origin, a Centroid
prior, or a centred prior mean). diagnose
checks a fit for this and other common pitfalls.
TSV
Bases: _ImageRegulariser
Total squared variation: weight * Σ (Δx b)² + (Δy b)².
A quadratic smoothness penalty, so it can be fitted by Levenberg–Marquardt (Kuramochi et al. 2018, ApJ 858, 56).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
weight
|
float
|
Strength of the penalty. |
required |
path
|
str
|
Path of the Image in the model (e.g. |
None
|
smoothed
|
bool
|
Penalize the Image's |
False
|
weight = np.asarray(weight, dtype=float)
instance-attribute
path = path
instance-attribute
smoothed = bool(smoothed)
instance-attribute
__init__(weight, path=None, smoothed=False)
residuals(model)
value(model)
TV
Bases: _ImageRegulariser
Total variation: weight * Σ √((Δx b)² + (Δy b)² + ε²).
Favours piecewise-flat images with sharp edges (Rudin, Osher & Fatemi 1992,
Physica D 60, 259). ε makes it differentiable where the image is flat.
It is averaged over the four flips of the image, so it does not depend on
which neighbour each forward difference pairs a pixel with. It is not
isotropic: a sharp edge along a diagonal costs about a fifth more per unit
length than one along a pixel axis.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
weight
|
float
|
Strength of the penalty. |
required |
epsilon
|
float
|
Smoothing scale, as a fraction of the mean pixel flux (default 1e-2). |
0.01
|
path
|
str
|
Path of the Image in the model. |
None
|
smoothed
|
bool
|
Penalize the Image's |
False
|
weight = np.asarray(weight, dtype=float)
instance-attribute
epsilon = np.asarray(epsilon, dtype=float)
instance-attribute
path = path
instance-attribute
smoothed = bool(smoothed)
instance-attribute
__init__(weight, epsilon=0.01, path=None, smoothed=False)
value(model)
MaxEntropy
Bases: _ImageRegulariser
Maximum entropy: weight * Σ b log(b / q), with b and q unit-sum.
The relative entropy of the image with respect to a default image q
(Skilling & Bryan 1984, MNRAS 211, 111; BSMEM, Baron & Young 2008).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
weight
|
float
|
Strength of the penalty. |
required |
prior
|
array - like
|
The default image |
None
|
path
|
str
|
Path of the Image in the model. |
None
|
smoothed
|
bool
|
Penalize the Image's |
False
|
prefers_lbfgs = True
class-attribute
instance-attribute
weight = np.asarray(weight, dtype=float)
instance-attribute
prior = None if prior is None else np.asarray(prior)
instance-attribute
path = path
instance-attribute
smoothed = bool(smoothed)
instance-attribute
__init__(weight, prior=None, path=None, smoothed=False)
value(model)
residuals(model)
Residuals r with value = 0.5 Σ r² (b and q sum to 1):
r = t √(2 w q φ(t)) for t = (b - q) / q and
φ(t) = ((1 + t) ln(1 + t) - t) / t², so that the entropy can be
fitted by Levenberg–Marquardt. Zero where b is.
metric_rows(model)
Rows whose JᵀJ is the curvature w b of the entropy in
ln b (the Skilling–Bryan metric): a positive Gauss–Newton form.
In the Image's softmax logits its diagonal is w b (1 - b).
Laplacian
Bases: _ImageRegulariser
Squared Laplacian: weight * Σ (∇²b)².
∇²b is the five-point Laplacian, with zeros beyond the edges. A
quadratic penalty on curvature, smoother than TSV, so it can be fitted by
Levenberg–Marquardt (Thiébaut & Young 2017).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
weight
|
float
|
Strength of the penalty. |
required |
path
|
str
|
Path of the Image in the model. |
None
|
smoothed
|
bool
|
Penalize the Image's |
False
|
weight = np.asarray(weight, dtype=float)
instance-attribute
path = path
instance-attribute
smoothed = bool(smoothed)
instance-attribute
__init__(weight, path=None, smoothed=False)
residuals(model)
value(model)
StarletL1
Bases: _ImageRegulariser
L1 norm of the starlet details: weight * Σ √(s² + ε²).
s are the detail coefficients of starlet at
every scale. An L1 norm favours few non-zero coefficients, so the image is
built from few compact structures of any size (Honma et al. 2014, PASJ 66,
95). The coarse plane, which carries the flux, is not penalized.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
weight
|
float
|
Strength of the penalty. |
required |
scales
|
int
|
Number of detail planes (default 4); the largest holds structure about
|
4
|
epsilon
|
float
|
Smoothing scale, as a fraction of the mean pixel flux (default 1e-2). |
0.01
|
path
|
str
|
Path of the Image in the model. |
None
|
smoothed
|
bool
|
Penalize the Image's |
False
|
weight = np.asarray(weight, dtype=float)
instance-attribute
scales = int(scales)
class-attribute
instance-attribute
epsilon = np.asarray(epsilon, dtype=float)
instance-attribute
path = path
instance-attribute
smoothed = bool(smoothed)
instance-attribute
__init__(weight, scales=4, epsilon=0.01, path=None, smoothed=False)
value(model)
LogSum
Bases: _ImageRegulariser
Log-sum sparsity: weight * Σ log(1 + b / (ε b̄)).
b̄ is the mean pixel flux over the support. As ε → 0 the penalty
counts the bright pixels: a smooth surrogate for the L0 "norm" of SQUEEZE
(Candès, Wakin & Boyd 2008; Baron, Monnier & Kloppenburg 2010). It is not
convex, so start the fit from a good image, and sweep its weight with
l_curve(..., warm_start=False): pixels a strong weight has driven dark
cannot recover in the weaker fits that follow.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
weight
|
float
|
Strength of the penalty. |
required |
epsilon
|
float
|
The flux, as a fraction of the mean pixel flux, at which a pixel starts to count (default 1e-2). |
0.01
|
path
|
str
|
Path of the Image in the model. |
None
|
smoothed
|
bool
|
Penalize the Image's |
False
|
weight = np.asarray(weight, dtype=float)
instance-attribute
epsilon = np.asarray(epsilon, dtype=float)
instance-attribute
path = path
instance-attribute
smoothed = bool(smoothed)
instance-attribute
__init__(weight, epsilon=0.01, path=None, smoothed=False)
value(model)
CleanResult
dataclass
The result of :func:clean.
Provisional
May change in any 0.4.x release without a deprecation cycle.
Attributes:
| Name | Type | Description |
|---|---|---|
model |
SourceModel
|
|
components |
(array, shape(npix, npix))
|
The components' total flux in each pixel, |
pixel_scale_mas |
float
|
Pixel size in milliarcseconds. |
chi2_red |
array
|
χ² per data point before each iteration; the last entry is the final model's. |
stop |
str
|
|
components_by_scale |
(array, shape(n_scales, npix, npix))
|
The fluxes |
scales_mas |
tuple of float
|
The FWHM of each scale's Gaussian shape (0 for a point). |
model
instance-attribute
components
instance-attribute
pixel_scale_mas
instance-attribute
chi2_red
instance-attribute
stop
instance-attribute
components_by_scale = None
class-attribute
instance-attribute
scales_mas = (0.0,)
class-attribute
instance-attribute
__init__(model, components, pixel_scale_mas, chi2_red, stop, components_by_scale=None, scales_mas=(0.0,))
restored(beam)
The components convolved with beam: the "restored" image.
In the orientation of the pixel grid, in the units of
components. Unlike radio astronomy's restored image, it does not
include the residuals.
Centroid
Bases: _ImageRegulariser
A Gaussian prior centring an Image's flux on the origin.
The penalty is ½ |c / sigma_mas|², where c is the flux-weighted
centroid of the Image on the sky, including its dra/ddec. This
is a genuine prior density, so it may be used when sampling.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sigma_mas
|
float
|
Standard deviation of the centroid, in milliarcseconds. |
required |
path
|
str
|
Path of the Image in the model. |
None
|
probabilistic = True
class-attribute
instance-attribute
weight = 1.0
instance-attribute
sigma_mas = np.asarray(sigma_mas, dtype=float)
instance-attribute
path = path
instance-attribute
__init__(sigma_mas, path=None)
centroid(model)
The Image's flux-weighted centroid (dra, ddec) in mas.
residuals(model)
value(model)
Beam
dataclass
A Gaussian approximation to the core of the dirty beam.
Attributes:
| Name | Type | Description |
|---|---|---|
major_mas, minor_mas |
float
|
Full widths at half maximum along the major and minor axes, in mas. |
pa_deg |
float
|
Position angle of the major axis, North to East, in degrees. |
major_mas
instance-attribute
minor_mas
instance-attribute
pa_deg
instance-attribute
__init__(major_mas, minor_mas, pa_deg)
LCurve
dataclass
The result of :func:l_curve.
Attributes:
| Name | Type | Description |
|---|---|---|
weights |
array
|
The regularization weights, in the order fitted (largest first). |
chi2 |
array
|
Total χ² of each fit. |
chi2_red |
(array, shape(n_weights, n_datasets))
|
χ² per data point of each dataset. |
penalty |
array
|
The unweighted regularizer, |
results |
list of FitResult
|
The fits. |
weights
instance-attribute
chi2
instance-attribute
chi2_red
instance-attribute
penalty
instance-attribute
results
instance-attribute
__init__(weights, chi2, chi2_red, penalty, results)
corner()
The weight at the L-curve's corner, where it bends most sharply.
The curve is (log χ², log penalty) parametrized by log w; the corner
is the interior point of largest curvature (Hansen & O'Leary 1993). Check
it by eye: a sparse or noisy sweep has a noisy curvature. A fit with a
non-finite χ² or penalty is left out.
discrepancy(target=1.0)
The weight at which χ² per data point reaches target.
Morozov's discrepancy principle: regularize as strongly as the data allow.
With several datasets the binding one (the largest χ² per point) must reach
the target. It interpolates linearly in log w and returns None if the
sweep never crosses the target. The truth has χ² per point 1 ± √(2/N), so 1
is the natural target; it relies on correct error bars and over-regularizes
if they are underestimated.
classic_maxent(data, path='env')
The maximum-entropy weight of Gull and Skilling's "classic MaxEnt".
For a sweep of MaxEntropy fits, the weight
w at which -2 w S equals the number of well-measured directions in
the image, N = Σ λ / (λ + w) (Gull 1989, Skilling 1989, both in
Maximum Entropy and Bayesian Methods, Kluwer). S is the entropy
(minus the sweep's penalty) and the λ are the eigenvalues of the
data's Gauss–Newton curvature in the entropy metric diag(√b) JᵀJ
diag(√b). It is the stationary point of the Laplace evidence in w, so
it assumes correct error bars. Both sides are evaluated at each fit in the
sweep and the crossing is interpolated linearly in log w; None if
the sweep does not bracket it. Like log_evidence
it uses the quoted errors, and rejects a sweep fitted with noise= terms
or one model per dataset.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
OIData or sequence of OIData
|
The data the sweep was fitted to. |
required |
path
|
str
|
Path of the regularized Image in the model (default |
'env'
|
Diagnosis
dataclass
The result of :func:diagnose.
Attributes:
| Name | Type | Description |
|---|---|---|
checks |
dict
|
Check name to value. Checks made per dataset or per Image are lists, in the order of the data or of the Images in the model. |
warnings |
list of str
|
One sentence for each problem found, saying what to do about it. Empty if none. |
checks
instance-attribute
warnings
instance-attribute
__init__(checks, warnings)
__str__()
starlet(image, scales=4)
The starlet (isotropic undecimated wavelet) transform of an image.
The à-trous algorithm with a B3-spline kernel (Starck, Murtagh & Fadili
2010): the image is smoothed scales times with the kernel's taps twice
as far apart each time, and each detail plane is the difference between
successive smoothings. Plane j holds structure about 2**j pixels
across. Beyond the edges the image is zero.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
image
|
(array - like, shape(ny, nx))
|
The image. |
required |
scales
|
int
|
Number of detail planes (default 4). |
4
|
Returns:
| Name | Type | Description |
|---|---|---|
details |
(Array, shape(scales, ny, nx))
|
The detail planes, finest first. |
coarse |
(Array, shape(ny, nx))
|
What is left after the last smoothing; |
clean(data, npix, pixel_scale_mas, base=None, *, gain=0.1, max_iterations=1000, target_chi2_red=1.0, refit_every=50, stall_window=50, stall_tolerance=0.001, refresh_norms=False, base_priors=None, spectrum=None, support=None, init=None, rotation_deg=0.0, dtype='float64', scales_mas=(0.0,), scale_bias=0.0)
Build an image from components, one at a time: gradient CLEAN.
Provisional
May change in any 0.4.x release without a deprecation cycle.
Each iteration adds gain times the Gauss–Newton step at the pixel that
lowers χ² most (Högbom's CLEAN for linear data, else matching pursuit); every
refit_every iterations a major cycle refits all fluxes by non-negative
least squares. See CLEAN.
Source: Högbom (1974), references.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
OIData or sequence of OIData
|
The data, fitted jointly. |
required |
npix
|
int and float
|
Grid size and pixel size (mas). |
required |
pixel_scale_mas
|
int and float
|
Grid size and pixel size (mas). |
required |
base
|
SourceModel and float
|
Fixed scene (e.g. a star) to add components to, not offset if a System; fraction of each step taken, in (0, 1]. |
None
|
gain
|
SourceModel and float
|
Fixed scene (e.g. a star) to add components to, not offset if a System; fraction of each step taken, in (0, 1]. |
None
|
max_iterations
|
(int, float and int)
|
Iteration limit; χ² per point to stop at; iterations per major cycle (0: none). |
1000
|
target_chi2_red
|
(int, float and int)
|
Iteration limit; χ² per point to stop at; iterations per major cycle (0: none). |
1000
|
refit_every
|
(int, float and int)
|
Iteration limit; χ² per point to stop at; iterations per major cycle (0: none). |
1000
|
stall_window
|
int and float
|
Stop when χ² falls by less than this fraction in this many iterations. |
50
|
stall_tolerance
|
int and float
|
Stop when χ² falls by less than this fraction in this many iterations. |
50
|
base_priors
|
dict and bool
|
Base priors refitted, and pixel norms |
None
|
refresh_norms
|
dict and bool
|
Base priors refitted, and pixel norms |
None
|
spectrum
|
Spectrum and array - like
|
Shared spectrum (default grey; needs a base); boolean |
None
|
support
|
Spectrum and array - like
|
Shared spectrum (default grey; needs a base); boolean |
None
|
init
|
Spectrum and array - like
|
Shared spectrum (default grey; needs a base); boolean |
None
|
rotation_deg
|
float and str
|
0.0
|
|
dtype
|
float and str
|
0.0
|
|
scales_mas
|
sequence of float and float
|
FWHMs (mas) of the components, 0 for a point ( |
(0.0,)
|
scale_bias
|
sequence of float and float
|
FWHMs (mas) of the components, 0 for a point ( |
(0.0,)
|
Returns:
| Type | Description |
|---|---|
CleanResult
|
The model, components and χ² history. |
starting_image(data, *, star=True, oversample=4.0, largest_mas=None, start='moments', hole_mas=None, field_fwhms=6.0, inner_elements=4.0, aperture_mas=None, pad_to_mas=None)
A starting model for an image fit, sized from the data.
Fits an analytic star (if star) plus a circular Gaussian envelope to the
data, then builds an image of field_fwhms envelope FWHMs (capped by
largest_mas and field_of_view), with
pixels oversample times finer than Nyquist. Fit it with
fit(start, image_priors(start), data, regularizers); see
imaging with regularizers. With star=True the
envelope's flux ratio is fixed unless you add a prior on "env.flux" to
image_priors(start) (a lone Image is normalized, so has no flux).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
OIData or sequence of OIData
|
The data. |
required |
star
|
bool
|
Whether an unresolved star at the origin is in the scene (it fixes the
position; otherwise use a |
True
|
oversample
|
float
|
Pixels per Nyquist pixel. |
4.0
|
largest_mas
|
float
|
Cap on the data-sized field, which never exceeds λ / B_min. |
None
|
start
|
(moments, dirty)
|
Starting pixels: the fitted Gaussian, or the positive part of the
|
"moments"
|
hole_mas
|
float
|
Radius of a hole in the support under the star
( |
None
|
field_fwhms
|
float
|
Field in envelope FWHMs; least field in resolution elements λ / (2 B_max). |
6.0
|
inner_elements
|
float
|
Field in envelope FWHMs; least field in resolution elements λ / (2 B_max). |
6.0
|
aperture_mas
|
float
|
A telescope's field of view, as a ceiling on field and envelope width. |
None
|
pad_to_mas
|
float
|
Least grid width (mas), padded with empty pixels; may exceed λ / B_min, but beyond it the data do not constrain the flux, so pair a large field with a support or priors. |
None
|
Returns:
| Type | Description |
|---|---|
SourceModel
|
|
dirty_image(data, npix, pixel_scale_mas, flux_ratio=None)
The dirty image: direct synthesis of the visibilities, unregularized.
I(x) = Σ_k w_k Re[V_k exp(+2πi u_k · x)] / Σ_k w_k, with uniform weights
on the informed samples: the image convolved with the dirty beam, plus
noise; it peaks at one for a lone point source. For AMIGO DISCO data the
visibilities are the least-squares estimate from the modes, with |V| = 1 on
average on the shortest baselines (the modes are blind to the total flux).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
OIData or sequence of OIData
|
The data; closure phases alone cannot make a dirty image
( |
required |
npix
|
int
|
Number of pixels on a side. |
required |
pixel_scale_mas
|
float
|
Pixel size in mas. |
required |
flux_ratio
|
float
|
Flux of extended emission relative to a star at the origin: the
best-fitting point source at the origin is subtracted and the image
scaled by |
None
|
Returns:
| Type | Description |
|---|---|
(Array, shape(npix, npix))
|
In the orientation of |
image_priors(scene)
Priors on the log-brightness of every Image in a scene.
Returns a priors dict for fit. An Image with
a plain log-brightness array gets a flat prior,
{"env.log_brightness": ImproperUniform(...)}, so that its pixels are
constrained only by the data and the regularizers. An Image with a
GaussianField gets standard-normal
priors on the field's latents, {"env.log_brightness.latent":
Normal(0, 1)}: the Gaussian-process prior, which needs no regularizer.
A scene with several such Images (a compact and an extended component,
say) gets one prior per Image.
Add priors for any other free parameters (fluxes, offsets) to the dict.
nyquist_pixel_scale(data)
The largest pixel scale (mas) that samples the data's finest fringes.
That is λ / (2 B) for the longest baseline B in units of wavelength,
over the unflagged samples of data (an OIData or a sequence of
them; see OIData.unflagged). A
flagged sample carries no signal, so its baseline does not count.
Reconstructions normally use pixels 2–4 times smaller.
field_of_view(data, largest_mas=500.0, *, aperture_mas=None)
The largest field of view (mas) that the data measure.
The smallest of largest_mas (a cap you choose, 500 by default); the
interferometric field, the largest λ / B over the unflagged samples with a
non-zero baseline B (structure larger than that is not measured, and on a
uv lattice a larger field would alias); the bandwidth-smearing field
R λ / B_max, when the data carry channel widths (see
smeared_model); and aperture_mas, if
given, the field of a single telescope, about λ / D. A ceiling, not a
recommended size: image_field sizes an
image from the data and uses it as the limit. Flagged samples are left out
(see OIData.unflagged).
image_field(data, fwhm_mas=None, *, field_fwhms=6.0, inner_elements=4.0, aperture_mas=None, largest_mas=500.0)
The width (mas) of an image field that suits the data.
The field is field_fwhms × fwhm_mas (without fwhm_mas, the
second_moment_fwhm; with no
visibility amplitudes to measure it, the ceiling), clipped to a floor of
inner_elements × λ / (2 B_max) and a ceiling of
field_of_view on the unflagged baselines
(the ceiling wins if they cross).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
OIData or sequence of OIData
|
The data. |
required |
fwhm_mas
|
float
|
The FWHM of the source in mas, to override the one from the visibilities. |
None
|
field_fwhms
|
float
|
The field in FWHMs of the source (6 by default; 3 to 8 are reasonable). A larger field spends pixels on empty sky, where flux can scatter in islands. |
6.0
|
inner_elements
|
float
|
The floor, in resolution elements λ / (2 B_max) (4 by default). |
4.0
|
aperture_mas
|
float
|
A telescope's field of view, as a further ceiling. |
None
|
largest_mas
|
float
|
A cap on the ceiling (500 by default, as for |
500.0
|
Returns:
| Type | Description |
|---|---|
float
|
The field in mas. |
second_moment_fwhm(data, v2_min=0.3)
The FWHM (mas) of the Gaussian that the short baselines see.
A circular Gaussian of FWHM θ has -ln V² = π² θ² ρ² / (2 ln 2) (ρ = B /
λ, cycles per radian), and any normalized source has this form to second
order in ρ. It is fitted by weighted least squares on the unflagged samples
with V² ≥ v2_min, needing no image and no fit. It is the rms width of
the whole source, a star included, so it is meaningless when a bright
unresolved component (flux fraction above about 0.55) keeps V² above
v2_min on every baseline: use
envelope_fwhm there.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
OIData or sequence of OIData
|
Data with visibility amplitudes (V², |V| or log |V|); projected (kernel, DISCO) observables and phase-only data have none. |
required |
v2_min
|
float
|
The smallest V² to use, 0.3 by default. |
0.3
|
Returns:
| Type | Description |
|---|---|
float or None
|
The FWHM in mas: zero if V² does not fall with ρ (an unresolved
source), and |
envelope_fwhm(data, star=True, aperture_mas=None)
The FWHM (mas) of the source's envelope, from a quick parametric fit.
This is the width that starting_image
sizes its field from: a star (if star) plus a Gaussian envelope is
fitted, and the FWHM is the envelope's or, if larger, the one the short
baselines see. Unlike
second_moment_fwhm alone it is
not diluted by a bright star. Pass it to
image_field.
beam(data)
The resolution of a dataset: the FWHM ellipse of its beam's core.
The dirty beam falls off near its peak as 1 - 2π² xᵀ M x, with M the
weighted mean of u uᵀ over the samples; this matches a Gaussian of
covariance M⁻¹ / 4π², whose FWHM ellipse is the usual "beam", roughly
λ/B and elongated where the coverage is. Every informative sample has equal
weight ("uniform weighting"): in AMIGO DISCO data, the uv points where the
modes have weight (at least 1e-3 of the largest); otherwise the unflagged
samples (see OIData.unflagged), since a
flagged sample measures nothing and counting it would claim a resolution
the data do not have.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
OIData or sequence of OIData
|
The data. |
required |
Returns:
| Type | Description |
|---|---|
Beam
|
|
convolve_beam(image, pixel_scale_mas, beam)
An image convolved with a Gaussian beam: the image at the data's resolution.
The "restored" image of radio astronomy: it shows only what the data resolve, so convolve both reconstructions to compare them. The kernel is an elliptical Gaussian with the beam's FWHMs and position angle (North to East), normalized to unit sum on an odd grid centred on a pixel. Flux beyond the edge is taken to be zero, so structure within about a beam of the edge is dimmed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
image
|
(array - like, shape(ny, nx))
|
The image, in the orientation of
|
required |
pixel_scale_mas
|
float
|
Pixel size in milliarcseconds. |
required |
beam
|
Beam
|
The beam (FWHMs in mas, position angle in degrees), usually
|
required |
Returns:
| Type | Description |
|---|---|
(Array, shape(ny, nx))
|
The convolved image. |
l_curve(model, priors, data, regularizer, weights, others=(), *, warm_start=True, **fit_options)
Fit a model over a range of weights for one regularizer.
The weights are fitted from largest to smallest, each starting from the
previous solution. Plot penalty against chi2 (log axes) to see the
trade-off, and compare LCurve.corner and
LCurve.discrepancy with the images
either side: there is usually a wide range of good weights.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
As for |
required | |
priors
|
As for |
required | |
data
|
As for |
required | |
regularizer
|
(TSV, TV, MaxEntropy, Laplacian, StarletL1 or LogSum)
|
The regularizer whose |
required |
weights
|
sequence of float
|
The weights to try (sorted internally, largest first). |
required |
others
|
sequence
|
Further regularizers kept fixed, e.g. a
|
()
|
warm_start
|
bool
|
Start each fit from the previous, stronger one (default), or every
fit from |
True
|
**fit_options
|
Passed to |
{}
|
Returns:
| Type | Description |
|---|---|
LCurve
|
|
log_evidence(model, data, path='env', *, priors=None, regularizers=())
Laplace-approximated log evidence of a Gaussian-process fit.
At the MAP model, log Z ≈ -½ χ² - L - ½ |z|² - ½ log det(I + JᵀJ)
up to a data-only constant, with z the whitened latents of the Image's
GaussianField (or the Gaussian-process parts
of a MultipoleDisk), J their Jacobian and
L the likelihood normalizer; extra priors in priors enter through
their log_prob at the MAP and their JᵀJ block of the Hessian. Compare
it across hyperparameters: it is conditional on those held in model (or
sample them: log-uniform priors from gp_priors
for a MultipoleDisk, your own log-uniform priors on a field's sigma
and length_mas for an Image). Exact only for a linear model, and it
assumes correct error bars (see error_scale)
and uses the quoted errors, so no noise= terms or per-dataset models. See
Evidence.
Source: MacKay (1992), references.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
SourceModel or FitResult
|
The MAP model, or better the |
required |
data
|
OIData or sequence of OIData
|
The data it was fitted to. |
required |
path
|
str or tuple of str or None
|
Path of the Image (default |
'env'
|
priors
|
dict
|
Priors of free parameters; non-latent parameters enter via |
None
|
regularizers
|
sequence
|
Fit regularizers; probabilistic ones ( |
()
|
Returns:
| Type | Description |
|---|---|
float
|
|
laplace_samples(model, data, n, key, path='env', npix=None, fov_mas=None, *, priors=None, regularizers=())
Draws from the Laplace posterior of a Gaussian-field image.
The whitened latents z are drawn from N(z₀, (I + JᵀJ)⁻¹) about the
MAP z₀, the approximation of log_evidence.
With priors, the latents' covariance is the marginal one of the full
Hessian diag(I, 0) + JᵀJ, with the other parameters' columns in J.
Exact only for a linear model, assumes correct error bars, uses the quoted
errors and is conditional on the hyperparameters held in model (or sample
them, as for log_evidence).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
SourceModel or FitResult
|
The MAP model, or the |
required |
data
|
OIData or sequence of OIData
|
The data it was fitted to. |
required |
n
|
int
|
Number of draws. |
required |
key
|
Array
|
A JAX random key. |
required |
path
|
str or tuple of str or None
|
Path of the Image (default |
'env'
|
npix
|
int and float
|
Together, also render the whole model for each draw (see
|
None
|
fov_mas
|
int and float
|
Together, also render the whole model for each draw (see
|
None
|
priors
|
dict or sequence of str
|
Free parameters (priors or paths) besides the latents; they enter the
Hessian through |
None
|
regularizers
|
sequence
|
Fit regularizers; probabilistic ones ( |
()
|
Returns:
| Type | Description |
|---|---|
dict
|
|
error_scale(model, data, path='env', *, by_observable=False, priors=None, regularizers=())
Re-estimate the scale of the error bars from a Gaussian-field fit.
Returns the factor s to multiply every error bar by for consistency with
the fit (s < 1: bars too large), solving s² = χ² / (N − γ) with γ the effective number of
parameters the data measure; see
Error scale. Fit, call this,
rescale with OIData.with_error_scale,
refit, and do so before choosing hyperparameters (bars that are too large
make the evidence over-regularize). s much greater than 1 means a failed
or inadequate model, not a noise fix: unmodelled structure is absorbed into
s. It uses the quoted errors, so no noise= terms or per-dataset
models.
Source: MacKay (1992), references.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
SourceModel or FitResult
|
The MAP model, as for |
required |
data
|
OIData or sequence of OIData
|
The data it was fitted to. |
required |
path
|
str or tuple of str or None
|
Path of the Image (default |
'env'
|
by_observable
|
bool
|
One scale per kind of observable instead of one for all. Data with
gains, closure-phase offsets or other covariance that does not scale
with the errors raise |
False
|
priors
|
dict or sequence of str
|
Free parameters (priors or paths) besides the latents; they enter
through their |
None
|
regularizers
|
sequence
|
Fit regularizers; probabilistic ones ( |
()
|
Returns:
| Type | Description |
|---|---|
float or dict
|
The scale |
chi2_by_observable(model, data, path=None)
Raw χ² of a model for each kind of observable, on the quoted errors.
The errors are the data's own (no noise= terms). Closure phases from
four or more telescopes contribute their whitened combinations, and their
periodic penalty rows (see
whitened_residuals) add to the χ²
but not to the count.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
SourceModel
|
The model. |
required |
data
|
OIData or sequence of OIData
|
The data. |
required |
path
|
str or tuple of str
|
Path(s) of the Gaussian-process Images, as for
|
None
|
Returns:
| Type | Description |
|---|---|
dict
|
|
short_baseline_residual(model, data, quantile=0.2)
Mean whitened visibility residual on the shortest baselines.
A reconstruction that loses flux on large scales leaves a coherent residual
on the shortest frequencies though the total χ² looks fine (the test of
MACIM, Ireland et al. 2006). The residuals are (model − data)/σ, so the
mean of n of them has standard error 1/√n if the model is right.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
SourceModel
|
The model. |
required |
data
|
OIData or sequence of OIData
|
The data. Visibilities in a linear combination ( |
required |
quantile
|
float
|
The fraction of visibility samples, shortest first by B/λ, that are used (default 0.2). |
0.2
|
Returns:
| Type | Description |
|---|---|
tuple of float
|
|
residual_image(model, data, npix, pixel_scale_mas, rotation_deg=0.0)
The residual dirty map of a model, for any observable.
The normalised χ² gradient -g_p / |J e_p| of adding a little flux at
each pixel p (g the gradient of χ², J e_p the response of the
whitened residuals), as clean uses. For linear
data it is the dirty image of the residuals; for closure phases, the
gradient the fit still feels. Positive pixels need more flux; pixels the
model cannot respond to are zero.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
SourceModel
|
The model, which needs a flux for the added point sources to be measured against. |
required |
data
|
OIData or sequence of OIData
|
The data. |
required |
npix
|
(int, float)
|
The centred grid of the map, as in |
required |
pixel_scale_mas
|
(int, float)
|
The centred grid of the map, as in |
required |
rotation_deg
|
float
|
The grid's rotation, as in |
0.0
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Shape |
diagnose(model, data, regularizers=())
Check a model and its data for common imaging pitfalls.
Nothing is printed or warned: print the returned
Diagnosis. Run it on the fitted model, with
the fit's regularizers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
SourceModel
|
The model, normally containing at least one
|
required |
data
|
OIData or sequence of OIData
|
The data. |
required |
regularizers
|
sequence
|
The regularizers of the fit; a
|
()
|
Returns:
| Type | Description |
|---|---|
Diagnosis
|
|
Sky-plane and uv-plane geometry shared by the source models.
Position angles follow the on-sky convention used throughout virgil:
North is +y (up), East is +x (left, i.e. x decreases left-to-right across
an image array's columns), and position angle is measured North to East,
i.e. counter-clockwise from the top in a plot with East to the left. This
matches the coordinate grid produced by :func:image_coordinates.
pixel_offsets(npix, pixel_scale_mas)
Sky offsets (mas) of the pixel centres along one image axis.
(centre - index) * pixel_scale_mas, centre at (npix - 1) / 2: dra
along columns (East left), ddec along rows (North up).
set_ft_defaults(ft=None, cpu_min_points=None, cpu_points_per_side=None, cpu_min_side=None, gpu_min_points=None, gpu_min_side=None, eps_f32=None, eps_f64=None, toeplitz_min_points=None, toeplitz_points_per_pixel=None, toeplitz_max_bytes=None)
Set how ft="auto" chooses the Fourier transform of pixel images.
Only the arguments you pass change. On a CPU the NUFFT is used when
there are at least max(cpu_min_points, cpu_points_per_side * N) points,
N the image's larger side, and its smaller side is at least
cpu_min_side; on a GPU when there are at least gpu_min_points
points and the smaller side is at least gpu_min_side. eps_f32 and
eps_f64 are its tolerances, relative to the zero-baseline visibility.
chi2="auto" radio terms use the dirty-beam form when they have at least
max(toeplitz_min_points, toeplitz_points_per_pixel * n_ddid * N²)
points, N the image side (ComplexVisData.term).
The settings are read when a function is traced: a jitted function that
has already compiled keeps its old choice. The toeplitz_* ones are read
at each fit.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ft
|
(auto, dft, nufft)
|
What models with |
"auto"
|
cpu_min_points
|
float
|
The CPU thresholds above. |
None
|
cpu_points_per_side
|
float
|
The CPU thresholds above. |
None
|
cpu_min_side
|
float
|
The CPU thresholds above. |
None
|
gpu_min_points
|
float
|
The GPU thresholds above. |
None
|
gpu_min_side
|
float
|
The GPU thresholds above. |
None
|
eps_f32
|
float
|
NUFFT tolerances for float32 and float64 images. |
None
|
eps_f64
|
float
|
NUFFT tolerances for float32 and float64 images. |
None
|
toeplitz_min_points
|
float
|
The size thresholds of |
None
|
toeplitz_points_per_pixel
|
float
|
The size thresholds of |
None
|
toeplitz_max_bytes
|
float
|
The size thresholds of |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
The previous settings: |