virgil.orbit_search
Scoring candidate orbits on multi-epoch data with the nuisances an orbit shares across epochs shared in the score: one companion flux per band (with an optional chromatic slope) integrated out on a log-uniform grid, each dataset's error scales marginalized, calibration gains and closure-phase offsets profiled, and extra terms such as radial velocities added. Positions are evaluated at every sample's own time.
Details
For a scene linear in the companion's flux f, the complex visibility is
V(f) = (A + f B) / (T₀ + f ΔT), with A and the total fluxes computed
once per dataset and B from the visibilities of a unit companion.
The coarse flux grid only finds the peak. Newton steps in ln f (and the slope β) profile it, and Gauss–Legendre nodes on each side of it, out to a 12-nat drop or the prior's bounds, integrate it: a well-measured flux (σ_ln f ≈ 0.02) is far narrower than any affordable grid step.
With s_max=None a block's −(ν/2) ln χ² is unbounded as χ² → 0, so a
block with few degrees of freedom (one triangle's closure phases, say)
that a flux or slope fits almost exactly makes a narrow spike in the
score, which can dominate the profile (profiled, flux, slope). The
default s_max=5 leaves about twice the error inflation of typical
interferometric data and bounds each block's gain at about ν ln s_max.
Scoring candidate orbits on multi-epoch data, with shared nuisances.
Provisional
May change in any 0.4.x release without a deprecation cycle.
score_orbits scores many candidate orbits
exactly on all the data at once, sharing across epochs the nuisances that one
orbit shares:
- Flux. One companion flux per band, optionally with a chromatic power law f(lambda) = f0 (lambda/lambda0)^beta, integrated out under a log-uniform prior about its profiled peak. A flux per epoch is a strictly looser model that makes peaks no single-flux orbit visits, so it is never used to rank.
- Error scales are integrated out block by block
(
marginal_loglike). - Gains and closure-phase offsets
(
OIData.with_gains,OIData.with_closure_offsets) are per epoch and linear, so they are profiled analytically, not marginalized, keeping a block's chi-squared proportional to 1/s^2.
The companion is placed at every sample's own time, so motion within a night
needs no threshold. Extra terms such as radial velocities
(RVData) or positions
(PositionData) are added to the score.
SharedFlux
dataclass
The companion flux shared across epochs, per band.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grid
|
(tuple, array - like or dict)
|
Flux grid at the reference wavelength: |
(0.001, 1.0, 16)
|
bands
|
dict
|
|
None
|
reference
|
optional
|
The band whose flux is at most 1 (default the first); it fixes which star is the companion in every band; the others may have grids above 1. |
None
|
slope
|
(tuple, array - like or dict)
|
The chromatic slope beta, |
None
|
wavel0
|
float or dict
|
Reference wavelength (m); default the geometric mean per band. |
None
|
order
|
int
|
Gauss-Legendre nodes per side of the marginal (16; each side runs to 12 nats below the peak, or the prior bound) and Newton steps to the profiled peak (8). |
16
|
newton
|
int
|
Gauss-Legendre nodes per side of the marginal (16; each side runs to 12 nats below the peak, or the prior bound) and Newton steps to the profiled peak (8). |
16
|
grid = (0.001, 1.0, 16)
class-attribute
instance-attribute
bands = None
class-attribute
instance-attribute
reference = None
class-attribute
instance-attribute
slope = None
class-attribute
instance-attribute
wavel0 = None
class-attribute
instance-attribute
order = 16
class-attribute
instance-attribute
newton = 8
class-attribute
instance-attribute
__init__(grid=(0.001, 1.0, 16), bands=None, reference=None, slope=None, wavel0=None, order=16, newton=8)
__post_init__()
OrbitScores
dataclass
The scores of candidate orbits, in the order they were given.
Returned by score_orbits; rank with
:meth:order or rank_scores.
Attributes:
| Name | Type | Description |
|---|---|---|
score, profiled |
ndarray
|
|
flux, flux_err, flux_at_edge, slope, slope_err, slope_at_edge |
ndarray
|
|
fallback |
ndarray
|
|
terms |
ndarray
|
|
scale, scale_at_bound |
ndarray
|
|
bands, reference, cost |
(tuple, object, int)
|
The band names, the reference band, and the work units spent (candidates times each dataset's band-marginal score evaluations). |
score
instance-attribute
profiled
instance-attribute
flux
instance-attribute
flux_err
instance-attribute
flux_at_edge
instance-attribute
slope
instance-attribute
slope_err
instance-attribute
slope_at_edge
instance-attribute
fallback
instance-attribute
terms
instance-attribute
scale
instance-attribute
scale_at_bound
instance-attribute
bands
instance-attribute
reference
instance-attribute
cost
instance-attribute
__init__(score, profiled, flux, flux_err, flux_at_edge, slope, slope_err, slope_at_edge, fallback, terms, scale, scale_at_bound, bands, reference, cost)
__len__()
order(quantum=_QUANTUM)
Candidate indices, best first, ties broken by index (see
rank_scores).
score_orbits(model, epochs, orbits, *, shared=None, terms=(), scales='marginal', dof=1.0, s_max=5.0, batch_size=None, max_evaluations=None, dtype='float64')
Score candidate orbits on all epochs, with the flux shared.
Unlike rank_orbits, each companion is placed at every sample's own
time. The score is the scale-marginalized log likelihood with the flux
integrated out and the extra terms added (details).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
callable
|
|
required |
epochs
|
Epochs
|
The data; datasets without sample times use |
required |
orbits
|
sequence or orbit
|
Orbits (or |
required |
shared
|
SharedFlux
|
Flux grids, bands and slopes (default one band, f in [1e-3, 1]). |
None
|
terms
|
sequence
|
Extra log likelihood terms or callables |
()
|
scales
|
optional
|
As for |
'marginal'
|
dof
|
optional
|
As for |
'marginal'
|
s_max
|
optional
|
As for |
'marginal'
|
batch_size
|
optional
|
Candidates per step (never changes the result); a budget in score
evaluations (over it raises |
None
|
max_evaluations
|
optional
|
Candidates per step (never changes the result); a budget in score
evaluations (over it raises |
None
|
dtype
|
optional
|
Candidates per step (never changes the result); a budget in score
evaluations (over it raises |
None
|
Returns:
| Type | Description |
|---|---|
OrbitScores
|
In the order of |
rank_scores(score, quantum=_QUANTUM)
Indices that sort score from best to worst, deterministically.
Scores are first rounded to multiples of quantum (nats), and equal
rounded scores rank by index, so that two candidates differing only by
rounding error (e.g. mirror-image grid cells) rank the same way on
every platform and in either precision. Non-finite scores rank last.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
score
|
array - like
|
One score per candidate, higher is better. |
required |
quantum
|
float
|
The rounding step (default 1e-3). |
_QUANTUM
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Integer indices, best first. |
Examples:
>>> rank_scores([1.0, 3.0, 3.0 + 1e-9, -float("inf"), 2.0]).tolist()
[1, 2, 4, 0, 3]