Source code for pysolvegn.derivation

"""
pysolve-gn - Robust Gauss-Newton Least Squares Solver.
Copyright (C) 2026 Artezaru, artezaru.github@proton.me

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
"""

from typing import Callable
from numbers import Real

import numpy


[docs] def build_numerical_jacobian( residual_func: Callable, epsilon: Real = 1e-8, ) -> Callable: r""" Build a Jacobian function for the Gauss-Newton optimization by computing the numerical derivatives of the residual function with respect to the parameters using finite differences. Parameters ---------- residual_func : Callable A function that computes the residuals for a given set of parameters. The function should take a 1D array of parameters as input and return a 1D array of residuals. epsilon : Real, optional A small perturbation value used for finite difference approximation of the Jacobian. Default is 1e-8. Returns ------- jacobian_func : Callable A function that computes the Jacobian matrix for a given set of parameters. The function should take a 1D array of parameters as input and return a 2D array representing the Jacobian matrix. Version ------- - 0.0.1: Initial version. - 0.0.2: Renamed from build_jacobian to build_numerical_jacobian for clarity. """ if not callable(residual_func): raise ValueError("The residual function must be callable.") if not isinstance(epsilon, Real): raise ValueError("Epsilon must be a real number.") if epsilon <= 0: raise ValueError("Epsilon must be a positive number.") epsilon = float(epsilon) def jacobian_func(params: numpy.ndarray) -> numpy.ndarray: params = numpy.asarray(params, dtype=numpy.float64) n_params = len(params) residuals = residual_func(params) n_residuals = len(residuals) jacobian = numpy.zeros((n_residuals, n_params), dtype=numpy.float64) for i in range(n_params): perturbation = numpy.zeros_like(params) perturbation[i] = epsilon jacobian[:, i] = ( residual_func(params + perturbation) - residuals ) / epsilon return jacobian return jacobian_func