Methods that use the Derivative
Newton devised a method to solve equations using values of the function and its first derivative. It is implemented by the NewtonRaphsonSolver class.
The NewtonRaphsonSolver class has two constructors. The first takes no arguments. The function to solve and its derivative must be set using the TargetFunction and DerivativeOfTargetFunction properties, which are both Func<T, TResult> delegates. The initial guess must be set using the InitialGuess property.
The second constructor takes these three properties as its three parameters.
// Use the parameterless constructor:
NewtonRaphsonSolver solver = new NewtonRaphsonSolver();
// Then set the target function and its derivative:
solver.TargetFunction = Math.Sin;
solver.DerivativeOfTargetFunction = Math.Cos;
// and the initial guess:
solver.InitialGuess = 4;
// Or specify all three directly in the constructor:
NewtonRaphsonSolver solver2 = new NewtonRaphsonSolver(Math.Sin, Math.Cos, 4);
The iteration can be controlled as outlined before by setting the AbsoluteTolerance, RelativeTolerance, ConvergenceCriterion, MaxIterations properties. The Solve method calculates the zero of the target function:
Console.WriteLine("Newton-Raphson Solver: sin(x) = 0");
Console.WriteLine(" Initial guess: 4");
double result = solver.Solve();
Console.WriteLine(" Result: {0}", solver.Status);
Console.WriteLine(" Solution: {0}", solver.Result);
Console.WriteLine(" Estimated error: {0}", solver.EstimatedError);
Console.WriteLine(" # iterations: {0}", solver.IterationsNeeded);
When you don't have the derivative...
You can still use this class if you don't have the derivative of the target function. In this case, use the static GetNumericalDifferentiator method of the FunctionMath class to create a Func<T, TResult> delegate that represents the numerical derivative of the target function.
It should be noted, however, that this technique requires significantly more function evaluations. If it is possible to find an interval that contains the root, the preferred method is to use one of the root bracketing solvers from the previous section.
A better alternative, if the function is simple enough to express in a single expression, is to use automatic differentiation to have the derivative calculated for you. To do this, you pass a lambda expression that evaluates the function to the GetDerivative(Expression<Func<Double, Double>>) method, which also creates a Func<T, TResult> delegate.
Both techniques are illustrated below:
solver.TargetFunction = Special.BesselJ0;
var derivative1 = FunctionMath.GetNumericalDifferentiator(Special.BesselJ0);
var derivative2 = SymbolicMath.GetDerivative(x => Special.BesselJ0(x));
solver.DerivativeOfTargetFunction = derivative2;
solver.InitialGuess = 5;
Console.WriteLine("Zero of Bessel function near x=5:");
result = solver.Solve();
Console.WriteLine(" Solution: {0}", solver.Result);
Console.WriteLine(" # iterations: {0}", solver.IterationsNeeded);
Bounds on the solution
In some cases, a straightforward application of Newton's algorithm can break down. For example, when an approximation ends up in a region where the function is not defined. In such cases, it can be beneficial to define bounds that the algorithm should stay within.
You can do this by setting the LowerBound and UpperBound properties. You can set only one of these properties or both.
The example below finds tries to solve the equation \( \sqrt(1-x)=1 \), starting with \( x = 1 \). Without an upper bound, the algorithm fails with a status of BadFunction. When the upper bound is set to 10, the algorithm finds the solution quickly:
solver.TargetFunction = Special.BesselJ0;
var derivative1 = FunctionMath.GetNumericalDifferentiator(Special.BesselJ0);
var derivative2 = SymbolicMath.GetDerivative(x => Special.BesselJ0(x));
solver.DerivativeOfTargetFunction = derivative2;
solver.InitialGuess = 5;
Console.WriteLine("Zero of Bessel function near x=5:");
result = solver.Solve();
Console.WriteLine(" Solution: {0}", solver.Result);
Console.WriteLine(" # iterations: {0}", solver.IterationsNeeded);
Reference
Newton, I. Methodus fluxionum et serierum infinitarum. 1664-1671.