Priors

Every parameter in unite carries a prior distribution that controls the range and shape of the posterior. unite provides three built-in prior types plus a mechanism for dependent priors that reference other parameters.


Supported Priors

Uniform

A flat prior between low and high:

from unite import prior

p = prior.Uniform(low=0.0, high=1000.0)
  • Most common choice for line fluxes, FWHM bounds, and other parameters with no strong prior expectation.

  • Bounds can be floats or parameter expressions (see Dependent Priors below).

TruncatedNormal

A Gaussian centered at loc with standard deviation scale, truncated to [low, high]:

p = prior.TruncatedNormal(loc=1.0, scale=0.05, low=0.8, high=1.2)
  • Good for calibration parameters where you have a prior expectation (e.g., RScale near 1.0).

  • loc, low, and high can all be parameter expressions.

Fixed

A constant value — not sampled:

p = prior.Fixed(6564.61)
  • The parameter is held constant at the given value.

  • to_dist() returns None (no NumPyro distribution is created).

  • Useful for:

    • Fixing line ratios to theoretical values

    • Fixing calibration parameters to known corrections

    • Fixing redshift when known precisely


Dependent Priors

One of unite’s most powerful features. Prior bounds can reference other parameters using arithmetic expressions on token objects. This creates dependency chains that are automatically resolved via topological sorting at model-build time.

Basic Example

from unite import line, prior

fwhm_narrow = line.FWHM('narrow', prior=prior.Uniform(50, 500))

# Broad component must be at least 150 km/s wider
fwhm_broad = line.FWHM('broad', prior=prior.Uniform(fwhm_narrow + 150, 5000))

The expression fwhm_narrow + 150 creates a parameter expression. At runtime, fwhm_narrow is sampled first, then the lower bound of fwhm_broad’s prior is evaluated as sampled_value + 150.

Note

Only a Parameter of the same kind can be used in an expression (e.g., FWHM in FWHM). You cannot, for example, use a line flux token in an expression for a redshift prior.

Supported Arithmetic

Parameters support +, -, *, and / against constants or other parameter tokens, building an expression tree that is evaluated at sample time. Expressions can be arbitrarily nested and combined.

Single-token expressions:

fwhm1 = line.FWHM()
fwhm2 = line.FWHM(prior=prior.Uniform(fwhm_narrow * 2 + 150, 5000))
fwhm3 = line.FWHM(prior=prior.Uniform(0, fwhm_narrow / 2 - 150))

Two-token expressions — including ratios across parameters:

# Flux ratio constraint: tie [OIII] 4363 ratio across kinematic components.
# Same electron temperature in both narrow and broad components.
flux_5007_narrow = line.Flux('5007_narrow', prior=prior.Uniform(0, 10))
flux_5007_broad  = line.Flux('5007_broad',  prior=prior.Uniform(0, 10))
flux_4363_narrow = line.Flux('4363_narrow', prior=prior.Uniform(0, 10))

# flux_4363_broad is fully determined — not a free parameter
flux_4363_broad = line.Flux(
    '4363_broad',
    prior=prior.Fixed(flux_4363_narrow * flux_5007_broad / flux_5007_narrow),
)

This also works for soft constraints using Uniform or TruncatedNormal bounds:

# Allow some scatter around the expected ratio
flux_4363_broad = line.Flux(
    '4363_broad',
    prior=prior.TruncatedNormal(
        loc=flux_4363_narrow * flux_5007_broad / flux_5007_narrow,
        scale=0.05,
        low=0,
        high=10,
    ),
)

Deep Dependency Chains

Dependencies can be arbitrarily deep:

fwhm_narrow    = line.FWHM('n',  prior=prior.Uniform(50, 500))
fwhm_broad     = line.FWHM('b',  prior=prior.Uniform(fwhm_narrow + 150, 3000))
fwhm_very_broad = line.FWHM('vb', prior=prior.Uniform(fwhm_broad + 200, 5000))

Topological sorting ensures the sampling order: fwhm_nfwhm_bfwhm_vb.

Using with TruncatedNormal

Both bounds and loc can be parameter expressions:

fwhm_narrow = line.FWHM('n', prior=prior.Uniform(50, 500))

# Broad component centerd at 2× narrow, with scatter
fwhm_broad = line.FWHM('b', prior=prior.TruncatedNormal(
    loc=fwhm_narrow * 2,
    scale=50,
    low=fwhm_narrow + 100,
    high=5000,
))

Topological Sorting

When parameters have dependent priors, ModelBuilder uses topological_sort() (Kahn’s algorithm) to determine the sampling order. Parameters are always sampled before any parameter whose prior depends on them.

from unite import model

builder = model.ModelBuilder(filtered_lines, filtered_cont, spectra)
model_fn, model_args = builder.build()
print(model_args.dependency_order)
# e.g. ['n', 'b', 'vb', 'Ha_flux', ...]

Circular dependencies (A depends on B, B depends on A) raise a ValueError at build time with a clear error message.


Serialization

Priors round-trip through YAML, including all parameter expressions. Single-token references serialize as {ref: name}; compound expressions serialize as nested {op, left, right} trees:

# fwhm_narrow + 150
fwhm_broad:
  prior:
    type: Uniform
    low: {op: '+', left: {ref: fwhm_gauss_narrow}, right: 150.0}
    high: 5000.0

# flux_4363_narrow * flux_5007_broad / flux_5007_narrow
flux_4363_broad:
  prior:
    type: Fixed
    value:
      op: /
      left:
        op: '*'
        left: {ref: flux_4363_narrow}
        right: {ref: flux_5007_broad}
      right: {ref: flux_5007_narrow}

The full configuration round-trip preserves all dependency chains. See Configuration Serialization for the complete serialization workflow.