Formula reference: sensitivity
Generated from the docstrings of vaft.formula.sensitivity:
6 public functions. Each entry is what
vaft.formula.describe("sensitivity.<name>") prints.
All formula categories
Overview
The mathematics shared by every VAFT domain that linearizes something: a Jacobian by finite differences, covariance pushed through a Jacobian, the same push by sampling, the singular-value spectrum of a Jacobian, and a test of whether the linearization still describes a finite step.
- A forward model
fmaps a 1-D parameter vectorx[n] to a 1-D output vectory[m]; a scalar output is treated asm = 1. The Jacobian isJ[i, j] = dy_i/dx_jwith shape(m, n). - Nothing here knows what the parameters mean. Which perturbation is
physical, which step is admissible and what the derivative is for belong to
the domain that owns
f(#1642 s12); interpreting the result belongs tovaft.validation.sensitivity. - Every function is cheap except
monte_carlo_propagation, which callsfonce per sample and is therefore never invoked by anything else in VAFT: it exists to be called explicitly (#1639 s8). propagate_formula_uncertaintyconnects these kernels to one publicvaft.formulafunction by its parameter names (#1874). It is opt-in: no formula calls it, and a formula’s own signature, values and cost do not change. A formula may carry a domain-owned analytic Jacobian (vaft.formula._propagation); otherwise the Jacobian is a scale-aware finite difference.
Functions
finite_difference_jacobian | Jacobian of a vector function by forward or central differences. |
linear_covariance_propagation | Output covariance of a linearized model, correlations included. |
linearity_ratio | How much of a finite response the linearization misses. |
monte_carlo_propagation | Output mean and covariance by sampling Gaussian inputs through the full model. |
propagate_formula_uncertainty | Propagate input uncertainty through one vaft.formula function, correlations included.
|
singular_value_spectrum | Singular values, numerical rank and null space of a (column-scaled) Jacobian. |
No function matches.
finite_difference_jacobian
source
finite_difference_jacobian(f, x, step=None, scheme='central')
Show source vaft/formula/sensitivity.py, lines 51–125
def finite_difference_jacobian(
f: Callable[[np.ndarray], np.ndarray],
x: np.ndarray,
step: float | np.ndarray | None = None,
scheme: str = "central",
) -> np.ndarray:
r"""Jacobian of a vector function by forward or central differences.
$$J_{ij} \approx \frac{f_i(x + h_j e_j) - f_i(x - h_j e_j)}{2 h_j}
\quad\text{(central)}, \qquad
J_{ij} \approx \frac{f_i(x + h_j e_j) - f_i(x)}{h_j} \quad\text{(forward)}$$
Parameters
----------
f : callable
Forward model taking a parameter vector and returning an output vector [any].
x : array-like
Point of linearization [any].
step : float or array-like, optional
Absolute step per parameter; default
$h_j = \epsilon^{1/3}\max(1, |x_j|)$ (central) or
$\epsilon^{1/2}\max(1, |x_j|)$ (forward) [any].
scheme : str, optional
``"central"`` (second order) or ``"forward"`` (first order); either
costs n + 1 or 2n + 1 evaluations, the extra one fixing the output
size [-].
Returns
-------
numpy.ndarray
``(m, n)`` Jacobian [any].
Raises
------
ValueError
On an unknown scheme or a non-positive step.
Numerical notes
---------------
The default steps balance truncation against round-off for a model
evaluated to machine precision. They are absolute and floored at one
unit, so a parameter far below unit magnitude (``1e-8``) is stepped far
beyond its own size -- possibly across a domain edge such as a sign or a
logarithm; give such a parameter its own ``step``. A model with its own solver tolerance
(an iterative equilibrium, an external code) needs a step well above that
tolerance, chosen by its owner: a finite difference across a convergence
threshold measures the solver, not the physics (#1642 s18).
References
----------
.. [1] J. Nocedal and S. J. Wright, *Numerical Optimization*, 2nd ed.,
Springer (2006), Sec. 8.1.
"""
if scheme not in ("central", "forward"):
raise ValueError(f"scheme must be 'central' or 'forward', got {scheme!r}")
x0 = _vector(x)
eps = np.finfo(float).eps
if step is None:
h = (eps ** (1 / 3) if scheme == "central" else eps ** 0.5) * np.maximum(1.0, np.abs(x0))
else:
h = np.broadcast_to(np.asarray(step, dtype=float), x0.shape).copy()
if np.any(~np.isfinite(h)) or np.any(h <= 0):
raise ValueError("finite-difference steps must be finite and positive")
f0 = _vector(f(x0.copy()))
jac = np.empty((f0.size, x0.size))
for j in range(x0.size):
up = x0.copy()
up[j] += h[j]
if scheme == "central":
down = x0.copy()
down[j] -= h[j]
jac[:, j] = (_vector(f(up)) - _vector(f(down))) / (2.0 * h[j])
else:
jac[:, j] = (_vector(f(up)) - f0) / h[j]
return jacJacobian of a vector function by forward or central differences.
\[J_{ij} \approx \frac{f_i(x + h_j e_j) - f_i(x - h_j e_j)}{2 h_j} \quad\text{(central)}, \qquad J_{ij} \approx \frac{f_i(x + h_j e_j) - f_i(x)}{h_j} \quad\text{(forward)}\]| Name | Type | Unit | Description | |||||
|---|---|---|---|---|---|---|---|---|
f | callable | any | Forward model taking a parameter vector and returning an output vector. | |||||
x | array-like | any | Point of linearization. | |||||
step | float or array-like, optional | any |
| |||||
scheme | str, optional | - |
| |||||
| Returns | ||||||||
| — | numpy.ndarray | any |
| |||||
Raises
ValueErrorOn an unknown scheme or a non-positive step.
Numerical notes · References
Numerical notes
The default steps balance truncation against round-off for a model
evaluated to machine precision. They are absolute and floored at one
unit, so a parameter far below unit magnitude (1e-8) is stepped far
beyond its own size – possibly across a domain edge such as a sign or a
logarithm; give such a parameter its own step. A model with its own solver tolerance
(an iterative equilibrium, an external code) needs a step well above that
tolerance, chosen by its owner: a finite difference across a convergence
threshold measures the solver, not the physics (#1642 s18).
References
- J. Nocedal and S. J. Wright, Numerical Optimization, 2nd ed., Springer (2006), Sec. 8.1.
linear_covariance_propagation
source
linear_covariance_propagation(jacobian, covariance)
Show source vaft/formula/sensitivity.py, lines 141–178
def linear_covariance_propagation(jacobian: np.ndarray, covariance: np.ndarray) -> np.ndarray:
r"""Output covariance of a linearized model, correlations included.
$$\Sigma_y = J\,\Sigma_x\,J^{\mathsf T}$$
Parameters
----------
jacobian : array-like
``(m, n)`` sensitivity matrix [any].
covariance : array-like
``(n, n)`` input covariance; a 1-D array is read as independent
variances [any].
Returns
-------
numpy.ndarray
``(m, m)`` output covariance, symmetrized [any].
Raises
------
ValueError
When the shapes do not agree or the input covariance is not symmetric
to round-off (relative ``1e-12`` of its largest entry).
Assumptions
-----------
The model is linear over the spread of the inputs; :func:`linearity_ratio`
and :func:`monte_carlo_propagation` test that.
References
----------
.. [1] JCGM 100:2008, *Evaluation of measurement data -- Guide to the
expression of uncertainty in measurement*, Sec. 5.2, Eq. (13).
"""
jac = np.atleast_2d(np.asarray(jacobian, dtype=float))
cov = _covariance(covariance, jac.shape[1])
out = jac @ cov @ jac.T
return 0.5 * (out + out.T)Output covariance of a linearized model, correlations included.
\[\Sigma_y = J\,\Sigma_x\,J^{\mathsf T}\]| Name | Type | Unit | Description |
|---|---|---|---|
jacobian | array-like | any |
|
covariance | array-like | any |
|
| Returns | |||
| — | numpy.ndarray | any |
|
Raises
ValueErrorWhen the shapes do not agree or the input covariance is not symmetric to round-off (relative1e-12of its largest entry).
Assumptions · References
Assumptions
The model is linear over the spread of the inputs; linearity_ratio
and monte_carlo_propagation test that.
References
- JCGM 100:2008, Evaluation of measurement data – Guide to the expression of uncertainty in measurement, Sec. 5.2, Eq. (13).
linearity_ratio
source
linearity_ratio(f, x, step, jacobian, output_scale=None)
Show source vaft/formula/sensitivity.py, lines 330–378
def linearity_ratio(
f: Callable[[np.ndarray], np.ndarray],
x: np.ndarray,
step: np.ndarray,
jacobian: np.ndarray,
output_scale: np.ndarray | None = None,
) -> float:
r"""How much of a finite response the linearization misses.
$$r = \frac{\lVert W(f(x + \delta) - f(x) - J\delta)\rVert}{\lVert W(f(x + \delta) - f(x))\rVert},
\qquad W = \operatorname{diag}(1/s_i)$$
Parameters
----------
f : callable
Forward model [any].
x : array-like
Point of linearization [any].
step : array-like
Finite perturbation $\delta$, typically one standard deviation along
a direction of interest [any].
jacobian : array-like
$J$ at ``x`` [any].
output_scale : array-like, optional
Typical magnitude $s_i$ per output, so outputs in different units
weigh alike; default unweighted [any].
Returns
-------
float
$r$; 0 for an exactly linear response, of order 1 when the local
Jacobian no longer describes the step; ``nan`` when the response is
zero [-].
Assumptions
-----------
One direction is tested per call; a model can be linear along one and not
another.
"""
x0 = _vector(x)
delta = _vector(step)
f0 = _vector(f(x0.copy()))
response = _vector(f(x0 + delta)) - f0
predicted = np.atleast_2d(np.asarray(jacobian, dtype=float)) @ delta
weight = np.ones_like(response) if output_scale is None else 1.0 / _vector(output_scale)
norm = float(np.linalg.norm(weight * response))
if norm == 0.0 or not np.isfinite(norm):
return float("nan")
return float(np.linalg.norm(weight * (response - predicted)) / norm)How much of a finite response the linearization misses.
\[r = \frac{\lVert W(f(x + \delta) - f(x) - J\delta)\rVert}{\lVert W(f(x + \delta) - f(x))\rVert}, \qquad W = \operatorname{diag}(1/s_i)\]| Name | Type | Unit | Description |
|---|---|---|---|
f | callable | any | Forward model. |
x | array-like | any | Point of linearization. |
step | array-like | any | Finite perturbation $\delta$, typically one standard deviation along a direction of interest. |
jacobian | array-like | any | $J$ at |
output_scale | array-like, optional | any | Typical magnitude $s_i$ per output, so outputs in different units weigh alike; default unweighted. |
| Returns | |||
| — | float | - | $r$; 0 for an exactly linear response, of order 1 when the local Jacobian no longer describes the step; |
Assumptions
Assumptions
One direction is tested per call; a model can be linear along one and not another.
monte_carlo_propagation
source
monte_carlo_propagation(f, mean, covariance, samples=2000, seed=0)
Output mean and covariance by sampling Gaussian inputs through the full model.
\[x^{(k)} \sim \mathcal N(\mu, \Sigma_x), \qquad \hat\Sigma_y = \frac{1}{K-1}\sum_k (y^{(k)} - \bar y)(y^{(k)} - \bar y)^{\mathsf T}\]| Name | Type | Unit | Description |
|---|---|---|---|
f | callable | any | Forward model, called once per sample. |
mean | array-like | any | Input mean. |
covariance | array-like | any | Input covariance, positive semi-definite; 1-D means independent variances. |
samples | int, optional | - | Number of draws $K$. |
seed | int, optional | - | Seed of the NumPy generator, so a result is reproducible. |
| Returns | |||
| — | dict | any |
|
Raises
ValueErrorWhensamplesis below 2, or the covariance does not match the mean, is not symmetric, or is not positive semi-definite.
Assumptions · Limitations · References
Assumptions
Gaussian inputs. Expensive: samples model evaluations, so it is never
called implicitly (#1639 s8); the cost is the caller’s decision.
Limitations
The statistics are over the accepted draws only, so a model that fails
on part of the input distribution yields a covariance conditioned on
success; rejected says how much was conditioned away. An output with
infinite variance (a pole inside the input distribution) has a sample
covariance that does not converge with samples.
References
- JCGM 101:2008, Propagation of distributions using a Monte Carlo method, Sec. 7.
propagate_formula_uncertainty
source
propagate_formula_uncertainty(formula, inputs, *, input_names=None, covariance=None, std=None, method='linear', derivative='auto', fd_step=None, domain=None, samples=2000, seed=0)
Propagate input uncertainty through one vaft.formula function, correlations included.
| Name | Type | Unit | Description | |||
|---|---|---|---|---|---|---|
formula | callable | any | A public formula, called with keyword arguments. | |||
inputs | mapping | any | Every argument the call needs, by parameter name; uncertain inputs at their mean values. | |||
input_names | sequence of str, optional | - | The uncertain inputs, in the order of the covariance axes; every other entry of | |||
covariance | array-like, optional | any |
| |||
std | array-like, optional | any | 1-D standard deviations of independent inputs, | |||
method | str, optional | - |
| |||
derivative | str, optional | - |
| |||
fd_step | float, array-like or mapping, optional | any |
| |||
domain | callable, optional | - | Predicate on the call’s arguments, | |||
samples | int, optional | - | Monte Carlo draws. | |||
seed | int, optional | - | Monte Carlo seed. | |||
| Returns | ||||||
| — | FormulaPropagation | any | Value, covariance, standard deviation and their provenance. | |||
Raises
ValueErrorNo uncertainty given (unknown is not zero), bothcovarianceandstd, an uncertain input missing, non-scalar, unknown to the formula or non-finite, an uncertainty that is NaN, infinite, negative, not symmetric or not positive semi-definite, input means outside the formula’s domain (declared, or a raised or non-finite evaluation), a non-finite Jacobian, a Monte Carlo run that accepts fewer than two draws, or an unknown or inconsistentmethod/derivative.
Assumptions · Limitations · References
Assumptions
"linear" is a local first-order result at the input means; it is
not a verified global uncertainty and carries no model-form or
fitted-coefficient uncertainty. "monte_carlo" assumes Gaussian
inputs (no other input distribution is supported), and its statistics
cover the accepted draws only: a draw outside the domain is rejected and
counted in rejected.
Limitations
Uncertain inputs must be scalars; profile- and field-valued covariance is out of scope (#1874). A finite difference is not a solver-native linearization.
References
- JCGM 100:2008, Guide to the expression of uncertainty in measurement, Sec. 5.2.
- JCGM 101:2008, Propagation of distributions using a Monte Carlo method, Sec. 7.
singular_value_spectrum
source
singular_value_spectrum(jacobian, column_scale=None, rtol=None)
Show source vaft/formula/sensitivity.py, lines 267–327
def singular_value_spectrum(
jacobian: np.ndarray,
column_scale: np.ndarray | None = None,
rtol: float | None = None,
) -> dict:
r"""Singular values, numerical rank and null space of a (column-scaled) Jacobian.
$$J D = U \Sigma V^{\mathsf T}, \qquad
\operatorname{rank} = \#\{\sigma_k > \tau\,\sigma_1\}, \qquad
\kappa = \sigma_1/\sigma_{\min}$$
Parameters
----------
jacobian : array-like
``(m, n)`` sensitivity matrix, ideally already weighted by the inverse
measurement uncertainty so its rows are commensurate [any].
column_scale : array-like, optional
Diagonal of $D$, e.g. a typical magnitude per parameter, making the
columns commensurate; default no scaling [any].
rtol : float, optional
Relative rank tolerance $\tau$; default $\max(m, n)\,\epsilon$ [-].
Returns
-------
dict
``singular_values`` (descending), ``rank``, ``nullity`` (n - rank),
``condition_number`` (inf when rank deficient), ``null_space``
(``(n, nullity)`` basis in the *unscaled* parameters) [any].
Raises
------
ValueError
When the matrix is not finite or ``column_scale`` has a zero.
Numerical notes
---------------
The SVD is taken of $JD$ itself, never of $J^{\mathsf T}J$, which would
square the condition number -- the practice of
:func:`vaft.code.efit.analyze_efit_identifiability`.
References
----------
.. [1] G. H. Golub and C. F. Van Loan, *Matrix Computations*, 4th ed.,
Johns Hopkins (2013), Secs. 2.4 and 5.4.
"""
jac = np.atleast_2d(np.asarray(jacobian, dtype=float))
if not np.all(np.isfinite(jac)):
raise ValueError("Jacobian must be finite")
n = jac.shape[1]
scale = np.ones(n) if column_scale is None else _vector(column_scale)
if scale.shape != (n,) or np.any(scale == 0) or not np.all(np.isfinite(scale)):
raise ValueError("column_scale must be finite, non-zero and one per column")
_, sigma, vt = np.linalg.svd(jac * scale, full_matrices=True)
tol = (max(jac.shape) * np.finfo(float).eps) if rtol is None else float(rtol)
rank = int(np.sum(sigma > tol * sigma[0])) if sigma.size and sigma[0] > 0 else 0
null = (vt[rank:].T * scale[:, None]) if rank < n else np.zeros((n, 0))
if null.shape[1]:
null = null / np.linalg.norm(null, axis=0)
condition = float(sigma[0] / sigma[n - 1]) if rank == n else float("inf")
return {"singular_values": sigma, "rank": rank, "nullity": n - rank,
"condition_number": condition, "null_space": null}Singular values, numerical rank and null space of a (column-scaled) Jacobian.
\[J D = U \Sigma V^{\mathsf T}, \qquad \operatorname{rank} = \#\{\sigma_k > \tau\,\sigma_1\}, \qquad \kappa = \sigma_1/\sigma_{\min}\]| Name | Type | Unit | Description |
|---|---|---|---|
jacobian | array-like | any |
|
column_scale | array-like, optional | any | Diagonal of $D$, e.g. a typical magnitude per parameter, making the columns commensurate; default no scaling. |
rtol | float, optional | - | Relative rank tolerance $\tau$; default $\max(m, n)\,\epsilon$. |
| Returns | |||
| — | dict | any |
|
Raises
ValueErrorWhen the matrix is not finite orcolumn_scalehas a zero.
Numerical notes · References
Numerical notes
The SVD is taken of $JD$ itself, never of $J^{\mathsf T}J$, which would
square the condition number – the practice of
vaft.code.efit.analyze_efit_identifiability.
References
- G. H. Golub and C. F. Van Loan, Matrix Computations, 4th ed., Johns Hopkins (2013), Secs. 2.4 and 5.4.