derivkit 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. derivkit/__init__.py +22 -0
  2. derivkit/calculus/__init__.py +17 -0
  3. derivkit/calculus/calculus_core.py +152 -0
  4. derivkit/calculus/gradient.py +97 -0
  5. derivkit/calculus/hessian.py +528 -0
  6. derivkit/calculus/hyper_hessian.py +296 -0
  7. derivkit/calculus/jacobian.py +156 -0
  8. derivkit/calculus_kit.py +128 -0
  9. derivkit/derivative_kit.py +315 -0
  10. derivkit/derivatives/__init__.py +6 -0
  11. derivkit/derivatives/adaptive/__init__.py +5 -0
  12. derivkit/derivatives/adaptive/adaptive_fit.py +238 -0
  13. derivkit/derivatives/adaptive/batch_eval.py +179 -0
  14. derivkit/derivatives/adaptive/diagnostics.py +325 -0
  15. derivkit/derivatives/adaptive/grid.py +333 -0
  16. derivkit/derivatives/adaptive/polyfit_utils.py +513 -0
  17. derivkit/derivatives/adaptive/spacing.py +66 -0
  18. derivkit/derivatives/adaptive/transforms.py +245 -0
  19. derivkit/derivatives/autodiff/__init__.py +1 -0
  20. derivkit/derivatives/autodiff/jax_autodiff.py +95 -0
  21. derivkit/derivatives/autodiff/jax_core.py +217 -0
  22. derivkit/derivatives/autodiff/jax_utils.py +146 -0
  23. derivkit/derivatives/finite/__init__.py +5 -0
  24. derivkit/derivatives/finite/batch_eval.py +91 -0
  25. derivkit/derivatives/finite/core.py +84 -0
  26. derivkit/derivatives/finite/extrapolators.py +511 -0
  27. derivkit/derivatives/finite/finite_difference.py +247 -0
  28. derivkit/derivatives/finite/stencil.py +206 -0
  29. derivkit/derivatives/fornberg.py +245 -0
  30. derivkit/derivatives/local_polynomial_derivative/__init__.py +1 -0
  31. derivkit/derivatives/local_polynomial_derivative/diagnostics.py +90 -0
  32. derivkit/derivatives/local_polynomial_derivative/fit.py +199 -0
  33. derivkit/derivatives/local_polynomial_derivative/local_poly_config.py +95 -0
  34. derivkit/derivatives/local_polynomial_derivative/local_polynomial_derivative.py +205 -0
  35. derivkit/derivatives/local_polynomial_derivative/sampling.py +72 -0
  36. derivkit/derivatives/tabulated_model/__init__.py +1 -0
  37. derivkit/derivatives/tabulated_model/one_d.py +247 -0
  38. derivkit/forecast_kit.py +783 -0
  39. derivkit/forecasting/__init__.py +1 -0
  40. derivkit/forecasting/dali.py +78 -0
  41. derivkit/forecasting/expansions.py +486 -0
  42. derivkit/forecasting/fisher.py +298 -0
  43. derivkit/forecasting/fisher_gaussian.py +171 -0
  44. derivkit/forecasting/fisher_xy.py +357 -0
  45. derivkit/forecasting/forecast_core.py +313 -0
  46. derivkit/forecasting/getdist_dali_samples.py +429 -0
  47. derivkit/forecasting/getdist_fisher_samples.py +235 -0
  48. derivkit/forecasting/laplace.py +259 -0
  49. derivkit/forecasting/priors_core.py +860 -0
  50. derivkit/forecasting/sampling_utils.py +388 -0
  51. derivkit/likelihood_kit.py +114 -0
  52. derivkit/likelihoods/__init__.py +1 -0
  53. derivkit/likelihoods/gaussian.py +136 -0
  54. derivkit/likelihoods/poisson.py +176 -0
  55. derivkit/utils/__init__.py +13 -0
  56. derivkit/utils/concurrency.py +213 -0
  57. derivkit/utils/extrapolation.py +254 -0
  58. derivkit/utils/linalg.py +513 -0
  59. derivkit/utils/logger.py +26 -0
  60. derivkit/utils/numerics.py +262 -0
  61. derivkit/utils/sandbox.py +74 -0
  62. derivkit/utils/types.py +15 -0
  63. derivkit/utils/validate.py +811 -0
  64. derivkit-1.0.0.dist-info/METADATA +50 -0
  65. derivkit-1.0.0.dist-info/RECORD +68 -0
  66. derivkit-1.0.0.dist-info/WHEEL +5 -0
  67. derivkit-1.0.0.dist-info/licenses/LICENSE +21 -0
  68. derivkit-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,296 @@
1
+ """Construct third-derivative tensors ("hyper-Hessians") for scalar- or tensor-valued functions."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable, Sequence
6
+ from functools import partial
7
+ from itertools import permutations
8
+ from typing import Any
9
+
10
+ import numpy as np
11
+ from numpy.typing import ArrayLike, NDArray
12
+
13
+ from derivkit.calculus.calculus_core import (
14
+ component_scalar_eval,
15
+ dispatch_tensor_output,
16
+ )
17
+ from derivkit.derivative_kit import DerivativeKit
18
+ from derivkit.utils.concurrency import (
19
+ parallel_execute,
20
+ resolve_inner_from_outer,
21
+ )
22
+ from derivkit.utils.sandbox import get_partial_function
23
+ from derivkit.utils.validate import ensure_finite
24
+
25
+ __all__ = [
26
+ "build_hyper_hessian",
27
+ ]
28
+
29
+
30
+ def build_hyper_hessian(
31
+ function: Callable[[ArrayLike], float | np.ndarray],
32
+ theta0: NDArray[np.float64] | Sequence[float],
33
+ *,
34
+ method: str | None = None,
35
+ n_workers: int = 1,
36
+ **dk_kwargs: Any,
37
+ ) -> NDArray[np.float64]:
38
+ """Returns the third-derivative tensor ("hyper-Hessian") of a function.
39
+
40
+ This function computes all third-order partial derivatives of a scalar- or
41
+ vector-valued function with respect to its parameters, evaluated at a single
42
+ point in parameter space. The resulting tensor generalizes the Hessian to
43
+ third order and is useful for higher-order Taylor expansions, non-Gaussian
44
+ approximations, and sensitivity analyses beyond quadratic order.
45
+
46
+ Args:
47
+ function: Function to differentiate.
48
+ theta0: 1D Parameter vector where the derivatives are evaluated.
49
+ method: Derivative method name or alias. If ``None``,
50
+ the :class:`derivkit.DerivativeKit` default is used.
51
+ n_workers: Outer parallelism across output components (tensor outputs only).
52
+ **dk_kwargs: Extra keyword args forwarded to :meth:`derivkit.DerivativeKit.differentiate`.
53
+ You may pass ``inner_workers=<int>`` here to override inner parallelism.
54
+
55
+ Returns:
56
+ Third-derivative tensor. For scalar outputs, the result has shape
57
+ ``(p, p, p)``, where ``p`` is the number of parameters. For tensor-valued
58
+ outputs with shape ``out_shape``, the result has shape
59
+ ``(*out_shape, p, p, p)``.
60
+
61
+ Raises:
62
+ ValueError: If ``theta0`` is empty.
63
+ FloatingPointError: If non-finite values are encountered.
64
+ """
65
+ theta = np.asarray(theta0, dtype=np.float64).reshape(-1)
66
+ if theta.size == 0:
67
+ raise ValueError("theta0 must be a non-empty 1D array.")
68
+
69
+ y0 = np.asarray(function(theta))
70
+ ensure_finite(y0, msg="Non-finite values in model output at theta0.")
71
+
72
+ inner_override = dk_kwargs.pop("inner_workers", None)
73
+ outer_workers = int(n_workers) if n_workers is not None else 1
74
+ inner_workers = (
75
+ int(inner_override)
76
+ if inner_override is not None
77
+ else resolve_inner_from_outer(outer_workers)
78
+ )
79
+
80
+ if y0.ndim == 0:
81
+ out = _build_hyper_hessian_scalar(
82
+ function=function,
83
+ theta=theta,
84
+ method=method,
85
+ inner_workers=inner_workers,
86
+ outer_workers=outer_workers,
87
+ **dk_kwargs,
88
+ )
89
+
90
+ ensure_finite(out, msg="Non-finite values encountered in hyper-Hessian.")
91
+ return out
92
+
93
+ # Tensor output: compute per-component scalar hyper-Hessian and reshape it back.
94
+ return dispatch_tensor_output(
95
+ function=function,
96
+ theta=theta,
97
+ method=method,
98
+ outer_workers=outer_workers,
99
+ inner_workers=inner_workers,
100
+ dk_kwargs=dk_kwargs,
101
+ build_component=_compute_component_hyper_hessian,
102
+ )
103
+
104
+
105
+ def _build_hyper_hessian_scalar(
106
+ function: Callable[[ArrayLike], float | np.ndarray],
107
+ theta: NDArray[np.float64],
108
+ method: str | None,
109
+ inner_workers: int | None,
110
+ outer_workers: int,
111
+ **dk_kwargs: Any,
112
+ ) -> NDArray[np.float64]:
113
+ """Returns a hyper-Hessian for a scalar-valued function.
114
+
115
+ Args:
116
+ function: Scalar-valued function to differentiate.
117
+ theta: 1D parameter vector where the derivatives are evaluated..
118
+ method: Derivative method name or alias. If ``None``,
119
+ the :class:`derivkit.DerivativeKit` default is used.
120
+ inner_workers: Number of inner workers for :class:`derivkit.DerivativeKit` calls.
121
+ outer_workers: Number of outer workers for parallelism over entries.
122
+ **dk_kwargs: Extra keyword args forwarded to :meth:`derivkit.DerivativeKit.differentiate`.
123
+
124
+ Returns:
125
+ Hyper-Hessian array with shape ``(p, p, p)`` with ``p`` the number of parameters.
126
+
127
+ Raises:
128
+ TypeError: If ``function`` does not return a scalar.
129
+ """
130
+ probe = np.asarray(function(theta), dtype=np.float64)
131
+ if probe.ndim != 0:
132
+ raise TypeError(
133
+ "Scalar hyper-Hessian path expects a scalar-valued function; "
134
+ f"got output with shape {probe.shape}."
135
+ )
136
+
137
+ p = int(theta.size)
138
+ iw = int(inner_workers or 1)
139
+
140
+ # Compute only unique entries i<=j<=k, then symmetrize.
141
+ triplets: list[tuple[int, int, int]] = [
142
+ (i, j, k) for i in range(p) for j in range(i, p) for k in range(j, p)
143
+ ]
144
+
145
+ def entry_worker(i: int, j: int, k: int) -> float:
146
+ """Worker to compute one hyper-Hessian entry.
147
+
148
+ Args:
149
+ i: First parameter index.
150
+ j: Second parameter index.
151
+ k: Third parameter index.
152
+
153
+ Returns:
154
+ Value of the third order derivative of the function at theta0.
155
+ """
156
+ return _third_derivative_entry(
157
+ function=function,
158
+ theta0=theta,
159
+ i=i,
160
+ j=j,
161
+ k=k,
162
+ method=method,
163
+ n_workers=iw,
164
+ dk_kwargs=dk_kwargs,
165
+ )
166
+
167
+ vals = parallel_execute(
168
+ entry_worker,
169
+ arg_tuples=triplets,
170
+ outer_workers=outer_workers,
171
+ inner_workers=iw,
172
+ )
173
+
174
+ hess = np.zeros((p, p, p), dtype=np.float64)
175
+ for (i, j, k), v in zip(triplets, vals, strict=True):
176
+ v = float(np.asarray(v).item())
177
+ for a, b, c in set(permutations((i, j, k), 3)):
178
+ hess[a, b, c] = v
179
+
180
+ ensure_finite(hess, msg="Non-finite values encountered in hyper-Hessian.")
181
+ return hess
182
+
183
+
184
+ def _third_derivative_entry(
185
+ *,
186
+ function: Callable[[ArrayLike], float | np.ndarray],
187
+ theta0: NDArray[np.float64],
188
+ i: int,
189
+ j: int,
190
+ k: int,
191
+ method: str | None,
192
+ n_workers: int,
193
+ dk_kwargs: dict[str, Any],
194
+ ) -> float:
195
+ """Computes the third order derivative of ``function`` at ``theta0`` with respect to parameters ``i``, ``j``, ``k``.
196
+
197
+ Args:
198
+ function: Scalar-valued function to differentiate.
199
+ theta0: 1D parameter vector at which the derivative is evaluated.
200
+ i: First parameter index.
201
+ j: Second parameter index.
202
+ k: Third parameter index.
203
+ method: Derivative method name or alias. If ``None``,
204
+ the :class:`derivkit.DerivativeKit` default is used.
205
+ n_workers: Number of workers for :class:`derivkit.DerivativeKit` calls.
206
+ dk_kwargs: Extra keyword args forwarded to :meth:`derivkit.DerivativeKit.differentiate`.
207
+
208
+ Returns:
209
+ Value of the third order derivative of the function at ``theta0``.
210
+ """
211
+ i, j, k = int(i), int(j), int(k)
212
+ ii, jj, kk = sorted((i, j, k))
213
+
214
+ if ii == jj == kk:
215
+ f1 = get_partial_function(function, ii, theta0)
216
+ kit = DerivativeKit(f1, float(theta0[ii]))
217
+ val = kit.differentiate(order=3, method=method, n_workers=n_workers, **dk_kwargs)
218
+ return float(np.asarray(val).item())
219
+
220
+ def g_func(t: float) -> float:
221
+ """Function for derivative with respect to parameter kk.
222
+
223
+ Args:
224
+ t: Value of parameter kk.
225
+
226
+ Returns:
227
+ Second derivative with respect to ii and jj at theta0 with kk=t.
228
+
229
+ """
230
+ th = theta0.copy()
231
+ th[kk] = float(t)
232
+
233
+ if ii == jj:
234
+ f1 = get_partial_function(function, ii, th)
235
+ kit2 = DerivativeKit(f1, float(th[ii]))
236
+ v2 = kit2.differentiate(order=2, method=method, n_workers=n_workers, **dk_kwargs)
237
+ return float(np.asarray(v2).item())
238
+
239
+ # mixed second derivative via nested 1D partials
240
+ def h_func(yj: float) -> float:
241
+ """Function for derivative with respect to parameter jj.
242
+
243
+ Args:
244
+ yj: Value of parameter jj.
245
+
246
+ Returns:
247
+ First derivative with respect to ii at theta0 with jj=yj and kk fixed.
248
+ """
249
+ th2 = th.copy()
250
+ th2[jj] = float(yj)
251
+ f_ii = get_partial_function(function, ii, th2)
252
+ kit1 = DerivativeKit(f_ii, float(th2[ii]))
253
+ v1 = kit1.differentiate(order=1, method=method, n_workers=n_workers, **dk_kwargs)
254
+ return float(np.asarray(v1).item())
255
+
256
+ kitm = DerivativeKit(h_func, float(th[jj]))
257
+ vm = kitm.differentiate(order=1, method=method, n_workers=n_workers, **dk_kwargs)
258
+ return float(np.asarray(vm).item())
259
+
260
+ kit3 = DerivativeKit(g_func, float(theta0[kk]))
261
+ v3 = kit3.differentiate(order=1, method=method, n_workers=n_workers, **dk_kwargs)
262
+ return float(np.asarray(v3).item())
263
+
264
+
265
+ def _compute_component_hyper_hessian(
266
+ idx: int,
267
+ theta: NDArray[np.float64],
268
+ method: str | None,
269
+ inner_workers: int | None,
270
+ dk_kwargs: dict[str, Any],
271
+ function: Callable[[ArrayLike], float | np.ndarray],
272
+ ) -> NDArray[np.float64]:
273
+ """Computes the hyper-Hessian for one output component of a tensor-valued function.
274
+
275
+ Args:
276
+ idx: Index of the output component to differentiate.
277
+ theta: Parameter vector where the derivatives are evaluated.
278
+ method: Derivative method name or alias. If ``None``,
279
+ the :class:`derivkit.DerivativeKit` default is used.
280
+ inner_workers: Number of inner workers for :class:`derivkit.DerivativeKit` calls.
281
+ dk_kwargs: Extra keyword args forwarded to :meth:`derivkit.DerivativeKit.differentiate`.
282
+ function: Original tensor-valued function.
283
+
284
+ Returns:
285
+ Hyper-Hessian array with shape ``(p, p, p)`` with ``p`` the number of parameters.
286
+ """
287
+ g = partial(component_scalar_eval, function=function, idx=int(idx))
288
+ # Use scalar builder with outer_workers=1 (parallel computation is already over components outside)
289
+ return _build_hyper_hessian_scalar(
290
+ g,
291
+ theta,
292
+ method=method,
293
+ inner_workers=inner_workers,
294
+ outer_workers=1,
295
+ **dk_kwargs,
296
+ )
@@ -0,0 +1,156 @@
1
+ """Contains functions used to construct the Jacobian matrix."""
2
+
3
+ from collections.abc import Callable
4
+ from functools import partial
5
+ from typing import Any
6
+
7
+ import numpy as np
8
+ from numpy.typing import ArrayLike, NDArray
9
+
10
+ from derivkit.derivative_kit import DerivativeKit
11
+ from derivkit.utils.concurrency import (
12
+ parallel_execute,
13
+ resolve_inner_from_outer,
14
+ )
15
+ from derivkit.utils.sandbox import get_partial_function
16
+
17
+
18
+ def build_jacobian(
19
+ function: Callable[[ArrayLike], ArrayLike | float],
20
+ theta0: ArrayLike,
21
+ method: str | None = None,
22
+ n_workers: int | None = 1,
23
+ **dk_kwargs: Any,
24
+ ) -> NDArray[np.floating]:
25
+ """Computes the Jacobian of a vector-valued function.
26
+
27
+ Each column in the Jacobian is the derivative with respect to one parameter.
28
+
29
+ Args:
30
+ function: The vector-valued function to be differentiated.
31
+ It should accept a list or array of parameter values as input and
32
+ return an array of observable values.
33
+ theta0: The parameter vector at which the jacobian is evaluated.
34
+ method: Method name or alias (e.g., ``"adaptive"``, ``"finite"``).
35
+ If ``None``, the :class:`derivkit.derivative_kit.DerivativeKit`
36
+ default (``"adaptive"``) is used.
37
+ n_workers: Number of workers used to parallelize across
38
+ parameters. If None or 1, no parallelization is used.
39
+ If greater than 1, this many threads will be used to compute
40
+ derivatives with respect to different parameters in parallel.
41
+ **dk_kwargs: Additional keyword arguments passed to
42
+ :meth:`derivkit.derivative_kit.DerivativeKit.differentiate`.
43
+
44
+ Returns:
45
+ A 2D array representing the jacobian. Each column corresponds to
46
+ the derivative with respect to one parameter.
47
+
48
+ Raises:
49
+ FloatingPointError: If non-finite values are encountered.
50
+ ValueError: If ``theta0`` is an empty array.
51
+ TypeError: If ``function`` does not return a vector value.
52
+ """
53
+ # Validate inputs and evaluate baseline output
54
+ theta = np.asarray(theta0, dtype=float).ravel()
55
+ if theta.size == 0:
56
+ raise ValueError("theta0 must be a non-empty 1D array.")
57
+
58
+ y0 = np.asarray(function(theta), dtype=float)
59
+ if y0.ndim != 1:
60
+ raise TypeError(
61
+ f"build_jacobian expects f: R^n -> R^m with 1-D vector output; got shape {y0.shape}"
62
+ )
63
+ if not np.isfinite(y0).all():
64
+ raise FloatingPointError("Non-finite values in model output at theta0.")
65
+
66
+ m = int(y0.size)
67
+ n = int(theta.size)
68
+
69
+ # Resolve parallelism policy
70
+ try:
71
+ outer_workers = max(1, int(n_workers or 1))
72
+ except (TypeError, ValueError):
73
+ outer_workers = 1
74
+ inner_workers = resolve_inner_from_outer(outer_workers)
75
+
76
+ # Prepare worker
77
+ worker = partial(
78
+ _column_derivative,
79
+ function=function,
80
+ theta0=theta,
81
+ method=method,
82
+ inner_workers=inner_workers,
83
+ expected_m=m,
84
+ **dk_kwargs,
85
+ )
86
+
87
+ # Parallelize across parameters
88
+ cols = parallel_execute(
89
+ worker,
90
+ arg_tuples=[(j,) for j in range(n)],
91
+ outer_workers=outer_workers,
92
+ inner_workers=inner_workers, # passed for context; we also pass explicitly to worker
93
+ )
94
+
95
+ # Stack columns → (m, n)
96
+ jac = np.column_stack([np.asarray(c, dtype=float).reshape(m) for c in cols])
97
+ return jac
98
+
99
+
100
+ def _column_derivative(
101
+ j: int,
102
+ function: Callable[[ArrayLike], ArrayLike | float],
103
+ theta0: ArrayLike,
104
+ method: str | None,
105
+ inner_workers: int | None,
106
+ expected_m: int,
107
+ **dk_kwargs: Any,
108
+ ) -> NDArray[np.floating]:
109
+ """Derivative of function with respect to parameter j.
110
+
111
+ Args:
112
+ j: Index of the parameter to differentiate with respect to.
113
+ function: The vector-valued function to be differentiated.
114
+ theta0: The parameter vector at which the jacobian is evaluated.
115
+ method: Method name or alias (e.g., ``"adaptive"``, ``"finite"``).
116
+ If ``None``, the :class:`derivkit.derivative_kit.DerivativeKit`
117
+ default (``"adaptive"``) is used.
118
+ inner_workers: Number of workers used by
119
+ :meth:`derivkit.derivative_kit.DerivativeKit.differentiate`.
120
+ **dk_kwargs: Additional keyword arguments passed to
121
+ :meth:`derivkit.derivative_kit.DerivativeKit.differentiate`.
122
+ expected_m: Expected length of the derivative vector.
123
+
124
+ Returns:
125
+ A 1D array representing the derivative with respect to parameter j.
126
+
127
+ Raises:
128
+ TypeError: If the derivative does not have the expected length.
129
+ FloatingPointError: If non-finite values are encountered.
130
+ """
131
+ theta_x = np.asarray(theta0, dtype=float).ravel().copy()
132
+
133
+ # Single-variable view: f_j(y) where theta_x[j] = y, others fixed
134
+ f_j = get_partial_function(function, j, theta_x)
135
+
136
+ # Normalize method aliases for the new DK API
137
+ # (let DK handle validation; we only map common shorthands)
138
+ method_norm = None
139
+ if method is not None:
140
+ m = method.lower()
141
+ alias = {"auto": "adaptive", "fd": "finite"}
142
+ method_norm = alias.get(m, m)
143
+
144
+ # Differentiate via new unified API (passes method through)
145
+ kit = DerivativeKit(f_j, theta_x[j])
146
+ g = kit.differentiate(method=method_norm, order=1, n_workers=inner_workers, **dk_kwargs)
147
+
148
+ g = np.atleast_1d(np.asarray(g, dtype=float)).reshape(-1)
149
+ if g.size != expected_m:
150
+ raise TypeError(
151
+ f"Expected derivative of length {expected_m} but got {g.size} for parameter index {j}."
152
+ )
153
+ if not np.isfinite(g).all():
154
+ raise FloatingPointError(f"Non-finite derivative for parameter index {j}.")
155
+
156
+ return g
@@ -0,0 +1,128 @@
1
+ """Provides the CalculusKit class.
2
+
3
+ A wrapper around the calculus helpers that exposes the gradient, Jacobian, and Hessian functions.
4
+
5
+ Typical usage examples:
6
+
7
+ >>> import numpy as np
8
+ >>> from derivkit.calculus_kit import CalculusKit
9
+ >>>
10
+ >>> def sin_function(x):
11
+ ... # scalar-valued function: f(x) = sin(x[0])
12
+ ... return np.sin(x[0])
13
+ >>>
14
+ >>> def identity_function(x):
15
+ ... # vector-valued function: f(x) = x
16
+ ... return np.asarray(x, dtype=float)
17
+ >>>
18
+ >>> calc = CalculusKit(sin_function, x0=np.array([0.5]))
19
+ >>> grad = calc.gradient()
20
+ >>> hess = calc.hessian()
21
+ >>>
22
+ >>> jac = CalculusKit(identity_function, x0=np.array([1.0, 2.0])).jacobian()
23
+ """
24
+
25
+ from collections.abc import Callable
26
+ from typing import Sequence
27
+
28
+ import numpy as np
29
+ from numpy.typing import NDArray
30
+
31
+ from derivkit.calculus import (
32
+ build_gradient,
33
+ build_hessian,
34
+ build_hessian_diag,
35
+ build_hyper_hessian,
36
+ build_jacobian,
37
+ )
38
+
39
+
40
+ class CalculusKit:
41
+ """Provides access to gradient, Jacobian, and Hessian tensors."""
42
+
43
+ def __init__(
44
+ self,
45
+ function: Callable[[Sequence[float] | np.ndarray], float | NDArray[np.floating]],
46
+ x0: Sequence[float] | np.ndarray,
47
+ ):
48
+ """Initialises class with function and expansion point.
49
+
50
+ Args:
51
+ function: The function to be differentiated. Accepts a 1D
52
+ array-like. Must return either a scalar (for gradient/Hessian)
53
+ or a 1D array (for Jacobian).
54
+ x0: Point at which to evaluate derivatives (shape ``(p,)``) for
55
+ ``p`` input parameters.
56
+ """
57
+ self.function = function
58
+ self.x0 = np.asarray(x0, dtype=float)
59
+
60
+ def gradient(self, *args, **kwargs) -> NDArray[np.floating]:
61
+ """Returns the gradient of a scalar-valued function.
62
+
63
+ This is a wrapper around :func:`derivkit.calculus.build_gradient`,
64
+ with the ``function`` and ``theta0`` arguments fixed to the values
65
+ provided at initialization of :class:`CalculusKit`. Any additional
66
+ positional or keyword arguments are forwarded to
67
+ :func:`derivkit.calculus.build_gradient`.
68
+
69
+ Refer to the documentation of
70
+ :func:`derivkit.calculus.build_gradient` for available options.
71
+ """
72
+ return build_gradient(self.function, self.x0, *args, **kwargs)
73
+
74
+ def jacobian(self, *args, **kwargs) -> NDArray[np.floating]:
75
+ """Returns the Jacobian of a vector-valued function.
76
+
77
+ This is a wrapper around :func:`derivkit.calculus.build_jacobian`,
78
+ with the ``function`` and ``theta0`` arguments fixed to the values
79
+ provided at initialization of :class:`CalculusKit`. Any additional
80
+ positional or keyword arguments are forwarded to
81
+ :func:`derivkit.calculus.build_jacobian`.
82
+
83
+ Refer to the documentation of
84
+ :func:`derivkit.calculus.build_jacobian` for available options.
85
+ """
86
+ return build_jacobian(self.function, self.x0, *args, **kwargs)
87
+
88
+ def hessian(self, *args, **kwargs) -> NDArray[np.floating]:
89
+ """Returns the Hessian of a scalar-valued function.
90
+
91
+ This is a wrapper around :func:`derivkit.calculus.build_hessian`,
92
+ with the ``function`` and ``theta0`` arguments fixed to the values
93
+ provided at initialization of :class:`CalculusKit`. Any additional
94
+ positional or keyword arguments are forwarded to
95
+ :func:`derivkit.calculus.build_hessian`.
96
+
97
+ Refer to the documentation of
98
+ :func:`derivkit.calculus.build_hessian` for available options.
99
+ """
100
+ return build_hessian(self.function, self.x0, *args, **kwargs)
101
+
102
+ def hessian_diag(self, *args, **kwargs) -> NDArray[np.floating]:
103
+ """Returns the diagonal of the Hessian of a scalar-valued function.
104
+
105
+ This is a wrapper around :func:`derivkit.calculus.build_hessian_diag`,
106
+ with the ``function`` and ``theta0`` arguments fixed to the values
107
+ provided at initialization of :class:`CalculusKit`. Any additional
108
+ positional or keyword arguments are forwarded to
109
+ :func:`derivkit.calculus.build_hessian_diag`.
110
+
111
+ Refer to the documentation of
112
+ :func:`derivkit.calculus.build_hessian_diag` for available options.
113
+ """
114
+ return build_hessian_diag(self.function, self.x0, *args, **kwargs)
115
+
116
+ def hyper_hessian(self, *args, **kwargs) -> NDArray[np.floating]:
117
+ """Returns the third-derivative tensor of a function.
118
+
119
+ This is a wrapper around :func:`derivkit.calculus.build_hyper_hessian`,
120
+ with the ``function`` and ``theta0`` arguments fixed to the values
121
+ provided at initialization of :class:`CalculusKit`. Any additional
122
+ positional or keyword arguments are forwarded to
123
+ :func:`derivkit.calculus.build_hyper_hessian`.
124
+
125
+ Refer to the documentation of
126
+ :func:`derivkit.calculus.build_hyper_hessian` for available options.
127
+ """
128
+ return build_hyper_hessian(self.function, self.x0, *args, **kwargs)