virgil.angles
Angles sampled as 2-D vectors, so that no prior has a wrap boundary. Put an
AngleVector in a priors dict under an angle's path (in degrees, such as a
binary's "pa" or an orbit's node), and fit, gauss_newton_mass and
numpyro_model sample a vector v = r(cos θ, sin θ) at the site
"<path>_vec" instead of the angle. The model receives θ in degrees, in
[0°, 360°): numpyro_model records it as the deterministic site "<path>",
and fit reports both.
from virgil.angles import AngleVector
priors = {"pa": AngleVector()} # uniform, no wall at 0°/360°
priors = {"pa": AngleVector(350.0, 20.0)} # von Mises, mean 350°, κ = 20
priors = {"node": AngleVector(100.0, 4.0, axial=True)} # known modulo 180°
The radius has a ring prior, ∝ exp(−(r − 1)²/2s²), whose mode is the unit
circle, so that maximum a posteriori fits stay away from the origin, where
θ is undefined. A von Mises prior enters as the chord √κ (v̂ − m̂), the same
form as virgil's unprojected phase residuals (2 sin(Δ/2)/σ, with κ = 1/σ²),
so every term has a least-squares form and Levenberg–Marquardt takes it. The
density is normalized in the plane (with the von Mises normalizer on the
circle, through i0e), so evidences stay normalized. The default, a uniform
angle, is the invariant prior; a von Mises prior is strong information from
an external measurement.
For orbits, orientation_priors samples 2Ω and ϖ = Ω + ω for
position-only fits (so the node ambiguity is one point, not two modes), or Ω
and ϖ when RVs fix the node.
Credit. The construction follows Octofitter's UniformCircular
(Thompson et al. 2023, AJ 166, 164) and exoplanet's Angle
(Foreman-Mackey et al. 2021, JOSS 6, 3285), which sample v ~ N(0, I). The
ring and the von Mises chords are virgil's. No code is taken from either.
Angles sampled as 2-D vectors, so that no prior has a wrap boundary.
AngleVector in a priors dict, under an
angle's path, samples a vector v = r(cos θ, sin θ) at the site
"<path>_vec" instead of the angle; the model receives θ in degrees in
[0°, 360°), recorded (by numpyro_model)
and reported (by fit) under "<path>". See
angle priors without a wrap.
AngleVector
Bases: Distribution
A prior on an angle (degrees), sampled as a 2-D vector.
Put it in a priors dict, keyed by the angle's path, for
fit,
gauss_newton_mass or
numpyro_model. The vector is
sampled at "<path>_vec", giving θ = atan2(v₂, v₁) in [0°, 360°); a
fit starts from the starting angle or init["<path>_vec"]. The
prior on θ is uniform unless mean and kappa give a von Mises
prior; see angle priors.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mean
|
float
|
Mean angle (degrees) and concentration of a von Mises prior (of 2θ
when |
None
|
kappa
|
float
|
Mean angle (degrees) and concentration of a von Mises prior (of 2θ
when |
None
|
axial
|
bool
|
Whether the von Mises prior is on 2θ (an axis known modulo 180°). |
False
|
ring_width
|
float
|
The ring's radial width (default 0.25); it does not change the prior on θ. |
0.25
|
arg_constraints = {'ring_width': constraints.positive}
class-attribute
instance-attribute
pytree_data_fields = ('mean_deg', 'kappa', 'ring_width')
class-attribute
instance-attribute
pytree_aux_fields = ('axial',)
class-attribute
instance-attribute
support = constraints.real_vector
class-attribute
instance-attribute
mean_deg = None if mean is None else mean if isinstance(mean, jax.Array) else float(mean)
instance-attribute
kappa = None if kappa is None else kappa if isinstance(kappa, jax.Array) else float(kappa)
instance-attribute
ring_width = ring_width if isinstance(ring_width, jax.Array) else float(ring_width)
instance-attribute
axial = bool(axial)
instance-attribute
uniform
property
Whether the prior on the angle is uniform.
__init__(mean=None, kappa=None, *, axial=False, ring_width=0.25, validate_args=None)
residuals(vector)
Residuals whose half sum of squares is -log p(vector) + const.
The ring, (r - 1)/s, then, for a von Mises prior, the chord √κ (v̂ - m̂) (two more rows).
log_prob(value)
sample(key, sample_shape=())
vector_angle(vector)
The direction of vector (last axis of length 2), in degrees in
[0°, 360°), measured from its first component towards its second.
vector_site(path)
The numpyro site of the vector that carries the angle at path.
is_angle_vector(prior)
Whether prior is an AngleVector.