Source code for nlsq.interfaces.jacobian_protocol

"""Protocol definition for Jacobian computation strategies.

This module defines the JacobianProtocol that Jacobian computation
strategies should implement, enabling different approaches (autodiff,
finite differences, analytical, sparse).
"""

from collections.abc import Callable
from typing import Any, Protocol, runtime_checkable

import numpy as np


[docs] @runtime_checkable class JacobianProtocol(Protocol): """Protocol for Jacobian computation strategies. This protocol defines the interface for computing Jacobians of residual functions, allowing different strategies to be swapped. Methods ------- compute(fun, x, args) Compute the Jacobian matrix at point x. """
[docs] def compute( self, fun: Callable[..., np.ndarray], x: np.ndarray, args: tuple[Any, ...] = (), ) -> np.ndarray: """Compute Jacobian matrix. Parameters ---------- fun : Callable Residual function ``f(x, *args) -> residuals``. x : np.ndarray Point at which to evaluate Jacobian. args : tuple Additional arguments to pass to fun. Returns ------- np.ndarray Jacobian matrix of shape (n_residuals, n_params). """ ...
[docs] @runtime_checkable class SparseJacobianProtocol(Protocol): """Protocol for sparse Jacobian computation. Extended protocol for Jacobians that may have sparse structure, returning sparse matrices when beneficial. """
[docs] def compute( self, fun: Callable[..., np.ndarray], x: np.ndarray, args: tuple[Any, ...] = (), ) -> np.ndarray: """Compute Jacobian matrix (possibly sparse).""" ...
[docs] def compute_sparse( self, fun: Callable[..., np.ndarray], x: np.ndarray, args: tuple[Any, ...] = (), sparsity_threshold: float = 0.5, ) -> Any: """Compute sparse Jacobian if beneficial. Parameters ---------- fun : Callable Residual function. x : np.ndarray Point at which to evaluate. args : tuple Additional arguments. sparsity_threshold : float Fraction of zeros above which to use sparse format. Returns ------- np.ndarray or scipy.sparse matrix Jacobian in appropriate format. """ ...
@property def sparsity_pattern(self) -> np.ndarray | None: """Known sparsity pattern, if any.""" ...
[docs] class AutodiffJacobian: """Jacobian computation using JAX automatic differentiation. This is the default Jacobian computation strategy for NLSQ. Parameters ---------- use_forward_mode : bool, default=False Use forward-mode AD (vmap of jvp) instead of reverse-mode. Forward mode is faster when n_params << n_residuals. """ __slots__ = ("_use_forward_mode",)
[docs] def __init__(self, use_forward_mode: bool = False) -> None: self._use_forward_mode = use_forward_mode
[docs] def compute( self, fun: Callable[..., np.ndarray], x: np.ndarray, args: tuple[Any, ...] = (), ) -> np.ndarray: """Compute Jacobian using JAX autodiff.""" import jax import jax.numpy as jnp x_jax = jnp.asarray(x) def fun_x(params: jnp.ndarray): return fun(params, *args) # Forward mode (jacfwd) is efficient when n_params << n_residuals. # Reverse mode (jacrev) is efficient when n_params >> n_residuals. jac_fn = jax.jacfwd if self._use_forward_mode else jax.jacrev jacobian = jac_fn(fun_x)(x_jax) return np.asarray(jacobian)