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,
method: str = "central",
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.
With the ``"central"`` method, the Jacobian is computed as:
.. math::
J_{i,j} = \frac{R_i(p + \epsilon e_j) - R_i(p - \epsilon e_j)}{2\epsilon}
With the ``"forward"`` method, the Jacobian is computed as:
.. math::
J_{i,j} = \frac{R_i(p + \epsilon e_j) - R_i(p)}{\epsilon}
With the ``"backward"`` method, the Jacobian is computed as:
.. math::
J_{i,j} = \frac{R_i(p) - R_i(p - \epsilon e_j)}{\epsilon}
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.
method : str, optional (default="central")
The finite difference method to use for computing the numerical Jacobian.
Must be one of "central", "forward", or "backward".
epsilon : Real, optional (default=1e-8)
A small perturbation value used for finite difference approximation of the Jacobian.
Returns
-------
jacobian_func : Callable
A function that computes the Jacobian matrix for a given set of parameters.
The function takes a 1D array of parameters as input and returns
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.
- 1.0.0: Added support for forward, backward, and central finite difference methods for numerical Jacobian computation.
"""
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)
if not isinstance(method, str):
raise ValueError("Method must be a string.")
method = method.lower()
if method not in ["central", "forward", "backward"]:
raise ValueError("Method must be one of 'central', 'forward', or 'backward'.")
def jacobian_func(parameters: numpy.ndarray) -> numpy.ndarray:
parameters = numpy.asarray(parameters, dtype=numpy.float64)
n_parameters = len(parameters)
residual = residual_func(parameters)
if not isinstance(residual, numpy.ndarray):
raise ValueError("The residual function must return a numpy array.")
if residual.ndim != 1:
raise ValueError("The residual function must return a 1D array.")
n_residual = len(residual)
jacobian = numpy.zeros((n_residual, n_parameters), dtype=numpy.float64)
perturbation = numpy.zeros((n_parameters,), dtype=numpy.float64)
for index in range(n_parameters):
perturbation[index] = epsilon * max(1.0, abs(parameters[index]))
if method == "central":
jacobian[:, index] = (
residual_func(parameters + perturbation)
- residual_func(parameters - perturbation)
) / (2 * perturbation[index])
elif method == "forward":
jacobian[:, index] = (
residual_func(parameters + perturbation) - residual_func(parameters)
) / perturbation[index]
elif method == "backward":
jacobian[:, index] = (
residual_func(parameters) - residual_func(parameters - perturbation)
) / perturbation[index]
perturbation[index] = 0.0
return jacobian
return jacobian_func