defmodule Fluxion.NewtonSolver do @moduledoc """ A nonlinear equation solver which uses Newton's Method to find the zeros of an arbitrary function. """ @doc """ Find a zero of the provided function starting from an initial guess. # Example iex> defmodule XSq do ...> @behaviour Fluxion.Function ...> def f(x), do: x*x - 4 ...> def dfdx(x), do: 2*x ...> end iex> computed_zero = Fluxion.NewtonSolver.find_zero(XSq, 10) iex> Fluxion.Math.is_equal(computed_zero, 2) :true iex> computed_zero = Fluxion.NewtonSolver.find_zero(XSq, -0.1) iex> IO.puts computed_zero iex> Fluxion.Math.is_equal(computed_zero, -2) :true """ @spec find_zero(Fluxion.Function.t(), number(), number()) :: number def find_zero(function, guess, max_iterations \\ 100) do iterate(function, guess, 0, max_iterations) end # rase an error if the solution cannot be found within the max iteration # count defp iterate(function, _current_guess, max_iterations, max_iterations) do raise "cannot find the zero for #{Kernel.inspect(function)} in #{ max_iterations } iterations" end # compute the next guess for the zero # return the guess if it's close enough to zero, otherwise iterate again defp iterate(function, current_guess, iteration, max_iterations) do next_guess = current_guess - function.f(current_guess) / function.dfdx(current_guess) if Fluxion.Math.is_equal(function.f(next_guess), 0) do next_guess else iterate(function, next_guess, iteration + 1, max_iterations) end end end