virgil.detection
Detection statistics of a companion search over a grid: the profile
likelihood ratio Δχ², the grid-marginalized log Bayes factor and the best
flux SNR. They are traceable in the data, so they can be computed for many
simulated observations under jax.lax.map with one compilation, which is
what empirical false-alarm probabilities and ROC curves need.
The Monte Carlo: gaussian_null and bootstrap_null simulate the null
hypothesis (Gaussian noise from the errors, or a residual bootstrap of the
data), rescale_errors first brings the null to χ²_r = 1,
injection_grid lays out companions to inject, and injection_recovery
runs the search on null and injected simulations with one compiled kernel.
Its DetectionMC result gives empirical false-alarm probabilities,
thresholds, ROC curves, completeness maps and contrast curves, and saves,
loads and concatenates for array jobs.
Detection statistics, null simulators and injection-recovery for ROC curves.
detection_statistics reduces a
companion search over a grid to delta_chi2, log_bayes_factor and
max_snr; it is traceable in the data, so it compiles once under
jax.lax.map. gaussian_null and
bootstrap_null simulate the null,
injection_recovery runs the statistics
on null and injected draws, and its DetectionMC
gives false-alarm probabilities, thresholds, ROC curves and contrast curves.
See Detection statistics and the Detection ROC curves tutorial.
DetectionMC
dataclass
Statistics of simulated null and injected companion searches.
Made by injection_recovery; plain
NumPy. Conventions are in
Detection statistics.
Source: Fawcett (2006), references.
Attributes:
| Name | Type | Description |
|---|---|---|
null |
dict[str, ndarray]
|
Per null draw: the three statistics, |
injected |
dict[str, ndarray]
|
The same per injected draw, plus the injected values by name. |
meta |
dict
|
JSON-serializable: grid, fingerprints, noise model, |
null
instance-attribute
injected
instance-attribute
meta
instance-attribute
n_null
property
Number of null draws.
n_injected
property
Number of injected draws.
__init__(null, injected, meta)
__repr__()
separations()
Separations of the injections in mas.
hypot(dra, ddec) for Cartesian grids, or the injected sep
for angular ones (sep, pa in degrees).
matched()
Whether each injection's best position is within match_radius.
All True when meta["match_radius"] is None. Angular positions
(sep, pa in degrees) are compared on the sky.
false_alarm_probability(stat, value, *, confidence=0.95)
Empirical false-alarm probability of value, with an interval.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stat
|
str
|
|
required |
value
|
float or array - like
|
Observed statistic(s). |
required |
confidence
|
float
|
Confidence of the binomial interval. |
0.95
|
Returns:
| Type | Description |
|---|---|
fap, lower, upper : numpy.ndarray
|
|
threshold(stat, fap, *, n_boot=200, seed=0)
Threshold of stat at false-alarm probability fap.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stat
|
str
|
The statistic. |
required |
fap
|
float
|
False-alarm probability (e.g. 1.35e-3 for one-sided 3σ). |
required |
n_boot
|
int
|
Bootstrap resamples of the null draws, for the error. |
200
|
seed
|
int
|
Seed of the bootstrap. |
0
|
Returns:
| Type | Description |
|---|---|
threshold, error : float
|
The |
Warns:
| Type | Description |
|---|---|
RuntimeWarning
|
If |
detected(stat, fap)
Whether each injection is detected at false-alarm probability fap.
Its statistic exceeds :meth:threshold and, with match_radius,
its best position matches the injection.
roc(stat, flux=None, sep_bin=None)
ROC curve: true- against false-positive rate over thresholds.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stat
|
str
|
The statistic. |
required |
flux
|
float or (float, float)
|
Only injections of this flux (to 1e-6), or with |
None
|
sep_bin
|
(float, float)
|
Only injections with |
None
|
Returns:
| Type | Description |
|---|---|
fpr, tpr, thresholds : numpy.ndarray
|
At each threshold ( |
auc(stat, flux=None, sep_bin=None)
Area under the :meth:roc curve (0.5 for no discrimination).
completeness(stat, fap, sep_bins=None, flux_bins=None)
Detection fraction of the injections by separation and flux.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stat
|
str
|
The statistic. |
required |
fap
|
float
|
False-alarm probability of the detection threshold. |
required |
sep_bins
|
array - like
|
Bin edges (mas, and flux); by default each distinct injected value
(six significant figures) is a bin, which suits
|
None
|
flux_bins
|
array - like
|
Bin edges (mas, and flux); by default each distinct injected value
(six significant figures) is a bin, which suits
|
None
|
Returns:
| Type | Description |
|---|---|
dict
|
|
contrast_curve(stat, fap, completeness=0.5, sep_bins=None, flux_bins=None)
Flux detected with a given completeness, against separation.
Per separation bin of :meth:completeness, the detection fraction is
made non-decreasing in flux and interpolated in log flux to where it
first reaches completeness.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stat
|
str
|
The statistic. |
required |
fap
|
float
|
False-alarm probability of the detection threshold. |
required |
completeness
|
float
|
Target detection fraction. |
0.5
|
sep_bins
|
array - like
|
As for :meth: |
None
|
flux_bins
|
array - like
|
As for :meth: |
None
|
Returns:
| Type | Description |
|---|---|
sep, flux : numpy.ndarray
|
Separations (mas) and flux relative to the primary (convert with
|
save(path)
Write to path, an .npz with the metadata as JSON.
meta["format"] is the file-format version (1).
load(path)
classmethod
Read a file written by :meth:save.
A file without meta["format"] is read as version 1. Any other
version than the one this virgil writes raises a ValueError,
rather than being read in a layout it may not have.
concatenate(results)
classmethod
Merge results of one experiment run with different seeds.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
results
|
sequence of DetectionMC
|
E.g. one per array job. They must agree on grid, model, null scene,
template, noise and |
required |
Returns:
| Type | Description |
|---|---|
DetectionMC
|
All draws, with every seed in |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the metadata are incompatible or a seed repeats. |
detection_statistics(model, data, grid, *, flux_param=None, batch_size=None)
Detection statistics of a companion search over a grid.
The null hypothesis is model with the companion flux at zero; see
Detection statistics.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
SourceModel or class
|
Companion model, as for
|
required |
data
|
OIData
|
Data to search; may be traced (a simulation built under |
required |
grid
|
dict[str, array - like]
|
Coordinate axes ( |
required |
flux_param
|
optional
|
Key of |
None
|
batch_size
|
optional
|
Key of |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, Array]
|
Scalars |
Warns:
| Type | Description |
|---|---|
RuntimeWarning
|
With concrete data, if the flux axis does not resolve the peak, the
best flux lies above it, or the optimizer did not converge. Nothing is
checked under |
Examples:
>>> stats = jax.lax.map(
... lambda key: detection_statistics(
... model, template.with_model(null_scene, key=key), grid
... ),
... jax.random.split(jax.random.PRNGKey(0), 100),
... )
local_nsigma(delta_chi2)
Local significance of delta_chi2, in Gaussian sigma.
The significance at one position fixed in advance:
nsigma with one degree of freedom. It ignores the
look-elsewhere effect, so for a search it overstates; see
Detection statistics.
Source: Wilks (1938), Chernoff (1954), references.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
delta_chi2
|
float or array - like
|
Profile likelihood ratio from
|
required |
Returns:
| Type | Description |
|---|---|
array - like
|
|
gaussian_null(null_scene, template, *, error_scale=1.0)
A simulator of the null hypothesis with Gaussian noise.
Each draw is template.with_model(scene, key=key): the scene's prediction
plus Gaussian noise from the template's errors. See
Detection statistics.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
null_scene
|
SourceModel
|
The scene without a companion. |
required |
template
|
OIData
|
Sampling and errors to simulate; its values are not used. |
required |
error_scale
|
float
|
True noise as a multiple of the template's errors, to test statistics under wrong errors. Static: a new value compiles anew. |
1.0
|
Returns:
| Type | Description |
|---|---|
callable
|
|
Examples:
>>> simulate = gaussian_null(BinaryModelCartesian(0, 0, 0), template)
>>> data = simulate(jax.random.PRNGKey(0))
bootstrap_null(null_scene, data, *, method='sign_flip')
A simulator of the null hypothesis by residual bootstrap.
The residuals of data about null_scene are whitened, sign-flipped or
resampled, re-coloured (keeping closure-phase correlations) and added to the
simulated scene's prediction. The null must fit, and a companion in the data
biases the null distribution upward; see
Detection statistics.
Source: Efron (1979), references.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
null_scene
|
SourceModel
|
The scene without a companion, fitted to |
required |
data
|
OIData
|
The real data, concrete, without gains, closure-phase offsets or extras. |
required |
method
|
(sign_flip, resample)
|
A wild bootstrap keeping each residual's magnitude, or resampling with replacement. |
"sign_flip"
|
Returns:
| Type | Description |
|---|---|
callable
|
|
rescale_errors(null_scene, data)
Scale the errors so that the null scene has χ²_r = 1.
Visibility and phase errors are scaled separately, each by
sqrt(χ² / n) of the whitened residuals of null_scene; see
Detection statistics.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
null_scene
|
SourceModel
|
The scene without a companion, fitted to |
required |
data
|
OIData
|
Data to rescale; concrete. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
data |
OIData
|
A copy with |
factors |
dict[str, float]
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If a block fits perfectly or not at all. |
injection_grid(separations, fluxes, n_pa, key)
Companions to inject: every separation and flux at random PAs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
separations
|
array - like
|
Separations in mas (non-negative). |
required |
fluxes
|
array - like
|
Fluxes relative to the primary (non-negative; 0 gives null draws). |
required |
n_pa
|
int
|
Position angles per (separation, flux), uniform in [0°, 360°). |
required |
key
|
Array or int
|
Random key or integer seed. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, ndarray]
|
|
injection_recovery(model, null_scene, template, grid, key, *, n_null, injections=None, noise='gaussian', match_radius=None, flux_param=None, draw_batch=1, chunk_size=None, batch_size=None, progress=True)
Detection statistics of simulated null and injected observations.
Each draw simulates an observation (null, or with a companion injected) and
runs detection_statistics on it
in one compiled kernel; see
Detection statistics.
Compare: Mawet et al. (2014), whose small-sample false-alarm correction is not applied here, references.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
SourceModel or class
|
Companion model: injected at the injected position and flux, null draws at zero flux. |
required |
null_scene
|
(SourceModel, OIData)
|
The scene without a companion (it must equal |
required |
template
|
(SourceModel, OIData)
|
The scene without a companion (it must equal |
required |
grid
|
The search grid and its flux key, as for |
required | |
flux_param
|
The search grid and its flux key, as for |
required | |
key
|
(Array or int, int)
|
Random key or integer seed, and the number of null draws (may be 0). |
required |
n_null
|
(Array or int, int)
|
Random key or integer seed, and the number of null draws (may be 0). |
required |
injections
|
dict[str, array - like]
|
Companions to inject, e.g. from
|
None
|
noise
|
(gaussian, bootstrap)
|
|
"gaussian"
|
match_radius
|
float
|
In mas: an injection counts as detected only if its best position is within this of the injected one (needs position keys). |
None
|
draw_batch
|
int
|
Draws evaluated together in |
1
|
chunk_size
|
int
|
Draws evaluated together in |
1
|
batch_size
|
int
|
Draws evaluated together in |
1
|
progress
|
bool
|
Show a |
True
|
Returns:
| Type | Description |
|---|---|
DetectionMC
|
The statistics of every draw, the injections, and metadata. |
Examples:
>>> mc = injection_recovery(
... BinaryModelCartesian, BinaryModelCartesian(0, 0, 0), template,
... grid, 0, n_null=1000,
... )