harv.samplers package¶
Sampling infrastructure for Keplerian orbits.
This module provides the rejection sampling infrastructure, prior distributions, the main RejectionSampler class, and the Samples container for posterior samples.
- class harv.samplers.AbstractSampler¶
Bases:
ModuleAbstract base for harv samplers.
Every sampler holds a prior and a pre-built model. Use the bare constructor to combine them:
sampler = RejectionSampler(prior, RVModel(extensions=...)) samples = sampler.run(data, n_prior_samples=100_000)
Concrete subclasses are marked
@finalper the project’s abstract-final pattern.- Parameters:
prior (
HarvPrior)model (
AbstractComponentModel|JointModel)
- __init__(prior, model, marginalized_names=None)¶
- Parameters:
prior (
HarvPrior)model (
AbstractComponentModel|JointModel)
- Return type:
None
- static __new__(cls, *args, **kwargs)¶
- get_extensions()¶
Return the extensions in effect for this sampler.
Walks the attached model: returns
model.extensionsfor a single component model, ordict[component_name, tuple[Extension, ...]]for aJointModelso the per-component association used for namespaced parameter names like"primary.jitter"is preserved. Use this in any downstream consumer (e.g.harv.plot.plot_rv()) that needs to know which extensions are in play.- Return type:
tuple[AbstractExtension,...] |dict[str,tuple[AbstractExtension,...]]
-
model:
AbstractComponentModel|JointModel¶
- class harv.samplers.NumpyroSampler¶
Bases:
AbstractSamplerMCMC sampler for Keplerian orbital parameters using numpyro.
Builds a numpyro model from a component model (or joint model) and runs warm-started MCMC from rejection-sampler output, returning a
Samplescontainer. Data is passed torun()rather than at construction, so the same configured sampler can be applied to multiple datasets.- Parameters:
prior (
HarvPrior) – Prior distributions for nonlinear (and optionally linear) parameters.parameterization – Orbital parameterization. For RV data defaults to
StandardRV. Ignored for Gaia astrometry data.extensions – Model extensions (jitter, trends, offsets, GP) supplied at construction time.
Examples
>>> from unxt import Q >>> from harv.models import StandardRV >>> from harv.samplers import NumpyroSampler >>> prior = StandardRV().default_prior( ... period_min=Q(2.0, "day"), ... period_max=Q(1000.0, "day"), ... sigma_K0=Q(30.0, "km/s"), ... sigma_v0=Q(50.0, "km/s"), ... ) >>> mcmc = NumpyroSampler(prior) >>> mcmc_samples = mcmc.run( ... data, init_samples=rej_samples, num_warmup=500, num_samples=1000 ... )
- Parameters:
model (
AbstractComponentModel|JointModel)
- __init__(prior, model, marginalized_names=None)¶
- Parameters:
prior (
HarvPrior)model (
AbstractComponentModel|JointModel)
- Return type:
None
- static __new__(cls, *args, **kwargs)¶
- get_extensions()¶
Return the extensions in effect for this sampler.
Walks the attached model: returns
model.extensionsfor a single component model, ordict[component_name, tuple[Extension, ...]]for aJointModelso the per-component association used for namespaced parameter names like"primary.jitter"is preserved. Use this in any downstream consumer (e.g.harv.plot.plot_rv()) that needs to know which extensions are in play.- Return type:
tuple[AbstractExtension,...] |dict[str,tuple[AbstractExtension,...]]
- optimize(samples, data, *, seed=None, max_passes=10, tol=0.0001)¶
Refine each input sample to the local posterior MAP via BFGS.
Uses
numpyro.optim.Minimize()(BFGS viajax.scipy.optimize.minimize()) with anAutoDeltaguide to find the local mode oflog_prior + marginal_log_likelihoodstarting from each sample in samples. The returnedSampleshas the refined nonlinear values, linear values set to the conditional posterior mean (equal to the conditional MAP since the conditional is Gaussian) at the refined nonlinear point, and populatedln_likelihood/ln_prior.Particularly useful when rejection sampling returns a single sample inside a posterior mode – this moves it from a random acceptance point to the mode peak.
- Parameters:
samples (
Samples) – Posterior samples (e.g. fromRejectionSampler.run()) used as warm starts. Each sample seeds an independent BFGS run.data (
AbstractData|AbstractDatasetContainer) – Observed data (same shape/type asrun()).seed (
int|None) – Random number seed. If not specified, picks a seed based on the current time.max_passes (
int) – Maximum number of BFGS restarts per sample. The wrappedjax.scipy.optimize.minimizeBFGS often quits early when its line search fails; restarting from the previous result frequently recovers further progress. Default 10.tol (
float) – Loss-change tolerance for the BFGS-restart loop. If the loss decreases by less thantolon a pass, optimization is considered converged. Default1e-4.
- Return type:
- Returns:
Refined samples with
ln_likelihoodandln_priorpopulated.
Notes
Only the marginalized path is supported (matches
run(marginalized=True)).extra_modelis not supported.Emits
UserWarningfor any sample that does not converge withinmax_passesBFGS restarts; the returned point for such a sample may not be the local MAP.
- run(data, *, init_samples=None, seed=None, marginalized=True, extra_model=None, extra_init_params=None, kernel=None, num_chains=4, num_warmup=500, num_samples=1000, chain_method='parallel', return_logprobs=False)¶
Run MCMC warm-started from rejection-sampler output.
- Parameters:
data (
AbstractData|AbstractDatasetContainer) – Observed data (RVData,GaiaAstrometryData, orSystemDatafor joint models).init_samples (
Samples|None) – Posterior samples produced by rejection sampling, used to set the initial positions for each MCMC chain.seed (
int|None) – Random number seed. If not specified, picks a seed based on the current time.marginalized (
bool) – IfTrue(default), linear parameters are analytically marginalized in the likelihood and conditionally sampled afterward. IfFalse, all parameters are sampled jointly.extra_model (
Callable[[dict[str,Any]],dict[str,Any]] |None) – A function(pars: dict) -> dictthat replaces specific linear parameters with deterministic functions of new physical parameters.extra_init_params (
dict[str,Any] |None) – Initial values for parameters introduced byextra_model.kernel (
type|None) – A numpyro MCMC kernel class. Defaults tonumpyro.infer.NUTS.num_chains (
int) – Number of independent MCMC chains. Default: 4.num_warmup (
int) – Number of warmup (burn-in) steps per chain. Default: 500.num_samples (
int) – Number of posterior samples per chain. Default: 1000.chain_method (
str) – How to run chains:"parallel","sequential", or"vectorized". Default:"parallel".return_logprobs (
bool) – IfTrue, store per-sample log-probabilities on the returnedSamples:ln_likelihood(the marginal log-likelihood, re-evaluated viamodel.log_prob) andln_prior(the summed nonlinear-prior log-density). EnablesSamples.map_sample()andSamples.ln_posterior. Requiresmarginalized=Trueand noextra_model. DefaultFalse.
- Return type:
- Returns:
Posterior samples container with nonlinear and linear parameters.
-
model:
AbstractComponentModel|JointModel¶
- harv.samplers.convert_parameterization(nonlinear, linear, *, source, target)¶
Convert parameter values between supported parameterizations.
Both
sourceandtargetmust belong to the same family (both RV, or both Gaia astrometry). Parameters not declared bysourceare preserved unchanged.- Parameters:
nonlinear (
dict[str,Quantity]) – Nonlinear parameter samples keyed by parameter name.linear (
dict[str,Quantity]) – Linear parameter samples keyed by parameter name.source (
AbstractParameterization) – Parameterization that the supplied values are currently in.target (
AbstractParameterization) – Parameterization to convert into.
- Returns:
Converted
(nonlinear, linear)parameter dictionaries.- Return type:
- Raises:
NotImplementedError – If the samples contain namespaced (joint-model) keys, or
sourceandtargetare not a supported same-family pair.ValueError – If a parameter declared by
sourceis missing from the inputs.
- harv.samplers.QD¶
alias of
QuantityDistribution
- class harv.samplers.QuantityDistribution¶
Bases:
ModulePairs a numpyro distribution with the physical unit of its samples.
For scalar distributions,
unitis a single string. For multivariate distributions (e.g.MultivariateNormalover parameters with mixed units),unitis a tuple of strings – one per dimension.- Parameters:
distribution (
Distribution) – The underlying numpyro distribution (works with bare floats).unit (
str|tuple[str,...]) – Physical unit of the samples. A single string for scalar distributions; a tuple for multivariate distributions where each element may have a different unit.
Examples
Scalar (period in days):
>>> import jax >>> import numpyro.distributions as dist >>> from harv import QuantityDistribution >>> qd = QuantityDistribution(dist.LogUniform(50., 2000.), "day") >>> sample = qd.sample(jax.random.key(0)) >>> sample.unit Unit("d")
Multivariate (astrometric linear parameters with mixed units):
qd = QuantityDistribution( dist.MultivariateNormal(loc=jnp.zeros(6), ...), ("mas", "mas", "mas/yr", "mas/yr", "mas", "mas"), ) sample = qd.sample(key) # -> raw jax.Array (consumer splits by name)
- __init__(distribution, unit)¶
- Parameters:
distribution (
Distribution)
- Return type:
None
- static __new__(cls, *args, **kwargs)¶
- log_prob(value)¶
Evaluate log-probability, stripping units if present.
- sample(key, sample_shape=())¶
Sample from the distribution, attaching units when possible.
For scalar units (
str), returnsQuantity. For tuple units (multivariate with mixed units), returns a raw array – the consumer splits by parameter name and attaches per-element units.
-
distribution:
Distribution¶
- class harv.samplers.RejectionSampler¶
Bases:
AbstractSamplerRejection sampler for Keplerian orbital parameters.
Implements rejection sampling with analytic marginalization over linear parameters. Configure once, then call
run()with each dataset.- Parameters:
prior (
HarvPrior) – Prior distributions for nonlinear (and optionally linear) parameters.model (
AbstractComponentModel|JointModel) – Fully constructed model template (no data, no linear_priors).marginalized_names (
tuple[str,...] |None) – Linear parameter names to analytically marginalize. If None, all Gaussian linear parameters are auto-classified for marginalization.batch_size (
int) – Number of samples to process per batch. Smaller values use less memory but may be slower. Default: 100_000.
Examples
>>> from unxt import Q >>> import jax.numpy as jnp >>> from harv import HarvPrior, RejectionSampler, RVData >>> from harv.models.rv import RVModel >>> data = RVData( ... time=Q(jnp.linspace(0, 100, 5), "day"), ... rv=Q(jnp.zeros(5), "km/s"), ... rv_err=Q(jnp.full(5, 1.0), "km/s"), ... ) >>> prior = HarvPrior.default_rv( ... period_min=Q(2.0, "day"), ... period_max=Q(1000.0, "day"), ... sigma_K0=Q(30.0, "km/s"), ... sigma_v0=Q(50.0, "km/s"), ... ) >>> sampler = RejectionSampler(prior, RVModel()) >>> samples = sampler.run(data, n_prior_samples=100_000)
- __init__(prior, model, marginalized_names=None, batch_size=100000)¶
- Parameters:
prior (
HarvPrior)model (
AbstractComponentModel|JointModel)batch_size (
int)
- Return type:
None
- static __new__(cls, *args, **kwargs)¶
- get_extensions()¶
Return the extensions in effect for this sampler.
Walks the attached model: returns
model.extensionsfor a single component model, ordict[component_name, tuple[Extension, ...]]for aJointModelso the per-component association used for namespaced parameter names like"primary.jitter"is preserved. Use this in any downstream consumer (e.g.harv.plot.plot_rv()) that needs to know which extensions are in play.- Return type:
tuple[AbstractExtension,...] |dict[str,tuple[AbstractExtension,...]]
- run(data, *, n_prior_samples, max_posterior_samples=None, seed=None, ignore_non_finite=False, return_logprobs=False)¶
Run rejection sampling.
- Parameters:
data (
AbstractData|AbstractDatasetContainer) – Observed data: anAbstractDatasubclass (e.g.RVData,GaiaAstrometryData) for single-component models, or anAbstractDatasetContainer(e.g.SystemData,SourceData) forJointModel.n_prior_samples (
int) – Number of samples to draw from the prior.max_posterior_samples (
int|None) – Maximum number of posterior samples to return. If None, returns all accepted samples.seed (
int|None) – Random number seed. If not specified, picks a seed based on the current time.ignore_non_finite (
bool) – IfTrue, anyNaNor infinite log-likelihood values are treated as rejected samples by replacing them with-infbefore the rejection step. IfFalse(default), non-finite values are left unchanged.return_logprobs (
bool) – IfTrue, store per-sample log-probabilities on the returnedSamples:ln_likelihood(the marginal log-likelihood) andln_prior(the summed nonlinear prior log-density). These enableSamples.map_sample()and theSamples.ln_posteriorproperty. DefaultFalse.
- Return type:
- Returns:
Posterior samples container.
-
model:
AbstractComponentModel|JointModel¶
- class harv.samplers.Samples¶
Bases:
ModuleContainer for posterior samples.
Stores both nonlinear and linear parameter samples as
Qobjects with units baked in. Provides dict-like access, statistical summaries, and visualization tools.- Parameters:
nonlinear (
dict[str,Quantity]) – Nonlinear parameter samples, one Q per parameter. Keys:"period","eccentricity","phase_peri", and optionally"arg_peri","cos_i","lon_asc_node". Units: period has time units; angles have"rad"; dimensionless parameters have unit"".linear (
dict[str,Quantity]) – Linear parameter samples, one Q per parameter. Keys: e.g."rv_semiamp","v_sys"for RV;"ra0","dec0","pmra","pmdec","parallax","semi_major_axis"for astrometry. Units are data-driven (e.g."km/s"for RV).data_type (
str) – Informational label identifying the model that produced these samples (e.g."RVModel","GaiaAstrometryModel","JointModel"). Stored in HDF5 for round-tripping.metadata (
dict[str,Any]) – Additional metadata (t_ref,num_chains, acceptance rate, etc.).linear_extension_names (
tuple[str,...]) – Names of linear parameters introduced by extensions (instrument offsets, polynomial trends, etc.) beyond the base linear set.
Examples
Samplesis normally produced byrun(), but can be constructed directly for testing or manual use:>>> from unxt import Q >>> from harv.samplers.samples import Samples >>> samples = Samples( ... nonlinear={ ... "period": Q([100.0, 101.0, 99.5], "day"), ... "eccentricity": Q([0.1, 0.15, 0.12], ""), ... "phase_peri": Q([0.3, 0.31, 0.29], ""), ... "arg_peri": Q([1.0, 1.1, 0.9], "rad"), ... }, ... linear={ ... "rv_semiamp": Q([10.0, 11.0, 9.5], "km/s"), ... "v_sys": Q([5.0, 5.1, 4.9], "km/s"), ... }, ... data_type="rv", ... metadata={"t_ref": 0.0, "t_ref_unit": "day"}, ... ) >>> samples.n_samples 3 >>> "period" in samples True >>> samples.keys()[:4] ['period', 'eccentricity', 'phase_peri', 'arg_peri']
- __init__(nonlinear, linear, data_type='', metadata=<factory>, linear_extension_names=(), ln_likelihood=None, ln_prior=None)¶
- static __new__(cls, *args, **kwargs)¶
- binary_mass_function()¶
Binary mass function from the RV orbital elements.
- chi2(data, model)¶
Per-sample goodness-of-fit \(\chi^2\) against the data.
For each posterior sample, evaluates the model prediction at the stored parameter values and returns \(\chi^2 = r^\top C^{-1} r\) (see
chi_squared()). Jitter and other extension noise terms are included viaC.- Parameters:
data (
AbstractData) – The data the samples were fit to.model (
Any) – The component model used for the fit (RVModelorGaiaAstrometryModel); provides the prediction and noise model.
- Return type:
- Returns:
Array of shape
(n_samples,).
- companion_mass(m1, *, sini=None)¶
Companion mass \(m_2\) given the primary mass.
For RV samples the mass function is
binary_mass_function();sinidefaults to 1 (the minimum companion mass). For astrometry samples the dark-companion astrometric mass function is used andsiniis ignored (the inclination is already encoded in the physical orbit size).
- convert_parameterization(*, source, target)¶
Convert stored values between supported parameterizations.
Wraps
harv.samplers.convert_parameterization(), returning a newSampleswithmetadata,data_type, andlinear_extension_namespreserved. The initial implementation supports single-component RV and Gaia astrometry parameterizations only.- Parameters:
source (
AbstractParameterization)target (
AbstractParameterization)
- Return type:
- classmethod from_hdf5(filename)¶
Load samples from HDF5 file.
- Parameters:
- Return type:
- Returns:
Loaded samples object.
Examples
>>> samples = Samples.from_hdf5("posterior_samples.h5") >>> samples.n_samples 42 >>> samples.data_type 'rv'
- property ln_posterior: Array¶
Per-sample log-posterior density,
ln_prior + ln_likelihood.Both
ln_priorandln_likelihoodmust have been stored (run the sampler withreturn_logprobs=True); otherwiseValueErroris raised.
- map_sample(*, return_index=False)¶
Return the maximum a posteriori (MAP) sample.
Selects the single sample with the highest
ln_posterior.- Parameters:
return_index (
bool) – IfTrue, also return the integer index of the MAP sample.- Return type:
- Returns:
A length-1
Sampleswith the MAP sample, or(Samples, index)whenreturn_indexisTrue.- Raises:
ValueError – If per-sample log-probabilities were not stored (run the sampler with
return_logprobs=True).
- max_phase_gap(data)¶
Largest gap in orbital-phase coverage, per sample.
The maximum gap between consecutive observations on the (circular) phase axis – the ESA Gaia “maximum phase gap” statistic.
- Parameters:
data (
AbstractData) – The data the samples were fit to.- Return type:
- Returns:
Array of shape
(n_samples,); values in[0, 1].
- median(key=None)¶
Compute median values for parameters.
- Parameters:
key (
str|None) – If provided, return median for this parameter only. If None, return dict of medians for all parameters.- Return type:
dict[str,AbstractQuantity|Array] |AbstractQuantity|Array- Returns:
Median value(s).
Examples
>>> from unxt import Q >>> from harv.samplers.samples import Samples >>> samples = Samples( ... nonlinear={"period": Q([100.0, 102.0], "day"), ... "eccentricity": Q([0.1, 0.15], ""), ... "phase_peri": Q([0.3, 0.31], ""), ... "arg_peri": Q([1.0, 1.1], "rad")}, ... linear={"rv_semiamp": Q([10.0, 12.0], "km/s"), ... "v_sys": Q([5.0, 5.2], "km/s")}, ... data_type="rv", ... metadata={"t_ref": 0.0, "t_ref_unit": "day"}, ... ) >>> med = samples.median("period") >>> med.unit Unit("d") >>> all_medians = samples.median() >>> "period" in all_medians True
- property meta: _MetadataView¶
Q-aware read-only view over
metadata.metadataitself stores quantity-valued entries in split form (<name>value +<name>_unitstring) so the static field never holds a JAX array. Usesamples.meta[name]to read those entries back asQinstances without manually reassembling the two halves; plain scalar entries (e.g.num_chains) round-trip unchanged. See_MetadataViewfor the full read API.
- minimum_companion_mass(m1)¶
Minimum companion mass (edge-on,
sin i = 1).Convenience wrapper for
companion_mass()withsini=1.
- percentile(key, percentiles=(16, 50, 84))¶
Compute percentiles for a parameter.
- Parameters:
- Return type:
- Returns:
Percentile values with appropriate units.
Examples
>>> from unxt import Q >>> from harv.samplers.samples import Samples >>> samples = Samples( ... nonlinear={"period": Q([100.0, 102.0], "day"), ... "eccentricity": Q([0.1, 0.15], ""), ... "phase_peri": Q([0.3, 0.31], ""), ... "arg_peri": Q([1.0, 1.1], "rad")}, ... linear={"rv_semiamp": Q([10.0, 12.0], "km/s"), ... "v_sys": Q([5.0, 5.2], "km/s")}, ... data_type="rv", ... metadata={"t_ref": 0.0, "t_ref_unit": "day"}, ... ) >>> p16, p50, p84 = samples.percentile("eccentricity") >>> len(samples.percentile("period", [5, 50, 95])) 3
- period_modes(data, n_clusters=2)¶
Cluster the period samples and test each mode for unimodality.
Runs K-means on
log(period)(experimental; see thejoker’sis_P_Kmodal). Requires the optionalscikit-learndependency.- Parameters:
data (
AbstractData) – The data the samples were fit to.n_clusters (
int) – Number of period modes to cluster into. Default 2.
- Return type:
- Returns:
(all_unimodal, mode_periods, n_per_mode)– whether every mode is individually unimodal, the median period of each mode (aQ), and the sample count per mode.
- period_unimodal(data)¶
Whether the period samples lie within a single mode.
Uses the criterion
ptp(P) < 4 * P_min**2 / (2*pi*T), whereTis the data time span – the period spacing at which adjacent aliases become resolvable (see thejoker’sis_P_unimodal).- Parameters:
data (
AbstractData) – The data the samples were fit to (for the observed time span).- Return type:
- periods_spanned(data)¶
Number of orbital periods spanned by the data, per sample.
- Parameters:
data (
AbstractData) – The data the samples were fit to.- Return type:
- Returns:
Array of shape
(n_samples,).
- phase_coverage(data, n_bins=10)¶
Fraction of phase bins containing at least one observation.
The ESA Gaia “phase coverage” statistic, per sample.
- Parameters:
data (
AbstractData) – The data the samples were fit to.n_bins (
int) – Number of equal-width phase bins. Default 10.
- Return type:
- Returns:
Array of shape
(n_samples,); values in[0, 1].
- phase_coverage_per_period(data)¶
Maximum number of observations within any single period, per sample.
- Parameters:
data (
AbstractData) – The data the samples were fit to.- Return type:
- Returns:
Array of shape
(n_samples,).
- plot_corner(params=None, truths=None, labels=None, **plot_kwargs)¶
Create corner plot of posterior samples using arviz.
- Parameters:
params (
list[str] |None) – Parameters to include in corner plot. If None, selects a default set based on data_type.truths (
dict[str,Any] |None) – Dictionary of true parameter values to overplot as reference values.labels (
dict[str,str] |None) – Override display names for specific parameters, e.g.{"period": "period [day]", "rv_semiamp": "K [km/s]"}. Parameters not listed use their plain parameter name as the label.**plot_kwargs (
Any) – Additional keyword arguments passed to arviz.plot_pair().
- Return type:
- Returns:
Array of axes from
arviz.plot_pair.
Examples
Default corner plot (selects parameters based on data type):
>>> axes = samples.plot_corner()
Specify parameters and overplot true values:
>>> from unxt import Q >>> axes = samples.plot_corner( ... params=["period", "eccentricity"], ... truths={"period": Q(100, "day"), "eccentricity": 0.3}, ... )
- reduced_chi2(data, model, *, dof=None)¶
Per-sample reduced \(\chi^2\) (\(\chi^2 / \mathrm{dof}\)).
- Parameters:
data (
AbstractData) – The data the samples were fit to.model (
Any) – The component model used for the fit.dof (
int|None) – Degrees of freedom. Defaults ton_obs - n_params, wheren_paramscounts every fitted parameter (orbital + linear + extension). A well-fitting model gives reduced \(\chi^2\) near 1.
- Return type:
- Returns:
Array of shape
(n_samples,).- Raises:
ValueError – If
dof(default or supplied) is not positive.
- semi_major_axis_AU()¶
Physical semi-major axis (AU) from the angular orbit size + parallax.
- summary(params=None)¶
Compute summary statistics for parameters.
For each parameter, computes: - median - mean - std (standard deviation) - percentiles (16th, 84th) for +/-1-sigma equivalent
- Parameters:
params (
list[str] |None) – List of parameter names to summarize. If None, summarizes all.- Return type:
- Returns:
Dictionary mapping parameter names to their statistics.
Examples
>>> from unxt import Q >>> from harv.samplers.samples import Samples >>> samples = Samples( ... nonlinear={"period": Q([100.0, 102.0], "day"), ... "eccentricity": Q([0.1, 0.15], ""), ... "phase_peri": Q([0.3, 0.31], ""), ... "arg_peri": Q([1.0, 1.1], "rad")}, ... linear={"rv_semiamp": Q([10.0, 12.0], "km/s"), ... "v_sys": Q([5.0, 5.2], "km/s")}, ... data_type="rv", ... metadata={"t_ref": 0.0, "t_ref_unit": "day"}, ... ) >>> summary = samples.summary(["period", "eccentricity"]) >>> sorted(summary.keys()) ['eccentricity', 'period'] >>> sorted(summary["period"].keys()) ['mean', 'median', 'p16', 'p84', 'std']
- thiele_innes_to_campbell()¶
Convert Thiele-Innes linear parameters to Campbell orbital elements.
See
campbell_from_thiele_innes()for the mathematical details of the conversion.
- to_arviz(params=None, labels=None)¶
Export samples to an
arviz.InferenceDataobject.- Parameters:
params (
list[str] |None) – Parameters to include. IfNone, all parameters returned bykeys()are included.labels (
dict[str,str] |None) – Override display names for specific parameters, e.g.{"period": "period [day]", "rv_semiamp": "K [km/s]"}. Parameters not listed use their plain parameter name as the label.
- Return type:
- Returns:
Inference data suitable for
arviz.plot_pair,arviz.summary, etc.- Raises:
ImportError – If
arvizis not installed.
Examples
>>> idata = samples.to_arviz(["period", "eccentricity"])
- to_hdf5(filename)¶
Save samples to HDF5 file.
Examples
Save posterior samples and reload them:
>>> samples.to_hdf5("posterior_samples.h5") >>> reloaded = Samples.from_hdf5("posterior_samples.h5") >>> reloaded.n_samples == samples.n_samples True
- wrap_angles()¶
Wrap negative
rv_semiamp/semi_major_axisto positive.Negative
rv_semiamp(K) orsemi_major_axis(a) describes an orbit that is physically identical to the all-positive case under two symmetries of the model:arg_peri -> arg_peri + piflips the sign of bothKanda(the(omega, K, a) -> (omega + pi, -K, -a)symmetry);lon_asc_node -> lon_asc_node + piflips the sign ofaalone (the astrometric(Omega, a) -> (Omega + pi, -a)symmetry;lon_asc_nodedoes not enter the RV model).
This method returns a new
SamplesenforcingK >= 0anda >= 0in two steps:shift
arg_peribypi(flipping everyrv_semiampandsemi_major_axis) on the samples where the trigger amplitude is negative — enforcingK >= 0;shift
lon_asc_nodebypi(flipping everysemi_major_axis) on the samples whereais still negative — enforcinga >= 0.
Both shifted angles are wrapped to
[0, 2*pi). A singlearg_perishift cannot make bothKandapositive when their signs disagree (which routinely happens in joint RV + astrometry posteriors), so the secondlon_asc_nodeshift is required.Joint models (e.g. SB2) namespace per-component linear parameters as
"component.param_name"; this method discovers everyrv_semiamp- andsemi_major_axis-suffixed key. The firstrv_semiamp-suffixed key (insertion order) triggers step 1;semi_major_axistriggers step 1 only when norv_semiampis present (astrometry-only fits).No-op when
arg_periis absent fromnonlinear, when neitherrv_semiampnorsemi_major_axisis inlinear, or when no entries are negative.- Raises:
NotImplementedError – If the model has multiple per-component
arg_periorlon_asc_nodekeys (e.g."primary.arg_peri"and"secondary.arg_peri"). All current harv joint factories share both, so this is not expected to arise in practice.- Return type:
Examples
>>> from unxt import Q >>> from harv.samplers.samples import Samples >>> samples = Samples( ... nonlinear={"period": Q([100.0, 100.0], "day"), ... "eccentricity": Q([0.1, 0.1], ""), ... "phase_peri": Q([0.3, 0.3], ""), ... "arg_peri": Q([1.0, 1.0], "rad")}, ... linear={"rv_semiamp": Q([-10.0, 10.0], "km/s"), ... "v_sys": Q([0.0, 0.0], "km/s")}, ... data_type="rv", ... metadata={"t_ref": 0.0, "t_ref_unit": "day"}, ... ) >>> wrapped = samples.wrap_angles() >>> bool((wrapped["rv_semiamp"].value >= 0).all()) True
- Return type:
Submodules¶
harv.samplers.base module¶
Abstract base for harv samplers.
Carries the shared field set (prior, model, marginalized_names).
Algorithm-specific surface (run() and its private helpers) lives on each
concrete subclass, since the rejection and MCMC algorithms have fundamentally
different run() signatures and internal state.
- class harv.samplers.base.AbstractSampler¶
Bases:
ModuleAbstract base for harv samplers.
Every sampler holds a prior and a pre-built model. Use the bare constructor to combine them:
sampler = RejectionSampler(prior, RVModel(extensions=...)) samples = sampler.run(data, n_prior_samples=100_000)
Concrete subclasses are marked
@finalper the project’s abstract-final pattern.- Parameters:
prior (
HarvPrior)model (
AbstractComponentModel|JointModel)
-
model:
AbstractComponentModel|JointModel¶
- get_extensions()¶
Return the extensions in effect for this sampler.
Walks the attached model: returns
model.extensionsfor a single component model, ordict[component_name, tuple[Extension, ...]]for aJointModelso the per-component association used for namespaced parameter names like"primary.jitter"is preserved. Use this in any downstream consumer (e.g.harv.plot.plot_rv()) that needs to know which extensions are in play.- Return type:
tuple[AbstractExtension,...] |dict[str,tuple[AbstractExtension,...]]
- __init__(prior, model, marginalized_names=None)¶
- Parameters:
prior (
HarvPrior)model (
AbstractComponentModel|JointModel)
- Return type:
None
harv.samplers.conversion module¶
Convert sampled parameter values between parameterizations.
This first implementation supports single-component RV and Gaia astrometry parameterizations only:
RV:
StandardRV <-> EcoswEsinwRVGaia astrometry:
StandardGaiaAstrometry <-> ThieleInnesGaiaAstrometry
Parameters not declared by the source parameterization (for example, extension
parameters such as jitter or polynomial-trend coefficients) are preserved
unchanged. See docs/spec.md (“Parameterization conversion”).
- harv.samplers.conversion.convert_parameterization(nonlinear, linear, *, source, target)¶
Convert parameter values between supported parameterizations.
Both
sourceandtargetmust belong to the same family (both RV, or both Gaia astrometry). Parameters not declared bysourceare preserved unchanged.- Parameters:
nonlinear (
dict[str,Quantity]) – Nonlinear parameter samples keyed by parameter name.linear (
dict[str,Quantity]) – Linear parameter samples keyed by parameter name.source (
AbstractParameterization) – Parameterization that the supplied values are currently in.target (
AbstractParameterization) – Parameterization to convert into.
- Returns:
Converted
(nonlinear, linear)parameter dictionaries.- Return type:
- Raises:
NotImplementedError – If the samples contain namespaced (joint-model) keys, or
sourceandtargetare not a supported same-family pair.ValueError – If a parameter declared by
sourceis missing from the inputs.
harv.samplers.numpyro module¶
Numpyro MCMC sampler for harv.
Provides the NumpyroSampler class for warm-started MCMC using the
new component model API. The component models’ numpyro_model() method
builds the numpyro model closure directly.
- final class harv.samplers.numpyro.NumpyroSampler¶
Bases:
AbstractSamplerMCMC sampler for Keplerian orbital parameters using numpyro.
Builds a numpyro model from a component model (or joint model) and runs warm-started MCMC from rejection-sampler output, returning a
Samplescontainer. Data is passed torun()rather than at construction, so the same configured sampler can be applied to multiple datasets.- Parameters:
prior (
HarvPrior) – Prior distributions for nonlinear (and optionally linear) parameters.parameterization – Orbital parameterization. For RV data defaults to
StandardRV. Ignored for Gaia astrometry data.extensions – Model extensions (jitter, trends, offsets, GP) supplied at construction time.
Examples
>>> from unxt import Q >>> from harv.models import StandardRV >>> from harv.samplers import NumpyroSampler >>> prior = StandardRV().default_prior( ... period_min=Q(2.0, "day"), ... period_max=Q(1000.0, "day"), ... sigma_K0=Q(30.0, "km/s"), ... sigma_v0=Q(50.0, "km/s"), ... ) >>> mcmc = NumpyroSampler(prior) >>> mcmc_samples = mcmc.run( ... data, init_samples=rej_samples, num_warmup=500, num_samples=1000 ... )
- Parameters:
model (
AbstractComponentModel|JointModel)
- run(data, *, init_samples=None, seed=None, marginalized=True, extra_model=None, extra_init_params=None, kernel=None, num_chains=4, num_warmup=500, num_samples=1000, chain_method='parallel', return_logprobs=False)¶
Run MCMC warm-started from rejection-sampler output.
- Parameters:
data (
AbstractData|AbstractDatasetContainer) – Observed data (RVData,GaiaAstrometryData, orSystemDatafor joint models).init_samples (
Samples|None) – Posterior samples produced by rejection sampling, used to set the initial positions for each MCMC chain.seed (
int|None) – Random number seed. If not specified, picks a seed based on the current time.marginalized (
bool) – IfTrue(default), linear parameters are analytically marginalized in the likelihood and conditionally sampled afterward. IfFalse, all parameters are sampled jointly.extra_model (
Callable[[dict[str,Any]],dict[str,Any]] |None) – A function(pars: dict) -> dictthat replaces specific linear parameters with deterministic functions of new physical parameters.extra_init_params (
dict[str,Any] |None) – Initial values for parameters introduced byextra_model.kernel (
type|None) – A numpyro MCMC kernel class. Defaults tonumpyro.infer.NUTS.num_chains (
int) – Number of independent MCMC chains. Default: 4.num_warmup (
int) – Number of warmup (burn-in) steps per chain. Default: 500.num_samples (
int) – Number of posterior samples per chain. Default: 1000.chain_method (
str) – How to run chains:"parallel","sequential", or"vectorized". Default:"parallel".return_logprobs (
bool) – IfTrue, store per-sample log-probabilities on the returnedSamples:ln_likelihood(the marginal log-likelihood, re-evaluated viamodel.log_prob) andln_prior(the summed nonlinear-prior log-density). EnablesSamples.map_sample()andSamples.ln_posterior. Requiresmarginalized=Trueand noextra_model. DefaultFalse.
- Return type:
- Returns:
Posterior samples container with nonlinear and linear parameters.
- optimize(samples, data, *, seed=None, max_passes=10, tol=0.0001)¶
Refine each input sample to the local posterior MAP via BFGS.
Uses
numpyro.optim.Minimize()(BFGS viajax.scipy.optimize.minimize()) with anAutoDeltaguide to find the local mode oflog_prior + marginal_log_likelihoodstarting from each sample in samples. The returnedSampleshas the refined nonlinear values, linear values set to the conditional posterior mean (equal to the conditional MAP since the conditional is Gaussian) at the refined nonlinear point, and populatedln_likelihood/ln_prior.Particularly useful when rejection sampling returns a single sample inside a posterior mode – this moves it from a random acceptance point to the mode peak.
- Parameters:
samples (
Samples) – Posterior samples (e.g. fromRejectionSampler.run()) used as warm starts. Each sample seeds an independent BFGS run.data (
AbstractData|AbstractDatasetContainer) – Observed data (same shape/type asrun()).seed (
int|None) – Random number seed. If not specified, picks a seed based on the current time.max_passes (
int) – Maximum number of BFGS restarts per sample. The wrappedjax.scipy.optimize.minimizeBFGS often quits early when its line search fails; restarting from the previous result frequently recovers further progress. Default 10.tol (
float) – Loss-change tolerance for the BFGS-restart loop. If the loss decreases by less thantolon a pass, optimization is considered converged. Default1e-4.
- Return type:
- Returns:
Refined samples with
ln_likelihoodandln_priorpopulated.
Notes
Only the marginalized path is supported (matches
run(marginalized=True)).extra_modelis not supported.Emits
UserWarningfor any sample that does not converge withinmax_passesBFGS restarts; the returned point for such a sample may not be the local MAP.
- __init__(prior, model, marginalized_names=None)¶
- Parameters:
prior (
HarvPrior)model (
AbstractComponentModel|JointModel)
- Return type:
None
- static __new__(cls, *args, **kwargs)¶
- get_extensions()¶
Return the extensions in effect for this sampler.
Walks the attached model: returns
model.extensionsfor a single component model, ordict[component_name, tuple[Extension, ...]]for aJointModelso the per-component association used for namespaced parameter names like"primary.jitter"is preserved. Use this in any downstream consumer (e.g.harv.plot.plot_rv()) that needs to know which extensions are in play.- Return type:
tuple[AbstractExtension,...] |dict[str,tuple[AbstractExtension,...]]
-
model:
AbstractComponentModel|JointModel¶
harv.samplers.rejection module¶
Rejection sampler for orbital parameter inference.
- final class harv.samplers.rejection.RejectionSampler¶
Bases:
AbstractSamplerRejection sampler for Keplerian orbital parameters.
Implements rejection sampling with analytic marginalization over linear parameters. Configure once, then call
run()with each dataset.- Parameters:
prior (
HarvPrior) – Prior distributions for nonlinear (and optionally linear) parameters.model (
AbstractComponentModel|JointModel) – Fully constructed model template (no data, no linear_priors).marginalized_names (
tuple[str,...] |None) – Linear parameter names to analytically marginalize. If None, all Gaussian linear parameters are auto-classified for marginalization.batch_size (
int) – Number of samples to process per batch. Smaller values use less memory but may be slower. Default: 100_000.
Examples
>>> from unxt import Q >>> import jax.numpy as jnp >>> from harv import HarvPrior, RejectionSampler, RVData >>> from harv.models.rv import RVModel >>> data = RVData( ... time=Q(jnp.linspace(0, 100, 5), "day"), ... rv=Q(jnp.zeros(5), "km/s"), ... rv_err=Q(jnp.full(5, 1.0), "km/s"), ... ) >>> prior = HarvPrior.default_rv( ... period_min=Q(2.0, "day"), ... period_max=Q(1000.0, "day"), ... sigma_K0=Q(30.0, "km/s"), ... sigma_v0=Q(50.0, "km/s"), ... ) >>> sampler = RejectionSampler(prior, RVModel()) >>> samples = sampler.run(data, n_prior_samples=100_000)
- run(data, *, n_prior_samples, max_posterior_samples=None, seed=None, ignore_non_finite=False, return_logprobs=False)¶
Run rejection sampling.
- Parameters:
data (
AbstractData|AbstractDatasetContainer) – Observed data: anAbstractDatasubclass (e.g.RVData,GaiaAstrometryData) for single-component models, or anAbstractDatasetContainer(e.g.SystemData,SourceData) forJointModel.n_prior_samples (
int) – Number of samples to draw from the prior.max_posterior_samples (
int|None) – Maximum number of posterior samples to return. If None, returns all accepted samples.seed (
int|None) – Random number seed. If not specified, picks a seed based on the current time.ignore_non_finite (
bool) – IfTrue, anyNaNor infinite log-likelihood values are treated as rejected samples by replacing them with-infbefore the rejection step. IfFalse(default), non-finite values are left unchanged.return_logprobs (
bool) – IfTrue, store per-sample log-probabilities on the returnedSamples:ln_likelihood(the marginal log-likelihood) andln_prior(the summed nonlinear prior log-density). These enableSamples.map_sample()and theSamples.ln_posteriorproperty. DefaultFalse.
- Return type:
- Returns:
Posterior samples container.
- __init__(prior, model, marginalized_names=None, batch_size=100000)¶
- Parameters:
prior (
HarvPrior)model (
AbstractComponentModel|JointModel)batch_size (
int)
- Return type:
None
- static __new__(cls, *args, **kwargs)¶
- get_extensions()¶
Return the extensions in effect for this sampler.
Walks the attached model: returns
model.extensionsfor a single component model, ordict[component_name, tuple[Extension, ...]]for aJointModelso the per-component association used for namespaced parameter names like"primary.jitter"is preserved. Use this in any downstream consumer (e.g.harv.plot.plot_rv()) that needs to know which extensions are in play.- Return type:
tuple[AbstractExtension,...] |dict[str,tuple[AbstractExtension,...]]
-
model:
AbstractComponentModel|JointModel¶
harv.samplers.samples module¶
Container for rejection sampler posterior samples.
This module provides the Samples class which stores posterior samples from rejection sampling with dict-like access, unit handling, and analysis tools.
- class harv.samplers.samples.Samples¶
Bases:
ModuleContainer for posterior samples.
Stores both nonlinear and linear parameter samples as
Qobjects with units baked in. Provides dict-like access, statistical summaries, and visualization tools.- Parameters:
nonlinear (
dict[str,Quantity]) – Nonlinear parameter samples, one Q per parameter. Keys:"period","eccentricity","phase_peri", and optionally"arg_peri","cos_i","lon_asc_node". Units: period has time units; angles have"rad"; dimensionless parameters have unit"".linear (
dict[str,Quantity]) – Linear parameter samples, one Q per parameter. Keys: e.g."rv_semiamp","v_sys"for RV;"ra0","dec0","pmra","pmdec","parallax","semi_major_axis"for astrometry. Units are data-driven (e.g."km/s"for RV).data_type (
str) – Informational label identifying the model that produced these samples (e.g."RVModel","GaiaAstrometryModel","JointModel"). Stored in HDF5 for round-tripping.metadata (
dict[str,Any]) – Additional metadata (t_ref,num_chains, acceptance rate, etc.).linear_extension_names (
tuple[str,...]) – Names of linear parameters introduced by extensions (instrument offsets, polynomial trends, etc.) beyond the base linear set.
Examples
Samplesis normally produced byrun(), but can be constructed directly for testing or manual use:>>> from unxt import Q >>> from harv.samplers.samples import Samples >>> samples = Samples( ... nonlinear={ ... "period": Q([100.0, 101.0, 99.5], "day"), ... "eccentricity": Q([0.1, 0.15, 0.12], ""), ... "phase_peri": Q([0.3, 0.31, 0.29], ""), ... "arg_peri": Q([1.0, 1.1, 0.9], "rad"), ... }, ... linear={ ... "rv_semiamp": Q([10.0, 11.0, 9.5], "km/s"), ... "v_sys": Q([5.0, 5.1, 4.9], "km/s"), ... }, ... data_type="rv", ... metadata={"t_ref": 0.0, "t_ref_unit": "day"}, ... ) >>> samples.n_samples 3 >>> "period" in samples True >>> samples.keys()[:4] ['period', 'eccentricity', 'phase_peri', 'arg_peri']
- property meta: _MetadataView¶
Q-aware read-only view over
metadata.metadataitself stores quantity-valued entries in split form (<name>value +<name>_unitstring) so the static field never holds a JAX array. Usesamples.meta[name]to read those entries back asQinstances without manually reassembling the two halves; plain scalar entries (e.g.num_chains) round-trip unchanged. See_MetadataViewfor the full read API.
- property ln_posterior: Array¶
Per-sample log-posterior density,
ln_prior + ln_likelihood.Both
ln_priorandln_likelihoodmust have been stored (run the sampler withreturn_logprobs=True); otherwiseValueErroris raised.
- wrap_angles()¶
Wrap negative
rv_semiamp/semi_major_axisto positive.Negative
rv_semiamp(K) orsemi_major_axis(a) describes an orbit that is physically identical to the all-positive case under two symmetries of the model:arg_peri -> arg_peri + piflips the sign of bothKanda(the(omega, K, a) -> (omega + pi, -K, -a)symmetry);lon_asc_node -> lon_asc_node + piflips the sign ofaalone (the astrometric(Omega, a) -> (Omega + pi, -a)symmetry;lon_asc_nodedoes not enter the RV model).
This method returns a new
SamplesenforcingK >= 0anda >= 0in two steps:shift
arg_peribypi(flipping everyrv_semiampandsemi_major_axis) on the samples where the trigger amplitude is negative — enforcingK >= 0;shift
lon_asc_nodebypi(flipping everysemi_major_axis) on the samples whereais still negative — enforcinga >= 0.
Both shifted angles are wrapped to
[0, 2*pi). A singlearg_perishift cannot make bothKandapositive when their signs disagree (which routinely happens in joint RV + astrometry posteriors), so the secondlon_asc_nodeshift is required.Joint models (e.g. SB2) namespace per-component linear parameters as
"component.param_name"; this method discovers everyrv_semiamp- andsemi_major_axis-suffixed key. The firstrv_semiamp-suffixed key (insertion order) triggers step 1;semi_major_axistriggers step 1 only when norv_semiampis present (astrometry-only fits).No-op when
arg_periis absent fromnonlinear, when neitherrv_semiampnorsemi_major_axisis inlinear, or when no entries are negative.- Raises:
NotImplementedError – If the model has multiple per-component
arg_periorlon_asc_nodekeys (e.g."primary.arg_peri"and"secondary.arg_peri"). All current harv joint factories share both, so this is not expected to arise in practice.- Return type:
Examples
>>> from unxt import Q >>> from harv.samplers.samples import Samples >>> samples = Samples( ... nonlinear={"period": Q([100.0, 100.0], "day"), ... "eccentricity": Q([0.1, 0.1], ""), ... "phase_peri": Q([0.3, 0.3], ""), ... "arg_peri": Q([1.0, 1.0], "rad")}, ... linear={"rv_semiamp": Q([-10.0, 10.0], "km/s"), ... "v_sys": Q([0.0, 0.0], "km/s")}, ... data_type="rv", ... metadata={"t_ref": 0.0, "t_ref_unit": "day"}, ... ) >>> wrapped = samples.wrap_angles() >>> bool((wrapped["rv_semiamp"].value >= 0).all()) True
- Return type:
- thiele_innes_to_campbell()¶
Convert Thiele-Innes linear parameters to Campbell orbital elements.
See
campbell_from_thiele_innes()for the mathematical details of the conversion.
- convert_parameterization(*, source, target)¶
Convert stored values between supported parameterizations.
Wraps
harv.samplers.convert_parameterization(), returning a newSampleswithmetadata,data_type, andlinear_extension_namespreserved. The initial implementation supports single-component RV and Gaia astrometry parameterizations only.- Parameters:
source (
AbstractParameterization)target (
AbstractParameterization)
- Return type:
- median(key=None)¶
Compute median values for parameters.
- Parameters:
key (
str|None) – If provided, return median for this parameter only. If None, return dict of medians for all parameters.- Return type:
dict[str,AbstractQuantity|Array] |AbstractQuantity|Array- Returns:
Median value(s).
Examples
>>> from unxt import Q >>> from harv.samplers.samples import Samples >>> samples = Samples( ... nonlinear={"period": Q([100.0, 102.0], "day"), ... "eccentricity": Q([0.1, 0.15], ""), ... "phase_peri": Q([0.3, 0.31], ""), ... "arg_peri": Q([1.0, 1.1], "rad")}, ... linear={"rv_semiamp": Q([10.0, 12.0], "km/s"), ... "v_sys": Q([5.0, 5.2], "km/s")}, ... data_type="rv", ... metadata={"t_ref": 0.0, "t_ref_unit": "day"}, ... ) >>> med = samples.median("period") >>> med.unit Unit("d") >>> all_medians = samples.median() >>> "period" in all_medians True
- percentile(key, percentiles=(16, 50, 84))¶
Compute percentiles for a parameter.
- Parameters:
- Return type:
- Returns:
Percentile values with appropriate units.
Examples
>>> from unxt import Q >>> from harv.samplers.samples import Samples >>> samples = Samples( ... nonlinear={"period": Q([100.0, 102.0], "day"), ... "eccentricity": Q([0.1, 0.15], ""), ... "phase_peri": Q([0.3, 0.31], ""), ... "arg_peri": Q([1.0, 1.1], "rad")}, ... linear={"rv_semiamp": Q([10.0, 12.0], "km/s"), ... "v_sys": Q([5.0, 5.2], "km/s")}, ... data_type="rv", ... metadata={"t_ref": 0.0, "t_ref_unit": "day"}, ... ) >>> p16, p50, p84 = samples.percentile("eccentricity") >>> len(samples.percentile("period", [5, 50, 95])) 3
- summary(params=None)¶
Compute summary statistics for parameters.
For each parameter, computes: - median - mean - std (standard deviation) - percentiles (16th, 84th) for +/-1-sigma equivalent
- Parameters:
params (
list[str] |None) – List of parameter names to summarize. If None, summarizes all.- Return type:
- Returns:
Dictionary mapping parameter names to their statistics.
Examples
>>> from unxt import Q >>> from harv.samplers.samples import Samples >>> samples = Samples( ... nonlinear={"period": Q([100.0, 102.0], "day"), ... "eccentricity": Q([0.1, 0.15], ""), ... "phase_peri": Q([0.3, 0.31], ""), ... "arg_peri": Q([1.0, 1.1], "rad")}, ... linear={"rv_semiamp": Q([10.0, 12.0], "km/s"), ... "v_sys": Q([5.0, 5.2], "km/s")}, ... data_type="rv", ... metadata={"t_ref": 0.0, "t_ref_unit": "day"}, ... ) >>> summary = samples.summary(["period", "eccentricity"]) >>> sorted(summary.keys()) ['eccentricity', 'period'] >>> sorted(summary["period"].keys()) ['mean', 'median', 'p16', 'p84', 'std']
- map_sample(*, return_index=False)¶
Return the maximum a posteriori (MAP) sample.
Selects the single sample with the highest
ln_posterior.- Parameters:
return_index (
bool) – IfTrue, also return the integer index of the MAP sample.- Return type:
- Returns:
A length-1
Sampleswith the MAP sample, or(Samples, index)whenreturn_indexisTrue.- Raises:
ValueError – If per-sample log-probabilities were not stored (run the sampler with
return_logprobs=True).
- period_unimodal(data)¶
Whether the period samples lie within a single mode.
Uses the criterion
ptp(P) < 4 * P_min**2 / (2*pi*T), whereTis the data time span – the period spacing at which adjacent aliases become resolvable (see thejoker’sis_P_unimodal).- Parameters:
data (
AbstractData) – The data the samples were fit to (for the observed time span).- Return type:
- period_modes(data, n_clusters=2)¶
Cluster the period samples and test each mode for unimodality.
Runs K-means on
log(period)(experimental; see thejoker’sis_P_Kmodal). Requires the optionalscikit-learndependency.- Parameters:
data (
AbstractData) – The data the samples were fit to.n_clusters (
int) – Number of period modes to cluster into. Default 2.
- Return type:
- Returns:
(all_unimodal, mode_periods, n_per_mode)– whether every mode is individually unimodal, the median period of each mode (aQ), and the sample count per mode.
- max_phase_gap(data)¶
Largest gap in orbital-phase coverage, per sample.
The maximum gap between consecutive observations on the (circular) phase axis – the ESA Gaia “maximum phase gap” statistic.
- Parameters:
data (
AbstractData) – The data the samples were fit to.- Return type:
- Returns:
Array of shape
(n_samples,); values in[0, 1].
- phase_coverage(data, n_bins=10)¶
Fraction of phase bins containing at least one observation.
The ESA Gaia “phase coverage” statistic, per sample.
- Parameters:
data (
AbstractData) – The data the samples were fit to.n_bins (
int) – Number of equal-width phase bins. Default 10.
- Return type:
- Returns:
Array of shape
(n_samples,); values in[0, 1].
- periods_spanned(data)¶
Number of orbital periods spanned by the data, per sample.
- Parameters:
data (
AbstractData) – The data the samples were fit to.- Return type:
- Returns:
Array of shape
(n_samples,).
- phase_coverage_per_period(data)¶
Maximum number of observations within any single period, per sample.
- Parameters:
data (
AbstractData) – The data the samples were fit to.- Return type:
- Returns:
Array of shape
(n_samples,).
- chi2(data, model)¶
Per-sample goodness-of-fit \(\chi^2\) against the data.
For each posterior sample, evaluates the model prediction at the stored parameter values and returns \(\chi^2 = r^\top C^{-1} r\) (see
chi_squared()). Jitter and other extension noise terms are included viaC.- Parameters:
data (
AbstractData) – The data the samples were fit to.model (
Any) – The component model used for the fit (RVModelorGaiaAstrometryModel); provides the prediction and noise model.
- Return type:
- Returns:
Array of shape
(n_samples,).
- reduced_chi2(data, model, *, dof=None)¶
Per-sample reduced \(\chi^2\) (\(\chi^2 / \mathrm{dof}\)).
- Parameters:
data (
AbstractData) – The data the samples were fit to.model (
Any) – The component model used for the fit.dof (
int|None) – Degrees of freedom. Defaults ton_obs - n_params, wheren_paramscounts every fitted parameter (orbital + linear + extension). A well-fitting model gives reduced \(\chi^2\) near 1.
- Return type:
- Returns:
Array of shape
(n_samples,).- Raises:
ValueError – If
dof(default or supplied) is not positive.
- binary_mass_function()¶
Binary mass function from the RV orbital elements.
- semi_major_axis_AU()¶
Physical semi-major axis (AU) from the angular orbit size + parallax.
- companion_mass(m1, *, sini=None)¶
Companion mass \(m_2\) given the primary mass.
For RV samples the mass function is
binary_mass_function();sinidefaults to 1 (the minimum companion mass). For astrometry samples the dark-companion astrometric mass function is used andsiniis ignored (the inclination is already encoded in the physical orbit size).
- minimum_companion_mass(m1)¶
Minimum companion mass (edge-on,
sin i = 1).Convenience wrapper for
companion_mass()withsini=1.
- to_hdf5(filename)¶
Save samples to HDF5 file.
Examples
Save posterior samples and reload them:
>>> samples.to_hdf5("posterior_samples.h5") >>> reloaded = Samples.from_hdf5("posterior_samples.h5") >>> reloaded.n_samples == samples.n_samples True
- classmethod from_hdf5(filename)¶
Load samples from HDF5 file.
- Parameters:
- Return type:
- Returns:
Loaded samples object.
Examples
>>> samples = Samples.from_hdf5("posterior_samples.h5") >>> samples.n_samples 42 >>> samples.data_type 'rv'
- __init__(nonlinear, linear, data_type='', metadata=<factory>, linear_extension_names=(), ln_likelihood=None, ln_prior=None)¶
- static __new__(cls, *args, **kwargs)¶
- to_arviz(params=None, labels=None)¶
Export samples to an
arviz.InferenceDataobject.- Parameters:
params (
list[str] |None) – Parameters to include. IfNone, all parameters returned bykeys()are included.labels (
dict[str,str] |None) – Override display names for specific parameters, e.g.{"period": "period [day]", "rv_semiamp": "K [km/s]"}. Parameters not listed use their plain parameter name as the label.
- Return type:
- Returns:
Inference data suitable for
arviz.plot_pair,arviz.summary, etc.- Raises:
ImportError – If
arvizis not installed.
Examples
>>> idata = samples.to_arviz(["period", "eccentricity"])
- plot_corner(params=None, truths=None, labels=None, **plot_kwargs)¶
Create corner plot of posterior samples using arviz.
- Parameters:
params (
list[str] |None) – Parameters to include in corner plot. If None, selects a default set based on data_type.truths (
dict[str,Any] |None) – Dictionary of true parameter values to overplot as reference values.labels (
dict[str,str] |None) – Override display names for specific parameters, e.g.{"period": "period [day]", "rv_semiamp": "K [km/s]"}. Parameters not listed use their plain parameter name as the label.**plot_kwargs (
Any) – Additional keyword arguments passed to arviz.plot_pair().
- Return type:
- Returns:
Array of axes from
arviz.plot_pair.
Examples
Default corner plot (selects parameters based on data type):
>>> axes = samples.plot_corner()
Specify parameters and overplot true values:
>>> from unxt import Q >>> axes = samples.plot_corner( ... params=["period", "eccentricity"], ... truths={"period": Q(100, "day"), "eccentricity": 0.3}, ... )