virgil.likelihood
Likelihoods of source models given data, and numpyro models.
Likelihoods of source models given interferometric data.
A model is given either as a template SourceModel,
whose parameters at dot-separated zodiax paths (e.g. "comp.flux") are
replaced, or as a class/callable called with the parameters as keyword
arguments (see build_model).
Every likelihood, grid, limit and fit goes through one residual vector,
whitened_residuals: the
residuals divided by their uncertainties, with unprojected phases measured
as a chord, 2 sin(Δ/2), so that the likelihood is smooth where phases
wrap at ±π. Closure phases from four or more telescopes are correlated:
their independent combinations are whitened together, using the sine
sin Δ (not the chord) of each residual, with a periodic penalty
2 sin²(Δ/2) / σ added per closure phase. Both terms repeat every 2π and
are smooth, so the likelihood is continuous everywhere.
whitened_residuals(model, data, **noise)
Residuals of a model divided by the data uncertainties.
This is the one residual vector behind every likelihood in virgil:
model_loglike is -0.5 * sum(whitened_residuals**2) plus the
Gaussian normalization, and least-squares fits minimize its sum of
squares.
Visibility and projected-phase (kernel or DISCO) residuals are
(model - data) / σ. Unprojected phase residuals Δ are
2 sin(Δ/2) / σ: equal to Δ/σ for small Δ, but smooth where Δ wraps
at ±π, so that a χ² surface has no kinks there. The resulting
likelihood is a von Mises distribution with concentration 1/σ²,
normalized exactly on the circle.
Closure phases from four or more telescopes are the exception: they are
correlated, so the sines sin Δ of the residuals are whitened together
and replaced by their independent combinations, and a periodic penalty
2 sin²(Δ/2)/σ per closure phase is appended (chords would flip sign at
Δ → Δ + 2π, making correlated combinations discontinuous). The χ² is
continuous and smooth everywhere, equals the correlated Gaussian
ΔᵀC⁻¹Δ to O(Δ³) for small residuals, and has no false minimum at
Δ = π. There are then n_independent + (number of closure phases)
residuals, namely OIData.n_residuals.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
SourceModel
|
Model to evaluate. |
required |
data
|
OIData
|
Data to compare with. |
required |
**noise
|
Error-inflation terms, |
{}
|
Returns:
| Type | Description |
|---|---|
array - like
|
One dimensionless residual per independent observable
( |
build_model(model, params, values)
Build a model from parameter names and values.
model is either a class/callable, called as model(**dict(zip(params,
values))), or a SourceModel instance used as a template whose
leaves at the (dot-separated) paths params are replaced by values.
model_loglike(model, data, *, reject_unphysical=False, **noise)
Evaluate the log likelihood for an instantiated model object.
This is -0.5 * sum(r**2) for the residuals r of
whitened_residuals, plus
each density's normalization: Gaussian in visibilities and projected
phases, -log σ - ½ log 2π; von Mises with concentration
κ = 1/σ² in uncorrelated unprojected phases, -log 2π - log i0e(κ),
which is the Gaussian one for σ ≪ 1 but stays a normalized density
on the circle when σ is large (e.g. a fitted phi_error).
Correlated closure phases (OIData.cp_noise) keep the Gaussian
normalization, their small-σ limit, since a correlated circular
density has no closed-form normalizer; their periodic penalty
residuals add nothing to it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
SourceModel
|
Model to evaluate. |
required |
data
|
OIData
|
Data to compare with. |
required |
reject_unphysical
|
bool
|
If True, return |
False
|
**noise
|
Error-inflation terms, e.g. fitted as nuisance parameters:
|
{}
|
inflated_errors(data, prediction, vis_error_rel=None, phi_error=None, vis_scale=None, phi_scale=None, vis_error=None, *, where='model', combine='quadrature')
The data uncertainties, scaled and with extra terms in quadrature.
The visibility errors become
hypot(vis_scale σ, vis_error, vis_error_rel V), for the model
visibility observable V, and the phase errors
hypot(phi_scale σ, phi_error). Terms left as None are not
applied. Extra observables (OIData.extras) are left unchanged;
give them floors with
OIData.with_error_floor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
OIData
|
Data whose uncertainties are inflated. |
required |
prediction
|
array - like
|
Model vector, e.g. from |
required |
vis_error_rel
|
float
|
Extra visibility error, as a fraction of the model visibility observable (e.g. of the model V² for squared visibilities): a calibration error. |
None
|
phi_error
|
float
|
Extra phase error in radians. |
None
|
vis_scale
|
float
|
Factors multiplying the visibility and phase uncertainties. |
None
|
phi_scale
|
float
|
Factors multiplying the visibility and phase uncertainties. |
None
|
vis_error
|
float
|
Extra absolute visibility error, in the units of the observable. |
None
|
where
|
(model, data)
|
What |
"model"
|
combine
|
(quadrature, max)
|
Add the terms in quadrature (default), or take the largest (a
floor, as |
"quadrature"
|
Returns:
| Type | Description |
|---|---|
array - like
|
Uncertainties matching |
flux_scale_posterior(model, data)
The grey scales of the extra spectra, given a model.
The scales (and polynomial coefficients) of OI_FLUX spectra and
correlated fluxes are marginalized in the likelihood (see
FluxSpectrum); this is their
Gaussian posterior conditional on model, for reporting.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
SourceModel
|
E.g. the best fit. |
required |
data
|
OIData
|
Data with extra spectra ( |
required |
Returns:
| Type | Description |
|---|---|
dict
|
Per kind ( |
noise_sites(noise, n_datasets)
Expand a noise specification into named sites.
noise maps error-inflation terms (NOISE_TERMS) to priors and
applies to every dataset, giving sites "noise.<term>"; a list of such
dicts, one per dataset, gives sites "noise[i].<term>". An entry may
also be tied: a function of the dict of sampled parameter values
(keyed like priors) that returns the term's value, which then has
no prior of its own (see is_tied),
unless it has a log_prior(values) method (see
tied_log_prior).
Returns:
| Type | Description |
|---|---|
dict
|
|
is_tied(spec)
Whether a noise entry is tied: a function of the sampled
parameters (rather than a prior), e.g. one epoch's error scale drawn
from a population (see
hierarchical_scales).
tied_log_prior(sites, values)
The summed log_prior(values) of the distinct tied noise
terms that have one, such as the population density of a centred
hierarchical_scales member
(a term used twice counts once).
noise_for(sites, values, index)
The error-inflation terms of dataset index, from site values.
loglike(values, params, model, data, **options)
Gaussian log-likelihood of a model with the given parameter values, assuming Gaussian errors.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
values
|
array - like
|
Values of the model parameters. |
required |
params
|
list
|
List of parameter names. |
required |
model
|
SourceModel or callable
|
Template model whose parameters at the dot-separated paths |
required |
data
|
OIData
|
Object containing the data to be fitted. |
required |
**options
|
Error terms and |
{}
|
Returns:
| Type | Description |
|---|---|
float
|
Log-likelihood value. |
joint_prediction(params, model, data)
Concatenate predictions for a parameter pytree and multiple data.
model(params, index) defines which parameters are shared and which
are specific to each observation.
joint_data(data)
Concatenate observed vectors in the same order as joint_prediction.
joint_errors(data)
Concatenate uncertainty vectors in the same order as joint_prediction.
joint_loglike(params, model, data, **options)
Sum independent Gaussian log likelihoods over multiple data.
options (error terms and reject_unphysical) are
passed to model_loglike.
numpyro_model(model, priors, data, regularizers=(), noise=None, likelihoods=(), **options)
Return a numpyro model sampling the parameters in priors.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
SourceModel or callable
|
Either a template model whose leaves at the paths in |
required |
priors
|
dict[str, Distribution]
|
Mapping from parameter path (e.g. |
required |
data
|
OIData or sequence of OIData
|
Data whose Gaussian log likelihood is added with |
required |
regularizers
|
sequence
|
()
|
|
noise
|
dict or list of dict
|
Priors on error-inflation terms ( |
None
|
likelihoods
|
sequence
|
Extra data terms, as for |
()
|
**options
|
Fixed error terms and |
{}
|
Returns:
| Type | Description |
|---|---|
callable
|
Zero-argument numpyro model, e.g. for |
Examples:
Sample an orbit from measured positions alone, with no OIData:
>>> import numpy as onp
>>> import numpyro.distributions as dist
>>> from virgil.likelihood import numpyro_model
>>> from virgil.orbits import KeplerOrbit, PositionData
>>> mjd = 60500.0 + onp.array([0.0, 100.0, 200.0, 300.0])
>>> truth = KeplerOrbit(400.0, 30.0, 0.4, 60.0, 40.0, 110.0, 20.0, t_ref=60500.0)
>>> dra, ddec, _ = (onp.asarray(x) for x in truth.relative(mjd))
>>> cov = onp.broadcast_to(0.05**2 * onp.eye(2), (4, 2, 2))
>>> positions = PositionData(mjd, dra, ddec, cov)
>>> priors = {"a_mas": dist.LogUniform(5.0, 50.0), "ecc": dist.Uniform(0.0, 0.9)}
>>> def orbit_fn(v):
... return KeplerOrbit(
... 400.0, 30.0, v["ecc"], 60.0, 40.0, 110.0, v["a_mas"], t_ref=60500.0
... )
>>> model = numpyro_model(
... lambda **kw: None, priors, (), likelihoods=[positions.term(orbit_fn)]
... )
>>> from numpyro.infer.util import log_density
>>> values = {"a_mas": 20.0, "ecc": 0.4}
>>> bool(onp.isfinite(float(log_density(model, (), {}, values)[0])))
True
chain_init_params(model, starts, key=None)
Initial parameters for numpyro's MCMC, one chain per start.
init_to_value starts every chain at one point. To start each
chain in its own mode (e.g. the distinct fits of
OrbitStart.chain_values),
numpyro instead takes init_params, in its unconstrained
coordinates and with a leading axis over chains; this converts one
dict of values per chain into them.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
callable
|
The numpyro model, e.g. from
|
required |
starts
|
sequence of dict
|
One dict of values per chain, keyed by sample site as
|
required |
key
|
Array
|
Random key for those missing sites (default |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
Unconstrained parameters by site, with a leading axis of
|
Examples:
>>> posterior = numpyro_model(model, priors, data)
>>> init = chain_init_params(posterior, start.chain_values(4))
>>> mcmc = MCMC(NUTS(posterior), num_warmup=500, num_samples=500,
... num_chains=4, chain_method="vectorized")
>>> mcmc.run(jax.random.PRNGKey(1), init_params=init)
posterior_predictive_summary(samples, model, data, params=None)
Mean and spread of the model observables over posterior samples.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
samples
|
dict[str, array - like]
|
Posterior samples, as equal-length 1D arrays keyed by parameter
name or path (e.g. from |
required |
model
|
SourceModel or callable
|
Template model or class, as for :func: |
required |
data
|
OIData
|
Data defining the observables. |
required |
params
|
list[str]
|
Which keys of |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
|