mixle.inference.decision module

Bayes-optimal decisions under a fitted mixle posterior.

Given a Posterior (the Monte-Carlo law over an unknown – a parameter, a latent state, or a future outcome), a loss function loss(action, draw) -> float, and a finite set of candidate actions, bayes_action() returns the action that minimises the posterior expected loss and a tail-risk profile (CVaR + loss quantiles) of the chosen action.

This is the decision half of the platform’s differentiator: a point predictor returns a number; a mixle model returns the action that is optimal under the user’s own loss and explicit about its tail risk.

It depends only on the public Posterior.samples(n, rng) contract (mixle.inference.posterior), so it carries no serving / HTTP opinion – a lack of capability is reported via mixle.capability.CapabilityError.

bayes_action(posterior, loss, actions, *, n=2000, seed=0, cvar_alpha=0.1, quantiles=(0.05, 0.5, 0.95), vectorized=None, report='selection')[source]

Pick the Bayes action: argmin_a E_{draw ~ posterior}[ loss(a, draw) ].

The winner’s reported numbers carry selection bias unless you pay to remove it. The chosen action minimises K NOISY Monte-Carlo estimates, so its own expected_loss is optimistic post-argmin (the winner’s curse): measured bias about -2.5 Monte-Carlo standard errors at K = 100 candidates – and it does NOT shrink with n, because more draws shrink the SE and the bias together. expected_loss_mc_se is returned so the optimism has a scale; with many near-tied candidates, read the winner’s advertised loss as roughly that many SEs too good. report="fresh-draws" removes the bias instead: the choice is made exactly as before, then n NEW posterior draws are taken and the winner’s reported expected_loss / risk_profile are recomputed on them (selection-independent, hence conditionally unbiased for the chosen action). That costs one more samples(n, rng) call and n more loss evaluations (one vectorized call) FOR THE WINNER ONLY – deliberately not the default, because a loss may be metered or side-effecting and the exact-invocation-count contract below is itself a stated guarantee; selection_expected_loss then preserves the in-sample value the action won with. alternatives always carries selection-stage estimates.

Parameters:
  • posterior (Any) – any object exposing samples(n, rng) – e.g. mixle.inference.posterior(model, data, over=...) (parameter, latent, or predictive).

  • loss (Callable[[Any, Any], float]) – loss(action, draw) -> float (or a numpy-vectorized loss(action, draws) -> array).

  • actions (Sequence[Any]) – the finite candidate-action set to minimise over.

  • n (int) – number of posterior draws for the Monte-Carlo expectation.

  • seed (int) – RNG seed for the posterior draw (reproducible).

  • cvar_alpha (float) – tail mass for the CVaR / VaR of the chosen action (0.1 -> worst 10%).

  • quantiles (Sequence[float]) – loss quantiles to report per action. Reported quantiles (and the CVaR/VaR fields) are plug-in Monte Carlo estimates and carry NO uncertainty quantification of their own – tail quantities converge far more slowly than the mean whose expected_loss_mc_se is reported, so raise n before leaning on a far tail (STAT-RR19-17).

  • vectorized (bool | None) – the loss’s calling convention. True -> called once per action with the whole draw array; False -> called once per draw; None (default) -> read loss.vectorized if the loss declares it, else auto-detect by probing the array call once. A probe is a real invocation of the loss, so a loss that keeps state, records calls, or has side effects should declare its convention (argument or attribute) to be evaluated exactly the required number of times.

  • report (str) – "selection" (default) reports the winner’s selection-stage estimates plus their Monte-Carlo SE; "fresh-draws" re-estimates the winner on n new draws (see above).

Returns:

{action, action_index, expected_loss, expected_loss_mc_se, risk_profile, alternatives, report, vectorized} – plus selection_expected_loss when report="fresh-draws".

Raises:
  • ValueError – if actions is empty, n is not positive, cvar_alpha is outside (0, 1], a quantile level is outside [0, 1], the posterior returns an empty, ragged, or wrong-sized sample batch (samples(n, rng) must return exactly n draws), or a loss evaluates to NaN.

  • TypeError – if n is not an exact integer, or cvar_alpha/a quantile level is a Boolean, a string, or anything else that is not already a real number.

  • CapabilityError – if posterior does not expose the samples(n, rng) contract.

Return type:

dict[str, Any]

class RiskProfile(expected_loss, cvar, cvar_alpha, var, quantiles, std)[source]

Bases: object

The tail-risk summary of a single action’s posterior loss distribution.

Parameters:
as_dict()[source]

Return risk metrics and quantiles as JSON-compatible data.

Return type:

dict[str, Any]