the newtonraphson method aka the newton method is a rootfinding algorithm that approximates a root of a given realvalued function fx it is an iterative method given by the formula xn 1 xn fxn f xn with the precision of the approximation increasing as the number of iterations increase reference https en wikipedia orgwikinewton27smethod approximate the derivative of a function fx at a point x using the finite difference method import math tolerance 1e5 derivative calcderivativelambda x x2 2 math isclosederivative 4 abstoltolerance true derivative calcderivativemath sin 0 math isclosederivative 1 abstoltolerance true find a root of the given function f using the newtonraphson method param f a realvalued singlevariable function param x0 initial guess param maxiter maximum number of iterations param step step size of x used to approximate f x param maxerror maximum approximation error param logsteps bool denoting whether to log intermediate steps return a tuple containing the approximation the error and the intermediate steps if logsteps is false then an empty list is returned for the third element of the tuple raises zerodivisionerror the derivative approaches 0 raises arithmeticerror no solution exists or the solution isn t found before the iteration limit is reached import math tolerance 1e15 root newtonraphsonlambda x x2 5x 2 0 4 maxerrortolerance math iscloseroot 5 math sqrt17 2 abstoltolerance true root newtonraphsonlambda x math logx 1 2 maxerrortolerance math iscloseroot math e abstoltolerance true root newtonraphsonmath sin 1 maxerrortolerance math iscloseroot 0 abstoltolerance true newtonraphsonmath cos 0 traceback most recent call last zerodivisionerror no converging solution found zero derivative newtonraphsonlambda x x2 1 2 traceback most recent call last arithmeticerror no converging solution found iteration limit reached approximate the derivative of a function f x at a point x using the finite difference method import math tolerance 1e 5 derivative calc_derivative lambda x x 2 2 math isclose derivative 4 abs_tol tolerance true derivative calc_derivative math sin 0 math isclose derivative 1 abs_tol tolerance true find a root of the given function f using the newton raphson method param f a real valued single variable function param x0 initial guess param max_iter maximum number of iterations param step step size of x used to approximate f x param max_error maximum approximation error param log_steps bool denoting whether to log intermediate steps return a tuple containing the approximation the error and the intermediate steps if log_steps is false then an empty list is returned for the third element of the tuple raises zerodivisionerror the derivative approaches 0 raises arithmeticerror no solution exists or the solution isn t found before the iteration limit is reached import math tolerance 1e 15 root _ newton_raphson lambda x x 2 5 x 2 0 4 max_error tolerance math isclose root 5 math sqrt 17 2 abs_tol tolerance true root _ newton_raphson lambda x math log x 1 2 max_error tolerance math isclose root math e abs_tol tolerance true root _ newton_raphson math sin 1 max_error tolerance math isclose root 0 abs_tol tolerance true newton_raphson math cos 0 traceback most recent call last zerodivisionerror no converging solution found zero derivative newton_raphson lambda x x 2 1 2 traceback most recent call last arithmeticerror no converging solution found iteration limit reached set initial guess log intermediate steps calculate next estimate
from collections.abc import Callable

RealFunc = Callable[[float], float]


def calc_derivative(f: RealFunc, x: float, delta_x: float = 1e-3) -> float:
    
    return (f(x + delta_x / 2) - f(x - delta_x / 2)) / delta_x


def newton_raphson(
    f: RealFunc,
    x0: float = 0,
    max_iter: int = 100,
    step: float = 1e-6,
    max_error: float = 1e-6,
    log_steps: bool = False,
) -> tuple[float, float, list[float]]:
    

    def f_derivative(x: float) -> float:
        return calc_derivative(f, x, step)

    a = x0  
    steps = []
    for _ in range(max_iter):
        if log_steps:  
            steps.append(a)

        error = abs(f(a))
        if error < max_error:
            return a, error, steps

        if f_derivative(a) == 0:
            raise ZeroDivisionError("No converging solution found, zero derivative")
        a -= f(a) / f_derivative(a)  
    raise ArithmeticError("No converging solution found, iteration limit reached")


if __name__ == "__main__":
    import doctest
    from math import exp, tanh

    doctest.testmod()

    def func(x: float) -> float:
        return tanh(x) ** 2 - exp(3 * x)

    solution, err, steps = newton_raphson(
        func, x0=10, max_iter=100, step=1e-6, log_steps=True
    )
    print(f"{solution=}, {err=}")
    print("\n".join(str(x) for x in steps))