DALI tensors#
This section shows how to construct Derivative Approximation for
LIkelihoods (DALI) tensors using derivkit.forecast_kit.ForecastKit.
The DALI expansion extends the Fisher matrix by including higher-order
derivative information, allowing non-Gaussian structure in the likelihood
to be approximated locally around a fiducial parameter point theta0.
In DerivKit, DALI tensors are returned using an introduced-at-order convention: each forecast order contributes a tuple of tensors, and all orders up to the requested one are returned.
The model must map parameters theta to a 1D data vector, and the observable
covariance matrix must have shape (n, n), where n is the length of the
data vector.
For a conceptual overview of DALI forecasting and its interpretation, see ForecastKit.
If you want to visualize parameter contours from DALI tensors, see the example DALI contours, which shows how to generate GetDist samples and plot confidence regions from the returned multiplets.
DALI tensor shapes#
For p model parameters, the DALI expansion returns tensors with the
following shapes:
order 1 (Fisher; “singlet-DALI”): -
Fwith shape(p, p)order 2 (doublet-DALI): -
D1with shape(p, p, p)-D2with shape(p, p, p, p)order 3 (triplet-DALI): -
T1with shape(p, p, p, p)-T2with shape(p, p, p, p, p)-T3with shape(p, p, p, p, p, p)
All tensors are evaluated at the fiducial parameter point theta0.
Basic usage#
The example below constructs DALI tensors around a fiducial parameter
point theta0 using a simple toy model.
>>> import numpy as np
>>> from derivkit import ForecastKit
>>> np.set_printoptions(precision=8, suppress=True)
>>> # Define a simple toy model: R^2 -> R^3
>>> def model(theta):
... a, b = theta
... return np.array([a, b, a + 2.0 * b], dtype=float)
>>> # Fiducial parameters and covariance
>>> theta0 = np.array([1.0, 2.0])
>>> cov = np.eye(3)
>>> # Build ForecastKit and compute DALI tensors up to second order
>>> fk = ForecastKit(function=model, theta0=theta0, cov=cov)
>>> dali = fk.dali(forecast_order=2)
>>> F = dali[1][0]
>>> D1, D2 = dali[2]
>>> print(F.shape)
(2, 2)
>>> print(D1.shape)
(2, 2, 2)
>>> print(D2.shape)
(2, 2, 2, 2)
Choosing a derivative backend#
As with Fisher forecasting, you can control how derivatives are computed by
passing method and backend-specific options.
All keyword arguments are forwarded to
derivkit.derivative_kit.DerivativeKit.differentiate().
>>> import numpy as np
>>> from derivkit import ForecastKit
>>> np.set_printoptions(precision=8, suppress=True)
>>> def model(theta):
... a, b = theta
... return np.array([a, b, a + 2.0 * b], dtype=float)
>>> theta0 = np.array([1.0, 2.0])
>>> cov = np.eye(3)
>>> fk = ForecastKit(function=model, theta0=theta0, cov=cov)
>>> dali = fk.dali(
... forecast_order=2,
... method="finite",
... n_workers=2,
... stepsize=1e-2,
... num_points=5,
... extrapolation="ridders",
... levels=4,
... )
>>> D1, D2 = dali[2]
>>> print(D1.shape)
(2, 2, 2)
>>> print(D2.shape)
(2, 2, 2, 2)
Parallel execution#
DALI tensor components can be computed in parallel using n_workers.
This parallelizes derivative evaluations across parameters and tensor entries.
>>> import numpy as np
>>> from derivkit import ForecastKit
>>> def model(theta):
... return np.array([theta[0], theta[1], theta[0] + 2.0 * theta[1]])
>>> fk = ForecastKit(
... function=model,
... theta0=np.array([1.0, 2.0]),
... cov=np.eye(3),
... )
>>> dali = fk.dali(forecast_order=2, n_workers=4)
>>> D1, D2 = dali[2]
>>> print(D1.shape)
(2, 2, 2)
>>> print(D2.shape)
(2, 2, 2, 2)
Notes#
DALI tensors are evaluated locally at
theta0.Each forecast order contributes a multiplet of tensors, returned via an introduced-at-order convention.
The likelihood is assumed Gaussian in the data with fixed covariance.
Higher-order forecasts increase computational cost relative to Fisher.
Changing the derivative backend affects numerical accuracy but not tensor structure.