virgil.ensemble
Ensembles of randomized image reconstructions in the manner of PYRA and
MYTHRA (Drevon et al. 2026): draw regularizer families, weights, pixel
sizes, fields and starting images; fit each group as an L-curve; select the
members that fit the data and average them into a mean image with a
per-pixel spread. A group can instead use a Gaussian-process prior whose
hyperparameters the evidence chooses (evidence_grid, gp_members); combine
averages both kinds of member. See the tutorial Imaging, part 7.
Ensembles of randomized image reconstructions, averaged into one image.
A single regularized reconstruction depends on choices the data do not fix: the regularizer and its weight, the pixel size, the field and the starting image. Following Drevon et al. (2026, https://arxiv.org/abs/2609.15365), this module runs many reconstructions with these choices drawn at random, keeps those that fit the data, and averages them while the average still fits. The mean is less sensitive to any one choice than a single reconstruction, and the spread of the members maps how much the image depends on those choices. It is written from the paper's description, not from their code; see Imaging, part 7.
The work is split so that a cluster can run it in parallel:
draw_groupsdraws the settings of each group: one geometry, regularizer family and starting image, and several weights.run_groupfits one group as anl_curve; groups that share a geometry and a family share a compilation. A group of family"gp"is instead anevidence_gridover a Gaussian-process prior (gp_members).combineselects the members and averages them into anEnsemble.
ensemble runs all three. The ensemble's
standard deviation is not a posterior uncertainty: it measures how much the
image changes between reasonable reconstruction choices, not the noise in the
data.
__provisional__ = ('evidence_grid', 'gp_members', 'GPFit', 'EvidenceGrid')
module-attribute
FAMILIES = {'tv': TV, 'tsv': TSV, 'maxent': MaxEntropy, 'starlet': StarletL1}
module-attribute
EnsembleSpec
dataclass
What an ensemble draws at random, and how it selects members.
See Imaging, part 7.
Attributes:
| Name | Type | Description |
|---|---|---|
families |
tuple of str
|
Families drawn uniformly: |
weight_ranges |
dict
|
Per family, the |
oversample |
tuple of float
|
Pixels per Nyquist pixel. |
field_factors |
tuple of float
|
The field is |
field_mas, aperture_mas |
float or None
|
Base field (default |
starts |
tuple of str
|
Starts drawn uniformly: |
max_npix |
int
|
Most pixels on a side; larger fields get coarser pixels, then a crop. |
window_dex |
float
|
A group keeps weights from |
chi2_ratio, mad_cut |
float
|
Drop members whose raw χ² per point on any dataset exceeds
|
max_chi2_red |
float or None
|
Deprecated absolute cut; use |
max_shift_mas |
float or None
|
Largest recentring shift without a star (default: beam major axis). |
mean_rtol |
float or None
|
Tolerance on the mean's χ² excess over the best member's (default
|
min_kept |
int
|
|
gp_sigmas, gp_lengths, gp_sigma_limits, gp_length_limits, gp_growths |
GP evidence grid ( |
|
gp_window_nats |
float
|
GP members are fits within this log evidence of the best. |
gp_axis_ratio |
float or None
|
Add anisotropic fields if a Gaussian fits below this axis ratio. |
families = ('tv', 'tsv', 'maxent', 'starlet')
class-attribute
instance-attribute
weight_ranges = dataclasses.field(default_factory=lambda: {'tv': (0.0001, 10.0), 'tsv': (0.1, 10000.0), 'maxent': (0.0001, 10.0), 'starlet': (0.0001, 10.0)})
class-attribute
instance-attribute
n_weights = 8
class-attribute
instance-attribute
oversample = (2.0, 3.0, 4.0)
class-attribute
instance-attribute
field_factors = (0.75, 1.0, 1.5)
class-attribute
instance-attribute
starts = ('moments', 'flat')
class-attribute
instance-attribute
max_npix = 128
class-attribute
instance-attribute
window_dex = 1.0
class-attribute
instance-attribute
max_chi2_red = None
class-attribute
instance-attribute
chi2_ratio = 2.0
class-attribute
instance-attribute
mad_cut = 5.0
class-attribute
instance-attribute
max_shift_mas = None
class-attribute
instance-attribute
mean_rtol = None
class-attribute
instance-attribute
min_kept = 3
class-attribute
instance-attribute
field_mas = None
class-attribute
instance-attribute
aperture_mas = None
class-attribute
instance-attribute
gp_sigmas = (1.0, 2.0, 4.0)
class-attribute
instance-attribute
gp_lengths = (0.5, 1.0, 2.0)
class-attribute
instance-attribute
gp_sigma_limits = (0.25, 16.0)
class-attribute
instance-attribute
gp_length_limits = (0.125, 8.0)
class-attribute
instance-attribute
gp_growths = 3
class-attribute
instance-attribute
gp_window_nats = 2.0
class-attribute
instance-attribute
gp_axis_ratio = 0.9
class-attribute
instance-attribute
__init__(families=('tv', 'tsv', 'maxent', 'starlet'), weight_ranges=(lambda: {'tv': (0.0001, 10.0), 'tsv': (0.1, 10000.0), 'maxent': (0.0001, 10.0), 'starlet': (0.0001, 10.0)})(), n_weights=8, oversample=(2.0, 3.0, 4.0), field_factors=(0.75, 1.0, 1.5), starts=('moments', 'flat'), max_npix=128, window_dex=1.0, max_chi2_red=None, chi2_ratio=2.0, mad_cut=5.0, max_shift_mas=None, mean_rtol=None, min_kept=3, field_mas=None, aperture_mas=None, gp_sigmas=(1.0, 2.0, 4.0), gp_lengths=(0.5, 1.0, 2.0), gp_sigma_limits=(0.25, 16.0), gp_length_limits=(0.125, 8.0), gp_growths=3, gp_window_nats=2.0, gp_axis_ratio=0.9)
__post_init__()
Draw
dataclass
The settings of one group of reconstructions.
Attributes:
| Name | Type | Description |
|---|---|---|
index |
int
|
Position of the group in the ensemble. |
family |
str
|
The regularizer family, or |
npix |
int
|
Pixels on a side. |
pixel_scale_mas |
float
|
Pixel size in mas. |
start |
str
|
The starting image. |
weights |
tuple of float
|
The regularizer weights, largest first (empty for |
index
instance-attribute
family
instance-attribute
npix
instance-attribute
pixel_scale_mas
instance-attribute
start
instance-attribute
weights
instance-attribute
geometry
property
(family, npix, pixel_scale_mas): groups sharing it share a
compilation.
__init__(index, family, npix, pixel_scale_mas, start, weights)
Group
dataclass
The result of run_group.
Attributes:
| Name | Type | Description |
|---|---|---|
draw |
Draw
|
The group's settings. |
curve |
LCurve or None
|
Its fits, one per weight, largest weight first; |
gp |
tuple of GPFit
|
For a Gaussian-process group
( |
grid |
EvidenceGrid or None
|
For a Gaussian-process group, the evidence grid they come from. |
draw
instance-attribute
curve
instance-attribute
gp = ()
class-attribute
instance-attribute
grid = None
class-attribute
instance-attribute
__init__(draw, curve, gp=(), grid=None)
Member
dataclass
One reconstruction in an ensemble.
Attributes:
| Name | Type | Description |
|---|---|---|
draw |
Draw
|
The settings of its group. |
weight |
float
|
Its regularizer weight (NaN for a Gaussian-process member). |
result |
FitResult
|
The fit. |
chi2_red |
tuple of float
|
Raw χ² per data point of each dataset (with the quoted errors). |
kept |
bool
|
Whether it is in the mean image. |
gp |
GPFit or None
|
For a Gaussian-process member, its hyperparameters and evidence. |
reason |
str or None
|
Why it was left out: |
draw
instance-attribute
weight
instance-attribute
result
instance-attribute
chi2_red
instance-attribute
kept = False
class-attribute
instance-attribute
reason = None
class-attribute
instance-attribute
gp = None
class-attribute
instance-attribute
total_chi2_red
property
χ² per data point over all datasets.
__init__(draw, weight, result, chi2_red, kept=False, reason=None, gp=None)
Ensemble
dataclass
The result of combine.
Attributes:
| Name | Type | Description |
|---|---|---|
model |
System
|
The mean scene, exactly: the kept members' images on their own grids
( |
mean |
Image
|
The mean extended emission on a common grid (the finest pixels and the
largest field of the kept members), summing to one; with a star its
|
std |
(array, shape(npix, npix))
|
Standard deviation across the kept members of |
chi2_red |
tuple of float
|
Raw χ² per data point of the mean scene on each dataset. |
trace |
list of tuple
|
|
members |
list of Member
|
Every reconstruction, kept or not. |
groups |
list of Group
|
The groups, with their L-curves. |
model
instance-attribute
mean
instance-attribute
std
instance-attribute
chi2_red
instance-attribute
trace
instance-attribute
members
instance-attribute
groups
instance-attribute
kept
property
The members in the mean.
__init__(model, mean, std, chi2_red, trace, members, groups)
summary()
A short table: the groups, what was kept, and the mean's fit.
GPFit
dataclass
One Gaussian-process fit of an evidence grid.
Provisional
May change in any 0.4.x release without a deprecation cycle.
Attributes:
| Name | Type | Description |
|---|---|---|
variant |
str
|
|
length_mas |
float
|
Correlation length in mas (for |
sigma |
float
|
Standard deviation of the log-brightness. |
log_evidence |
float
|
|
result |
FitResult
|
The fit. |
variant
instance-attribute
length_mas
instance-attribute
sigma
instance-attribute
log_evidence
instance-attribute
result
instance-attribute
__init__(variant, length_mas, sigma, log_evidence, result)
EvidenceGrid
dataclass
The result of evidence_grid.
Provisional
May change in any 0.4.x release without a deprecation cycle.
Attributes:
| Name | Type | Description |
|---|---|---|
lengths_mas, sigmas |
dict of tuple
|
For each variant ( |
log_evidence |
dict of array
|
Per variant, the log evidence, with rows over |
fits |
dict
|
|
best |
GPFit
|
The fit of largest evidence. |
interior |
bool
|
Whether the best point is inside its grid's edges: on an edge the optimum may lie beyond it, because the growth stopped at a limit. |
axis_ratio, pa_deg |
float or None
|
The elliptical-Gaussian fit's minor-to-major ratio and position
angle ( |
lengths_mas
instance-attribute
sigmas
instance-attribute
log_evidence
instance-attribute
fits
instance-attribute
best
instance-attribute
interior
instance-attribute
axis_ratio = None
class-attribute
instance-attribute
pa_deg = None
class-attribute
instance-attribute
__init__(lengths_mas, sigmas, log_evidence, fits, best, interior, axis_ratio=None, pa_deg=None)
draw_groups(data, n_groups, key, spec=None, star=True)
Draw the settings of n_groups groups of reconstructions.
Each group draws a regularizer family, pixel size, field, starting image
and spec.n_weights log-uniform weights, as set by the
EnsembleSpec. The draws depend only on
key and spec, so every task of a cluster array can draw them all and
run its own.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
OIData or sequence of OIData
|
The data. |
required |
n_groups
|
int
|
Number of groups. |
required |
key
|
PRNGKey
|
The random key. |
required |
spec
|
EnsembleSpec
|
What to draw (default |
None
|
star
|
bool
|
Whether the scene has an unresolved star (default), which the field's
envelope fit then allows for
( |
True
|
Returns:
| Type | Description |
|---|---|
list of Draw
|
Sorted by geometry, so that groups sharing a compilation run one after the other. |
reference_starts(data, star=True, starts=('moments', 'flat'), aperture_mas=None)
The starting images that each group resamples onto its own grid.
Each is made once by starting_image,
which fits a star plus a Gaussian envelope: "moments" is that
Gaussian, "dirty" the positive part of the dirty image, and
"flat" a uniform image with the fitted flux.
aperture_mas : float, optional
The aperture, as for EnsembleSpec.
Returns:
| Type | Description |
|---|---|
dict
|
Start name to an |
run_group(data, draw, *, star=True, starts=None, spec=None, **fit_options)
Fit one group of an ensemble: an L-curve over its weights.
A group of family "gp" is instead an evidence grid
(gp_members).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
OIData or sequence of OIData
|
The data. |
required |
draw
|
Draw
|
The group, from |
required |
star
|
bool
|
Whether the scene is an unresolved star at the origin plus the
image (default), or the image alone, centred by a
|
True
|
starts
|
dict
|
From |
None
|
spec
|
EnsembleSpec
|
Used only by a |
None
|
**fit_options
|
{}
|
Returns:
| Type | Description |
|---|---|
Group
|
|
evidence_grid(data, draw, *, star=True, starts=None, spec=None, **fit_options)
Fit Gaussian-process images over a grid of amplitude and length.
Provisional
May change in any 0.4.x release without a deprecation cycle.
Each point is a MAP fit of an image whose log-brightness is a
GaussianField of amplitude σ and
correlation length ℓ about the group's starting image, scored by
log_evidence. The grid starts at
spec.gp_sigmas by spec.gp_lengths and grows while its best point is
on an edge; for elongated data a second grid is anisotropic. See
Imaging, part 7. The
evidence assumes correct error bars; check them with
error_scale.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
OIData or sequence of OIData
|
The data. |
required |
draw
|
Draw
|
Geometry and starting image, as from
|
required |
star
|
bool
|
As for |
True
|
starts
|
dict
|
From |
None
|
spec
|
EnsembleSpec
|
The grid's settings (default |
None
|
**fit_options
|
Passed to |
{}
|
Returns:
| Type | Description |
|---|---|
EvidenceGrid
|
|
gp_members(data, draw, *, star=True, starts=None, spec=None, **fit_options)
Fit one Gaussian-process group of an ensemble.
Provisional
May change in any 0.4.x release without a deprecation cycle.
The group's members are the fits of its
evidence_grid whose log evidence is
within spec.gp_window_nats of the best, so that
combine averages over the amplitude and length
the data leave open. There is no L-curve window.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
As for |
required | |
draw
|
As for |
required | |
star
|
As for |
required | |
starts
|
As for |
required | |
spec
|
As for |
required | |
**fit_options
|
As for |
required |
Returns:
| Type | Description |
|---|---|
Group
|
With |
combine(data, groups, *, spec=None, star=True)
Select the members of an ensemble and average them.
The selection follows MYTHRA (Drevon et al. 2026): keep the weights just
before each L-curve's corner (Gaussian-process members skip this), drop
diverged, badly fitting and outlying members, resample the survivors to a
common grid, and add each to a running mean, in order of χ², only if the
mean still fits every dataset as well as the best member, up to
spec.mean_rtol. See Imaging, part 7.
With a star, the mean is of the whole normalized sky, star included.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
OIData or sequence of OIData
|
The data the groups were fitted to. Give visibilities and closure phases as separate datasets to judge each separately. |
required |
groups
|
sequence of Group
|
From |
required |
spec
|
EnsembleSpec
|
The selection settings (default |
None
|
star
|
bool
|
As for |
True
|
Returns:
| Type | Description |
|---|---|
Ensemble
|
|
ensemble(data, n_groups, key, *, spec=None, star=True, **fit_options)
Run, select and average an ensemble of randomized reconstructions.
draw_groups, then
run_group for each group in turn, then
combine. Each group is one L-curve, so the
ensemble has n_groups * spec.n_weights members, and compiles once
per distinct (family, npix, pixel_scale_mas): a handful, since
each is drawn from a short list.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
OIData or sequence of OIData
|
The data. Give visibilities and closure phases as separate datasets for the mean to be judged on each. |
required |
n_groups
|
int
|
Number of groups. |
required |
key
|
PRNGKey
|
The random key. |
required |
spec
|
EnsembleSpec
|
What to draw and how to select (default |
None
|
star
|
bool
|
Whether the scene has an unresolved star at the origin (default). |
True
|
**fit_options
|
Passed to |
{}
|
Returns:
| Type | Description |
|---|---|
Ensemble
|
|