Mark registry¶
The population size distribution f(Q | θ) as one registry shared by the population fit, the missing-mass simulator and the total-emission estimator: a power law, a lognormal, a lognormal body spliced continuously to a Pareto tail, and a mixture of lognormals. Each family has log_prob, sample, the partial first moment truncated_mean that the totals need, and numpyro_prior for its default priors. Tier V.
marks
¶
The mark registry, the population size distribution f(Q | θ) (implementation in plumax.population._src.marks).
LognormalMark
¶
Bases: Module
Lognormal f(q) = LN(q; μ, σ²).
θ = {μ, σ}.
Attributes:
| Name | Type | Description |
|---|---|---|
mu_prior_scale |
float
|
Scale of the Normal(0, ·) prior on μ. |
sigma_prior_scale |
float
|
Scale of the HalfNormal prior on σ. |
Examples:
>>> import jax.numpy as jnp
>>> mark = LognormalMark()
>>> mean = mark.truncated_mean(0.0, jnp.inf, mu=0.0, sigma=1.0)
>>> bool(jnp.allclose(mean, jnp.exp(0.5)))
True
log_survival
¶
log P(Q > q) = log Φ(−(log q − μ)/σ), 0 for q ≤ 0.
Source code in src/plumax/population/_src/marks.py
LognormalParetoMark
¶
Bases: Module
Lognormal body spliced to a Pareto tail at Q_break, continuous there.
f(q) = w LN(q; μ, σ²)/F_LN(Q_b) for q < Q_b and f(q) = (1 − w) α Q_bᵅ q^(−α−1) for q ≥ Q_b, with the body weight w = t/(t + b) set by continuity, where b = LN(Q_b; μ, σ²)/F_LN(Q_b) and t = α/Q_b are the two normalised pieces' densities at the break. One free tail index α; E[Q] is finite for α > 1 and Var[Q] for α > 2.
θ = {μ, σ, α, Q_b}.
Attributes:
| Name | Type | Description |
|---|---|---|
q_break_loc |
float
|
Centre of the LogNormal(log ·, 0.5²) prior on Q_b, a published detection-floor value for the instrument class [kg/s]. |
q_break_log_scale |
float
|
Log-scale of that prior. |
mu_prior_scale |
float
|
Scale of the Normal(0, ·) prior on μ. |
sigma_prior_scale |
float
|
Scale of the HalfNormal prior on σ. |
alpha_bounds |
tuple[float, float]
|
|
Examples:
>>> import jax.numpy as jnp
>>> mark = LognormalParetoMark(q_break_loc=2.0)
>>> theta = dict(mu=0.0, sigma=1.0, alpha=2.5, q_break=2.0)
>>> below = mark.log_prob(jnp.asarray(2.0 - 1e-9), **theta)
>>> above = mark.log_prob(jnp.asarray(2.0), **theta)
>>> bool(jnp.allclose(below, above))
True
log_survival
¶
log_survival(q: ArrayLike, *, mu: ArrayLike, sigma: ArrayLike, alpha: ArrayLike, q_break: ArrayLike) -> Float[Array, '*q']
log P(Q > q): (1 − w)(Q_b/q)ᵅ above the break, plus the body above.
P(Q > q) = (1 − w) + w [Φ(z_b) − Φ(z)]/Φ(z_b) below Q_b.
Source code in src/plumax/population/_src/marks.py
Mark
¶
MixtureLognormalMark
¶
Bases: Module
Mixture of K lognormals f(q) = Σₖ wₖ LN(q; μₖ, σₖ²).
θ = {weights, locs, scales}, each of length K.
Attributes:
| Name | Type | Description |
|---|---|---|
n_components |
int
|
K. |
concentration |
float
|
Dirichlet concentration of the prior on the weights (the same for every component). |
mu_prior_scale |
float
|
Scale of the Normal(0, ·) prior on each μₖ; the sampled μₖ are sorted, which removes label switching. |
sigma_prior_scale |
float
|
Scale of the HalfNormal prior on each σₖ. |
Examples:
>>> import jax.numpy as jnp
>>> mark = MixtureLognormalMark(n_components=2)
>>> theta = dict(
... weights=jnp.array([0.5, 0.5]),
... locs=jnp.array([0.0, 0.0]),
... scales=jnp.array([1.0, 1.0]),
... )
>>> single = LognormalMark().log_prob(2.0, mu=0.0, sigma=1.0)
>>> bool(jnp.allclose(mark.log_prob(2.0, **theta), single))
True
log_survival
¶
log_survival(q: ArrayLike, *, weights: ArrayLike, locs: ArrayLike, scales: ArrayLike) -> Float[Array, '*q']
log P(Q > q) = log Σₖ wₖ Φ(−(log q − μₖ)/σₖ).
Source code in src/plumax/population/_src/marks.py
PowerLawMark
¶
Bases: Module
Pareto power law f(q) = (α − 1)/q_min · (q/q_min)^(−α) on q ≥ q_min.
θ = {α}, α > 1. E[Q] is finite for α > 2; for α ≤ 2 truncated_mean
over an unbounded interval is +∞ (logarithmic in the upper limit at
α = 2).
Attributes:
| Name | Type | Description |
|---|---|---|
q_min |
float
|
Lower end of the support [kg/s]. |
alpha_bounds |
tuple[float, float]
|
|
Examples:
>>> import jax.numpy as jnp
>>> mark = PowerLawMark(q_min=1.0)
>>> float(jnp.exp(mark.log_prob(jnp.asarray(1.0), alpha=3.0)))
2.0
>>> float(mark.truncated_mean(1.0, jnp.inf, alpha=3.0))
2.0
log_survival
¶
log P(Q > q) = (α − 1) log(q_min/q) above q_min, 0 below.
Source code in src/plumax/population/_src/marks.py
make_mark
¶
Build a registry mark family by name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
A key of :data: |
required |
**fields
|
Any
|
The family's fields (support bounds, prior settings). |
{}
|
Returns:
| Type | Description |
|---|---|
Mark
|
The mark family. |
Raises:
| Type | Description |
|---|---|
KeyError
|
For an unknown name. |