derivkit.forecasting.expansions module#

Utilities for evaluating Fisher and DALI likelihood expansions.

This module provides functional helpers to evaluate approximate likelihoods (or posterior) surfaces from forecast tensors.

Conventions#

This module uses a single convention throughout:

  • delta_chi2 is defined from the displacement d = theta - theta0.

  • The log posterior is returned (up to an additive constant) as:

    log p(theta) = logprior(theta) - 0.5 * delta_chi2(theta)
    

When bias is provided, an additional systematic source term is included.

With the forecast tensors returned by derivkit.forecasting.get_forecast_tensors() (using the introduced-at-order convention):

  • dali[1] == (F,)

  • dali[2] == (D1, D2)

  • dali[3] == (T1, T2, T3)

  • dali[4] == (Qa1, Qa2, Qa3, Qa4)

the DALI delta_chi2 is:

  • order 1 (Fisher): d.T @ F @ d

  • order 2 (doublet): add D1[d^3] + (1/4) D2[d^4]

  • order 3 (triplet): add (1/3) T1[d^4] + (1/6) T2[d^5] + (1/36) T3[d^6]

  • order 4 (quadruplet): add (1/12) Qa1[d^5] + (1/24) Qa2[d^6] + (1/72) Qa3[d^7] + (1/576) Qa4[d^8]

GetDist convention#

GetDist expects loglikes to be the negative log posterior, up to a constant. Since this module defines:

log p = logprior - 0.5 * delta_chi2 + const

a compatible choice for GetDist is:

loglikes = -logprior + 0.5 * delta_chi2 - systematic_source

(optionally shifted by an additive constant for numerical stability). The systematic source is zero when bias=None.

derivkit.forecasting.expansions.build_delta_chi2_dali(theta: NDArray[floating], theta0: NDArray[floating], dali: Any, *, forecast_order: int | None = 2) → float#

Compute delta_chi2 under the DALI approximation.

This evaluates a scalar delta_chi2 from the displacement d = theta - theta0 using forecast tensors returned by derivkit.forecasting.get_forecast_tensors().

The input must be the dict form using the introduced-at-order convention:

  • dali[1] == (F,) with F of shape (p, p)

  • dali[2] == (D1, D2) with shapes (p, p, p) and (p, p, p, p)

  • dali[3] == (T1, T2, T3) with shapes (p,)*4, (p,)*5, (p,)*6

  • dali[4] == (Qa1, Qa2, Qa3, Qa4) with shapes (p,)*5, (p,)*6`, (p,)*7, (p,)*8

The evaluated quantity is:

  • order 2: d.T @ F @ d + D1[d^3] + (1/4) D2[d^4]

  • order 3: order 2 plus (1/3) T1[d^4] + (1/6) T2[d^5] + (1/36) T3[d^6]

  • order 4: order 3 plus (1/12) Qa1[d^5] + (1/24) Qa2[d^6] + (1/72) Qa3[d^7] + (1/576) Qa4[d^8]

Parameters:
  • theta – Evaluation point in parameter space.

  • theta0 – Expansion point (fiducial parameters).

  • dali – Forecast tensors as a dict.

  • forecast_order – Maximum order to include. If None, uses the highest key in dali and requires it to be at least 2.

Returns:

Scalar delta_chi2.

Raises:
  • TypeError – If dali is not a dict.

  • ValueError – If required tensor orders are missing or have incompatible shapes.

derivkit.forecasting.expansions.build_delta_chi2_fisher(theta: NDArray[floating], theta0: NDArray[floating], fisher: NDArray[floating]) → float#

Computes a displacement chi-squared under the Fisher approximation.

Parameters:
  • theta – Evaluation point in parameter space. This is the trial parameter vector at which the Fisher expansion is evaluated.

  • theta0 – Expansion point (reference parameter vector). The Fisher matrix is assumed to have been computed at this point, and the expansion is taken in the displacement theta - theta0.

  • fisher – Fisher matrix with shape (p, p) with p the number of parameters.

Returns:

The scalar delta chi-squared value between theta and theta_0.

derivkit.forecasting.expansions.build_logposterior_dali(theta: NDArray[floating], theta0: NDArray[floating], dali: Any, *, bias: dict[int, NDArray[floating]] | None = None, forecast_order: int | None = 2, prior_terms: Sequence[tuple[str, dict[str, Any]] | dict[str, Any]] | None = None, prior_bounds: Sequence[tuple[float | None, float | None]] | None = None, logprior: Callable[[NDArray[floating]], float] | None = None) → float#

Computes the log posterior under the DALI approximation.

The log posterior combines the prior, the DALI likelihood expansion, and an optional systematic mismatch contribution.

The DALI contribution is evaluated using build_delta_chi2_dali(). When bias is provided, the mismatch tensors are contracted with the parameter displacement and weighted by the factorial of their order.

The mismatch tensors can be obtained from derivkit.forecasting.dali_bias.build_dali_bias_tensor().

Parameters:
  • theta – Evaluation point in parameter space.

  • theta0 – Expansion point (fiducial parameters).

  • dali – Forecast tensors as a dict in the introduced-at-order convention.

  • bias – Optional dictionary of systematic mismatch tensors returned by derivkit.forecasting.dali_bias.build_dali_bias_tensor(). The dictionary maps derivative orders to parameter-space tensors. The tensors contribute to the log posterior through the requested forecast_order. If None, no systematic mismatch is included.

  • forecast_order – Maximum order to include in delta_chi2. If None, uses the highest key in dali.

  • prior_terms – Prior term specification passed to build_prior().

  • prior_bounds – Global hard bounds passed to build_prior().

  • logprior – Optional custom log-prior callable.

Returns:

Scalar log posterior value (up to an additive constant). If the prior evaluates to a non-finite value, returns -np.inf.

derivkit.forecasting.expansions.build_logposterior_fisher(theta: NDArray[floating], theta0: NDArray[floating], fisher: NDArray[floating], *, bias: NDArray[floating] | None = None, prior_terms: Sequence[tuple[str, dict[str, Any]] | dict[str, Any]] | None = None, prior_bounds: Sequence[tuple[float | None, float | None]] | None = None, logprior: Callable[[NDArray[floating]], float] | None = None) → float#

Computes the log posterior under the Fisher approximation.

The returned value is defined up to an additive constant in log space. This corresponds to an overall multiplicative normalization of the posterior density in probability space.

If no prior is provided, this returns the Fisher log-likelihoods expansion with a flat prior and no hard cutoffs.

The Fisher approximation corresponds to a purely quadratic delta_chi2 surface:

delta_chi2 = d.T @ F @ d

so the log posterior is:

log p = -0.5 * delta_chi2

The Fisher approximation describes a Gaussian likelihood through its quadratic form. The enclosed probability associated with a given delta_chi2 threshold depends on the number of parameters.

The DALI approximation extends this quadratic form with higher-order terms, allowing the likelihood to depart from Gaussianity. See build_delta_chi2_dali() for the corresponding DALI expansion.

Parameters:
  • theta – Evaluation point in parameter space. This is the trial parameter vector at which the Fisher/DALI expansion is evaluated.

  • theta0 – Expansion point (reference parameter vector). The Fisher matrix and any DALI tensors are assumed to have been computed at this point, and the expansion is taken in the displacement theta - theta0.

  • fisher – Fisher matrix with shape (p, p) with p the number of parameters.

  • bias – Optional linear systematic-bias vector of shape (p,). If provided, adds bias @ (theta - theta0) to the Fisher log posterior.

  • prior_terms – prior term specification passed to derivkit.forecasting.priors.core.build_prior().

  • prior_bounds – Global hard bounds passed to derivkit.forecasting.priors.core.build_prior().

  • logprior – Optional custom log-prior callable. If it returns a non-finite value, the posterior is treated as zero at that point and the function returns -np.inf.

Returns:

Scalar log posterior value, defined up to an additive constant.

derivkit.forecasting.expansions.build_subspace(idx: Sequence[int], *, theta0: NDArray[floating], fisher: NDArray[floating] | None = None, dali: dict[int, tuple[NDArray[floating], ...]] | None = None) → dict[str, Any]#

Extracts a parameter subspace for Fisher or DALI expansions.

This returns a slice through parameter space: parameters not in idx are held fixed at their expansion values. This is not a marginalization.

Provide exactly one of fisher or dali:

  • Fisher: fisher has shape (p, p) and the return dict contains {"theta0": theta0_sub, "fisher": fisher_sub}.

  • DALI: dali is the dict form returned by derivkit.forecasting.get_forecast_tensors() using the introduced-at-order convention, and the return dict contains {"theta0": theta0_sub, "dali": dali_sub}.

Parameters:
  • idx – Parameter indices to extract.

  • theta0 – Expansion point of shape (p,).

  • fisher – Fisher matrix of shape (p, p).

  • dali – Forecast tensors as a dict mapping order -> multiplet.

Returns:

A dict containing the sliced objects. Always includes "theta0". Includes "fisher" if fisher was provided, or "dali" if dali was provided.

Raises:
  • ValueError – If not exactly one of fisher or dali is provided.

  • TypeError – If idx contains non-integers, or if dali is not a dict.

  • IndexError – If any index in idx is out of bounds.

  • ValueError – If the provided arrays have incompatible shapes.