using CalculusWithJulia
using Plots
plotly()
using SymPy
using Roots
30 Newton’s method
This section uses these add-on packages:
The Babylonian method is an algorithm to find an approximate value for \(\sqrt{k}\). It was described by the first-century Greek mathematician Hero of Alexandria.
The method starts with some initial guess, called \(x_0\). This is usually some nearby value to the answer. The method then applies a formula to produce an improved guess. This is repeated until the improved guess is accurate enough or it is clear the algorithm fails to work.
For the Babylonian method, the next guess, \(x_{i+1}\), is derived from the current guess, \(x_i\). In mathematical notation, this is the updating step:
\[ x_{i+1} = \frac{1}{2}(x_i + \frac{k}{x_i}) \]
We use this algorithm to approximate the square root of \(2\), a value known to the Babylonians.
Start with \(x\), then form \(x/2 + 1/x\), from this again form \(x/2 + 1/x\), repeat.
We represent this step using a function
babylon(x) = x/2 + 1/x
babylon (generic function with 1 method)
Let’s look starting with \(x = 2\) as a rational number:
= babylon(2//1)
x₁ ^2.0 x₁, x₁
(3//2, 2.25)
Our estimate improved from something which squared to \(4\) down to something which squares to \(2.25.\) A big improvement, but there is still more to come. Had we done one more step:
= (babylon ∘ babylon)(2//1)
x₂ ^2.0 x₂, x₂
(17//12, 2.0069444444444446)
We now see accuracy until the third decimal point.
= (babylon ∘ babylon ∘ babylon)(2//1)
x₃ ^2.0 x₃, x₃
(577//408, 2.000006007304883)
This is now accurate to the sixth decimal point. That is about as far as we, or the Bablyonians, would want to go by hand. Using rational numbers quickly grows out of hand. The next step shows the explosion.
reduce((x,step) -> babylon(x), 1:4, init=2//1)
665857//470832
(In the above, we used reduce
to repeat a function call \(4\) times, as an alternative to the composition operation. In this section we show a few styles to do this repetition before introducing a packaged function.)
However, with the advent of floating point numbers, the method stays quite manageable:
= reduce((x, step) -> babylon(x), 1:6, init=2.0)
xₙ ^2 xₙ, xₙ
(1.414213562373095, 1.9999999999999996)
We can see that the algorithm - to the precision offered by floating point numbers - has resulted in an answer 1.414213562373095
. This answer is an approximation to the actual answer. Approximation is necessary, as \(\sqrt{2}\) is an irrational number and so can never be exactly represented in floating point. That being said, we can see that the value of \(f(x)\) is accurate to the last decimal place, so our approximation is very close and is achieved in a few steps.
30.1 Newton’s generalization
Let \(f(x) = x^3 - 2x -5\). The value of \(2\) is almost a zero, but not quite, as \(f(2) = -1\). We can check that there are no rational roots. Though there is a method to solve the cubic it may be difficult to compute and will not be as generally applicable as some algorithm like the Babylonian method to produce an approximate answer.
Is there some generalization to the Babylonian method?
We know that the tangent line is a good approximation to the function at the point. Looking at this graph gives a hint as to an algorithm:
The tangent line and the function nearly agree near \(2\). So much so, that the intersection point of the tangent line with the \(x\) axis nearly hides the actual zero of \(f(x)\) that is near \(2.1\).
That is, it seems that the intersection of the tangent line and the \(x\) axis should be an improved approximation for the zero of the function.
Let \(x_0\) be \(2\), and \(x_1\) be the intersection point of the tangent line at \((x_0, f(x_0))\) with the \(x\) axis. Then by the definition of the tangent line:
\[ f'(x_0) = \frac{\Delta y }{\Delta x} = \frac{f(x_0)}{x_0 - x_1}. \]
This can be solved for \(x_1\) to give \(x_1 = x_0 - f(x_0)/f'(x_0)\). In general, if we had \(x_i\) and used the intersection point of the tangent line to produce \(x_{i+1}\) we would have Newton’s method:
\[ x_{i+1} = x_i - \frac{f(x_i)}{f'(x_i)}. \]
Using automatic derivatives, as brought in with the CalculusWithJulia
package, we can implement this algorithm.
The algorithm above starts at \(2\) and then becomes:
f(x) = x^3 - 2x - 5
= 2.0
x0 = x0 - f(x0) / f'(x0) x1
2.1
We can see we are closer to a zero:
f(x0), f(x1)
(-1.0, 0.06100000000000083)
Trying again, we have
= x1 - f(x1)/ f'(x1)
x2 f(x2), f(x1) x2,
(2.094568121104185, 0.00018572317327247845, 0.06100000000000083)
And again:
= x2 - f(x2)/ f'(x2)
x3 f(x3), f(x2) x3,
(2.094551481698199, 1.7397612239733462e-9, 0.00018572317327247845)
= x3 - f(x3)/ f'(x3)
x4 f(x4), f(x3) x4,
(2.0945514815423265, -8.881784197001252e-16, 1.7397612239733462e-9)
We see now that \(f(x_4)\) is within machine tolerance of \(0\), so we call \(x_4\) an approximate zero of \(f(x)\).
On the computer, we know that actual convergence will likely never occur, but accuracy to a certain tolerance can often be achieved.
In the example above, we kept track of the previous values. This is unnecessary if only the answer is sought. In that case, the update step could use the same variable. Here we use reduce
:
= reduce((x, step) -> x - f(x)/f'(x), 1:4, init=2)
xₙ f(xₙ) xₙ,
(2.0945514815423265, -8.881784197001252e-16)
In practice, the algorithm is implemented not by repeating the update step a fixed number of times, rather by repeating the step until either we converge or it is clear we won’t converge. For good guesses and most functions, convergence happens quickly.
Newton looked at this same example in 1699 (B.T. Polyak, Newton’s method and its use in optimization, European Journal of Operational Research. 02/2007; 181(3):1086-1096.; and Deuflhard Newton Methods for Nonlinear Problems: Affine Invariance and Adaptive Algorithms) though his technique was slightly different as he did not use the derivative, per se, but rather an approximation based on the fact that his function was a polynomial.
We can read that he guessed the answer was 2 + p
, as there is a sign change between \(2\) and \(3\). Newton put this guess into the polynomial to get after simplification p^3 + 6p^2 + 10p - 1
. This has an approximate zero found by solving the linear part 10p-1 = 0
. Taking p = 0.1
he then can say the answer looks like 2 + p + q
and repeat to get q^3 + 6.3\cdot q^2 + 11.23 \cdot q + 0.061 = 0
. Again taking just the linear part estimates q = 0.005431...
. After two steps the estimate is 2.105431...
. This can be continued by expressing the answer as 2 + p + q + r
and then solving for an estimate for r
.
Raphson (1690) proposed a simplification avoiding the computation of new polynomials, hence the usual name of the Newton-Raphson method. Simpson introduced derivatives into the formulation and systems of equations.
Examples
Example: visualizing convergence
This graphic demonstrates the method and the rapid convergence:
This interactive graphic (built using JSXGraph) allows the adjustment of the point x0
, initially at \(0.85\). Five iterations of Newton’s method are illustrated. Different positions of x0
clearly converge, others will not.
Example: numeric not algebraic
For the function \(f(x) = \cos(x) - x\), we see that SymPy can not solve symbolically for a zero:
@syms x::real
solve(cos(x) - x, x)
LoadError: PyError ($(Expr(:escape, :(ccall(#= /Users/jverzani/.julia/packages/PyCall/1gn3u/src/pyfncall.jl:43 =# @pysym(:PyObject_Call), PyPtr, (PyPtr, PyPtr, PyPtr), o, pyargsptr, kw))))) <class 'NotImplementedError'>
NotImplementedError('multiple generators [x, cos(x)]\nNo algorithms are implemented to solve equation -x + cos(x)')
File "/Users/jverzani/.julia/conda/3/x86_64/lib/python3.10/site-packages/sympy/solvers/solvers.py", line 1145, in solve
solution = _solve(f[0], *symbols, **flags)
File "/Users/jverzani/.julia/conda/3/x86_64/lib/python3.10/site-packages/sympy/solvers/solvers.py", line 1693, in _solve
raise NotImplementedError('\n'.join([msg, not_impl_msg % f]))
PyError ($(Expr(:escape, :(ccall(#= /Users/jverzani/.julia/packages/PyCall/1gn3u/src/pyfncall.jl:43 =# @pysym(:PyObject_Call), PyPtr, (PyPtr, PyPtr, PyPtr), o, pyargsptr, kw))))) <class 'NotImplementedError'>
NotImplementedError('multiple generators [x, cos(x)]\nNo algorithms are implemented to solve equation -x + cos(x)')
File "/Users/jverzani/.julia/conda/3/x86_64/lib/python3.10/site-packages/sympy/solvers/solvers.py", line 1145, in solve
solution = _solve(f[0], *symbols, **flags)
File "/Users/jverzani/.julia/conda/3/x86_64/lib/python3.10/site-packages/sympy/solvers/solvers.py", line 1693, in _solve
raise NotImplementedError('\n'.join([msg, not_impl_msg % f]))
Stacktrace:
[1] pyerr_check
@ ~/.julia/packages/PyCall/1gn3u/src/exception.jl:75 [inlined]
[2] pyerr_check
@ ~/.julia/packages/PyCall/1gn3u/src/exception.jl:79 [inlined]
[3] _handle_error(msg::String)
@ PyCall ~/.julia/packages/PyCall/1gn3u/src/exception.jl:96
[4] macro expansion
@ ~/.julia/packages/PyCall/1gn3u/src/exception.jl:110 [inlined]
[5] #107
@ ~/.julia/packages/PyCall/1gn3u/src/pyfncall.jl:43 [inlined]
[6] disable_sigint
@ ./c.jl:167 [inlined]
[7] __pycall!
@ ~/.julia/packages/PyCall/1gn3u/src/pyfncall.jl:42 [inlined]
[8] _pycall!(ret::PyCall.PyObject, o::PyCall.PyObject, args::Tuple{PyCall.PyObject, PyCall.PyObject}, nargs::Int64, kw::Ptr{Nothing})
@ PyCall ~/.julia/packages/PyCall/1gn3u/src/pyfncall.jl:29
[9] _pycall!(ret::PyCall.PyObject, o::PyCall.PyObject, args::Tuple{PyCall.PyObject, PyCall.PyObject}, kwargs::@Kwargs{})
@ PyCall ~/.julia/packages/PyCall/1gn3u/src/pyfncall.jl:11
[10] (::PyCall.PyObject)(::PyCall.PyObject, ::Vararg{PyCall.PyObject}; kwargs::@Kwargs{})
@ PyCall ~/.julia/packages/PyCall/1gn3u/src/pyfncall.jl:86
[11] solve(x::Sym{PyCall.PyObject}, args::Sym{PyCall.PyObject}; kwargs::@Kwargs{})
@ SymPy ~/.julia/packages/SymPyCore/wmUfN/src/SymPy/gen_methods_sympy.jl:26
[12] top-level scope
@ In[22]:2
We can find a numeric solution, even though there is no closed-form answer. Here we try Newton’s method:
f(x) = cos(x) - x
= .5
x = x - f(x)/f'(x) # 0.7552224171056364
x = x - f(x)/f'(x) # 0.7391416661498792
x = x - f(x)/f'(x) # 0.7390851339208068
x = x - f(x)/f'(x) # 0.7390851332151607
x = x - f(x)/f'(x)
x f(x) x,
(0.7390851332151607, 0.0)
To machine tolerance the answer is a zero, even though the exact answer is irrational and all finite floating point values can be represented as rational numbers.
Example non-polynomial
The first example by Newton of applying the method to a non-polynomial function was solving an equation from astronomy: \(x - e \sin(x) = M\), where \(e\) is an eccentric anomaly and \(M\) a mean anomaly. Newton used polynomial approximations for the trigonometric functions, here we can solve directly.
Let \(e = 1/2\) and \(M = 3/4\). With \(f(x) = x - e\sin(x) - M\) then \(f'(x) = 1 - e cos(x)\). Starting at 1, Newton’s method for 3 steps becomes:
= 0.5, 0.75
ec, M f(x) = x - ec * sin(x) - M
fp(x) = 1 - ec * cos(x)
= 1
x = x - f(x) / fp(x)
x = x - f(x) / fp(x)
x = x - f(x) / fp(x)
x f(x) x,
(1.2194561665177588, 8.208542734422508e-10)
Example
Use Newton’s method to find the largest real solution to \(e^x = x^6\).
A plot shows us roughly where the value lies:
f(x) = exp(x)
g(x) = x^6
plot(f, 0, 25; label="f")
plot!(g; label="g")
Clearly by \(20\) the two paths diverge. We know exponentials eventually grow faster than powers, and this is seen in the graph.
To use Newton’s method to find the intersection point. Stop when the increment \(f(x)/f'(x)\) is smaller than 1e-4
. We need to turn the solution to an equation into a value where a function is \(0\). Just moving the terms to one side of the equals sign gives \(e^x - x^6 = 0\), or the \(x\) we seek is a solution to \(h(x)=0\) with \(h(x) = e^x - x^6\).
h(x) = exp(x) - x^6
= 20
x for step in 1:10
= h(x)/h'(x)
delta = x - delta
x @show step, x, delta
end
(step, x, delta) = (1, 19.096144519894025, 0.9038554801059746)
(step, x, delta) = (2, 18.279618508448504, 0.8165260114455216)
(step, x, delta) = (3, 17.615584160589165, 0.6640343478593392)
(step, x, delta) = (4, 17.186229687634423, 0.42935447295474183)
(step, x, delta) = (5, 17.02052242498673, 0.1657072626476933)
(step, x, delta) = (6, 16.999206949504874, 0.021315475481855334)
(step, x, delta) = (7, 16.998887423017564, 0.00031952648730976816)
(step, x, delta) = (8, 16.998887352296055, 7.072150775034742e-8)
(step, x, delta) = (9, 16.99888735229605, 2.3862112429152903e-15)
(step, x, delta) = (10, 16.99888735229605, -1.1931056214576512e-15)
So it takes \(8\) steps to get an increment that small and about 10
steps to get to full convergence.
Example division as multiplication
Newton-Raphson Division is a means to divide by multiplying.
Why would you want to do that? Well, even for computers division is harder (read slower) than multiplying. The trick is that \(p/q\) is simply \(p \cdot (1/q)\), so finding a means to compute a reciprocal by multiplying will reduce division to multiplication.
Well suppose we have \(q\), we could try to use Newton’s method to find \(1/q\), as it is a solution to \(f(x) = x - 1/q\). The Newton update step simplifies to:
\[ x - f(x) / f'(x) \quad\text{or}\quad x - (x - 1/q)/ 1 = 1/q \]
That doesn’t really help, as Newton’s method is just \(x_{i+1} = 1/q\). That is, it just jumps to the answer, the one we want to compute by some other means!
Trying again, we simplify the update step for a related function: \(f(x) = 1/x - q\) with \(f'(x) = -1/x^2\) and then one step of the process is:
\[ x_{i+1} = x_i - (1/x_i - q)/(-1/x_i^2) = -qx^2_i + 2x_i. \]
Now for \(q\) in the interval \([1/2, 1]\) we want to get a good initial guess. Here is a claim. We can use \(x_0=48/17 - 32/17 \cdot q\). Let’s check graphically that this is a reasonable initial approximation to \(1/q\):
plot(q -> 1/q, 1/2, 1, label="1/q")
plot!(q -> 1/17 * (48 - 32q), label="linear approximation")
It can be shown that we have for any \(q\) in \([1/2, 1]\) with initial guess \(x_0 = 48/17 - 32/17\cdot q\) that Newton’s method will converge to \(16\) digits in no more than this many steps:
\[ \log_2(\frac{53 + 1}{\log_2(17)}). \]
= log2((53 + 1)/log2(17))
a ceil(Integer, a)
4
That is \(4\) steps suffices.
For \(q = 0.80\), to find \(1/q\) using the above we have
= 0.80
q = (48/17) - (32/17)*q
x = -q*x*x + 2*x
x = -q*x*x + 2*x
x = -q*x*x + 2*x
x = -q*x*x + 2*x x
1.25
This method has basically \(18\) multiplication and addition operations for one division, so it naively would seem slower, but timing this shows the method is competitive with a regular division.
30.2 Wrapping in a function
In the previous examples, we saw fast convergence, guaranteed converge in \(4\) steps, and an example where \(8\) steps were needed to get the requested level of approximation. Newton’s method usually converges quickly, but may converge slowly, and may not converge at all. Automating the task to avoid repeatedly running the update step is a task best done by the computer.
The while
loop is a good way to repeat commands until some condition is met. With this, we present a simple function implementing Newton’s method, we iterate until the update step gets really small (the atol
) or the convergence takes more than \(50\) steps. (There are other, better choices that could be used to determine when the algorithm should stop, these are just easy to understand.)
function nm(f, fp, x0)
= 1e-14
atol = 0
ctr = Inf
delta while (abs(delta) > atol) && (ctr < 50)
= f(x0)/fp(x0)
delta = x0 - delta
x0 = ctr + 1
ctr end
< 50 ? x0 : NaN
ctr end
nm (generic function with 1 method)
Examples
- Find a zero of \(\sin(x)\) starting at \(x_0=3\):
nm(sin, cos, 3)
3.141592653589793
This is an approximation for \(\pi\), that historically found use, as the convergence is fast.
- Find a solution to \(x^5 = 5^x\) near \(2\):
Writing a function to handle this, we have:
k(x) = x^5 - 5^x
k (generic function with 1 method)
We could find the derivative by hand, but use the automatic one instead:
= nm(k, k', 2)
alpha k(alpha) alpha,
(1.764921914525776, 0.0)
30.2.1 Functions in the Roots package
Typing in the nm
function might be okay once, but would be tedious if it was needed each time. Besides, it isn’t as robust to different inputs as possible. The Roots
package provides a Newton
method for find_zero
.
To use a different method with find_zero
, the calling pattern is find_zero(f, x, M)
where f
represent the function(s), x
the initial point(s), and M
the method. Here we have:
find_zero((sin, cos), 3, Roots.Newton())
3.141592653589793
Or, if a derivative is not specified, one can be computed using automatic differentiation:
f(x) = sin(x)
find_zero((f, f'), 2, Roots.Newton())
3.141592653589793
The argument verbose=true
will force a print out of a message summarizing the convergence and showing each step.
f(x) = exp(x) - x^4
find_zero((f,f'), 8, Roots.Newton(); verbose=true)
Results of univariate zero finding:
* Converged to: 8.6131694564414
* Algorithm: Roots.Newton()
* iterations: 8
* function evaluations ≈ 16
* stopped as x_n ≈ x_{n-1} using atol=xatol, rtol=xrtol
Trace:
x₁ = 8, fx₁ = -1115.0420129582717
x₂ = 9.1951685161021075, fx₂ = 2700.5339924159998
x₃ = 8.7944708020788642, fx₃ = 615.76735783715776
x₄ = 8.6356413896038262, fx₄ = 67.416233795479457
x₅ = 8.6135576545997825, fx₅ = 1.1446528211590703
x₆ = 8.6131695743314562, fx₆ = 0.00034750863687804667
x₇ = 8.6131694564414101, fx₇ = 3.3651303965598345e-11
x₈ = 8.6131694564413994, fx₈ = 1.8189894035458565e-12
x₉ = 8.6131694564413994, fx₉ = 1.8189894035458565e-12
8.6131694564414
Example: intersection of two graphs
Find the intersection point between \(f(x) = \cos(x)\) and \(g(x) = 5x\) near \(0\).
We have Newton’s method to solve for zeros of \(f(x)\), i.e. when \(f(x) = 0\). Here we want to solve for \(x\) with \(f(x) = g(x)\). To do so, we make a new function \(h(x) = f(x) - g(x)\), that is \(0\) when \(f(x)\) equals \(g(x)\):
f(x) = cos(x)
g(x) = 5x
h(x) = f(x) - g(x)
= find_zero((h,h'), 0, Roots.Newton())
x0 h(x0), f(x0), g(x0) x0,
(0.19616428118784215, 0.0, 0.9808214059392107, 0.9808214059392107)
We redo the above using a parameter for the \(5\), as there are some options on how it would be done. We let f(x,p) = cos(x) - p*x
. Then we can use Roots.Newton
by also defining a derivative:
f(x,p) = cos(x) - p*x
fp(x,p) = -sin(x) - p
= find_zero((f,fp), pi/4, Roots.Newton(); p=5)
xn f(xn, 5) xn,
(0.19616428118784215, 0.0)
To use automatic differentiation is not straightforward, as we must hold the p
fixed. For this, we introduce a closure that fixes p
and differentiates in the x
variable (called u
below):
f(x,p) = cos(x) - p*x
fp(x,p) = (u -> f(u,p))'(x)
= find_zero((f,fp), pi/4, Roots.Newton(); p=5) xn
0.19616428118784215
Example: Finding \(c\) in Rolle’s Theorem
The function \(r(x) = \sqrt{1 - \cos(x^2)^2}\) has a zero at \(0\) and one at \(a\) near \(1.77\).
r(x) = sqrt(1 - cos(x^2)^2)
plot(r, 0, 1.77)
As \(f(x)\) is differentiable between \(0\) and \(a\), Rolle’s theorem says there will be value where the derivative is \(0\). Find that value.
This value will be a zero of the derivative. A graph shows it should be near \(1.2\), so we use that as a starting value to get the answer:
find_zero((r',r''), 1.2, Roots.Newton())
1.2533141373155003
30.3 Convergence rates
Newton’s method is famously known to have “quadratic convergence.” What does this mean? Let the error in the \(i\)th step be called \(e_i = x_i - \alpha\). Then Newton’s method satisfies a bound of the type:
\[ \lvert e_{i+1} \rvert \leq M_i \cdot e_i^2. \]
If \(M\) were just a constant and we suppose \(e_0 = 10^{-1}\) then \(e_1\) would be less than \(M 10^{-2}\) and \(e_2\) less than \(M^2 10^{-4}\), \(e_3\) less than \(M^3 10^{-8}\) and \(e_4\) less than \(M^4 10^{-16}\) which for \(M=1\) is basically the machine precision when values are near \(1\). That is for some problems, with a good initial guess it will take around \(4\) or so steps to converge.
To identify \(M\), let \(\alpha\) be the zero of \(f\) to be approximated. Assume
- The function \(f\) has at continuous second derivative in a neighborhood of \(\alpha\).
- The value \(f'(\alpha)\) is non-zero in the neighborhood of \(\alpha\).
Then this linearization holds at each \(x_i\) in the above neighborhood:
\[ f(x) = f(x_i) + f'(x_i) \cdot (x - x_i) + \frac{1}{2} f''(\xi) \cdot (x-x_i)^2. \]
The value \(\xi\) is from the mean value theorem and is between \(x\) and \(x_i\).
Dividing by \(f'(x_i)\) and setting \(x=\alpha\) (as \(f(\alpha)=0\)) leaves
\[ 0 = \frac{f(x_i)}{f'(x_i)} + (\alpha-x_i) + \frac{1}{2}\cdot \frac{f''(\xi)}{f'(x_i)} \cdot (\alpha-x_i)^2. \]
For this value, we have
\[ \begin{align*} x_{i+1} - \alpha &= \left(x_i - \frac{f(x_i)}{f'(x_i)}\right) - \alpha\\ &= \left(x_i - \alpha \right) - \frac{f(x_i)}{f'(x_i)}\\ &= (x_i - \alpha) + \left( (\alpha - x_i) + \frac{1}{2}\frac{f''(\xi) \cdot(\alpha - x_i)^2}{f'(x_i)} \right)\\ &= \frac{1}{2}\frac{f''(\xi)}{f'(x_i)} \cdot(x_i - \alpha)^2. \end{align*} \]
That is
\[ e_{i+1} = \frac{1}{2}\frac{f''(\xi)}{f'(x_i)} e_i^2. \]
This convergence to \(\alpha\) will be quadratic if:
- The initial guess \(x_0\) is not too far from \(\alpha\), so \(e_0\) is managed.
- The derivative at \(\alpha\) is not too close to \(0\), hence, by continuity \(f'(x_i)\) is not too close to \(0\). (As it appears in the denominator). That is, the function can’t be too flat, which should make sense, as then the tangent line is nearly parallel to the \(x\) axis and would intersect far away.
- The function \(f\) has a continuous second derivative at \(\alpha\).
- The second derivative is not too big (in absolute value) near \(\alpha\). A large second derivative means the function is very concave, which means it is “turning” a lot. In this case, the function turns away from the tangent line quickly, so the tangent line’s zero is not necessarily a good approximation to the actual zero, \(\alpha\).
The basic tradeoff: methods like Newton’s are faster than the bisection method in terms of function calls, but are not guaranteed to converge, as the bisection method is.
What can go wrong when one of these isn’t the case is illustrated next:
30.3.1 Poor initial step
30.3.2 The second derivative is too big
30.3.3 The tangent line at some xᵢ is flat
Example
Suppose \(\alpha\) is a simple zero for \(f(x)\). (The value \(\alpha\) is a zero of multiplicity \(k\) if \(f(x) = (x-\alpha)^kg(x)\) where \(g(\alpha)\) is not zero. A simple zero has multiplicity \(1\). If \(f'(\alpha) \neq 0\) and the second derivative exists, then a zero \(\alpha\) will be simple.) Around \(\alpha\), quadratic convergence should apply. However, consider the function \(g(x) = f(x)^k\) for some integer \(k \geq 2\). Then \(\alpha\) is still a zero, but the derivative of \(g\) at \(\alpha\) is zero, so the tangent line is basically flat. This will slow the convergence up. We can see that the update step \(g(x)/g'(x)\) becomes \((1/k) f(x)/f'(x)\), so an extra factor is introduced.
The calculation that produces the quadratic convergence now becomes:
\[ \begin{align*} x_{i+1} - \alpha &= (x_i - \alpha) - \frac{1}{k}(x_i-\alpha - \frac{f''(\xi)}{2f'(x_i)}(x_i-\alpha)^2) \\ &= \frac{k-1}{k} (x_i-\alpha) + \frac{f''(\xi)}{2kf'(x_i)}(x_i-\alpha)^2. \end{align*} \]
As \(k > 1\), the \((x_i - \alpha)\) term dominates, and we see the convergence is linear with \(\lvert e_{i+1}\rvert \approx (k-1)/k \lvert e_i\rvert\).
30.4 Questions
Question
Look at this graph with \(x_0\) marked with a point:
If one step of Newton’s method was used, what would be the value of \(x_1\)?
Question
Look at this graph of some increasing, concave up \(f(x)\) with initial point \(x_0\) marked. Let \(\alpha\) be the zero.
What can be said about \(x_1\)?
Look at this graph of some increasing, concave up \(f(x)\) with initial point \(x_0\) marked. Let \(\alpha\) be the zero.
What can be said about \(x_1\)?
Suppose \(f(x)\) is increasing and concave up. From the tangent line representation: \(f(x) = f(c) + f'(c)\cdot(x-c) + f''(\xi)/2 \cdot(x-c)^2\), explain why it must be that the graph of \(f(x)\) lies on or above the tangent line.
This question can be used to give a proof for the previous two questions, which can be answered by considering the graphs alone. Combined, they say that if a function is increasing and concave up and \(\alpha\) is a zero, then if \(x_0 < \alpha\) it will be \(x_1 > \alpha\), and for any \(x_i > \alpha\), \(\alpha \le x_{i+1} \le x_i\), so the sequence in Newton’s method is decreasing and bounded below; conditions for which it is guaranteed mathematically there will be convergence.
Question
Let \(f(x) = x^2 - 3^x\). This has derivative \(2x - 3^x \cdot \log(3)\). Starting with \(x_0=0\), what does Newton’s method converge on?
Question
Let \(f(x) = \exp(x) - x^4\). There are 3 zeros for this function. Which one does Newton’s method converge to when \(x_0=2\)?
Question
Let \(f(x) = \exp(x) - x^4\). As mentioned, there are 3 zeros for this function. Which one does Newton’s method converge to when \(x_0=8\)?
Question
Let \(f(x) = \sin(x) - \cos(4\cdot x)\).
Starting at \(\pi/8\), solve for the root returned by Newton’s method
Question
Using Newton’s method find a root to \(f(x) = \cos(x) - x^3\) starting at \(x_0 = 1/2\).
Question
Use Newton’s method to find a root of \(f(x) = x^5 + x -1\). Make a quick graph to find a reasonable starting point.
Question
For the following graph, graphically consider the algorithm for a few different starting points.
If \(x_0\) is \(1\) what occurs?
When \(x_0 = 1.0\) the following values are true for \(f\):
(e₀ = -0.16730397826141874, f₀′ = 4.0, f̄₀′′ = 31.81133480963258, ē₁ = 0.11130237758510458)
Where the values f̄₀′′
and ē₁
are worst-case estimates when \(\xi\) is between \(x_0\) and the zero.
Does the magnitude of the error increase or decrease in the first step?
If \(x_0\) is set near \(0.50\) what happens?
When \(x_0 = 0.5\) the following values are true for \(f\):
(e₀ = -0.6673039782614187, f₀′ = -0.6875, f̄₀′′ = 31.81133480963258, ē₁ = -10.302120429488337)
Where the values f̄₀′′
and ē₁
are worst-case estimates when \(\xi\) is between \(x_0\) and the zero.
Does the magnitude of the error increase or decrease in the first step?
If \(x_0\) is set near \(0.75\) what happens?
Question
Will Newton’s method converge for the function \(f(x) = x^5 - x + 1\) starting at \(x=1\)?
Question
Will Newton’s method converge for the function \(f(x) = 4x^5 - x + 1\) starting at \(x=1\)?
Question
Will Newton’s method converge for the function \(f(x) = x^{10} - 2x^3 - x + 1\) starting from \(0.25\)?
Question
Will Newton’s method converge for \(f(x) = 20x/(100 x^2 + 1)\) starting at \(0.1\)?
Question
Will Newton’s method converge to a zero for \(f(x) = \sqrt{(1 - x^2)^2}\) starting at \(1.0\)?
Question
Use Newton’s method to find a root of \(f(x) = 4x^4 - 5x^3 + 4x^2 -20x -6\) starting at \(x_0 = 0\).
Question
Use Newton’s method to find a zero of \(f(x) = \sin(x) - x/2\) that is bigger than \(0\).
Question
The Newton baffler (defined below) is so named, as Newton’s method will fail to find the root for most starting points.
function newton_baffler(x)
if ( x - 0.0 ) < -0.25
0.75 * ( x - 0 ) - 0.3125
elseif ( x - 0 ) < 0.25
2.0 * ( x - 0 )
else
0.75 * ( x - 0 ) + 0.3125
end
end
newton_baffler (generic function with 1 method)
Will Newton’s method find the zero at \(0.0\) starting at \(1\)?
Considering this plot:
plot(newton_baffler, -1.1, 1.1)
Starting with \(x_0=1\), you can see why Newton’s method will fail. Why?
This function does not have a small first derivative; or a large second derivative; and the bump up can be made as close to the origin as desired, so the starting point can be very close to the zero. However, even though the conditions of the error term are satisfied, the error term does not apply, as \(f\) is not continuously differentiable.
Question
Let \(f(x) = \sin(x) - x/4\). Starting at \(x_0 = 2\pi\) Newton’s method will converge to a value, but it will take many steps. Using the argument verbose=true
for find_zero
, how many steps does it take:
What is the zero that is found?
Is this the closest zero to the starting point, \(x_0\)?
Question
Quadratic convergence of Newton’s method only applies to simple roots. For example, we can see (using the verbose=true
argument to the Roots
package’s newton
method, that it only takes \(4\) steps to find a zero to \(f(x) = \cos(x) - x\) starting at \(x_0 = 1\). But it takes many more steps to find the same zero for \(f(x) = (\cos(x) - x)^2\).
How many?
Question: Implicit equations
The equation \(x^2 + x\cdot y + y^2 = 1\) is a rotated ellipse.
Can we find which point on its graph has the largest \(y\) value?
This would be straightforward if we could write \(y(x) = \dots\), for then we would simply find the critical points and investiate. But we can’t so easily solve for \(y\) interms of \(x\). However, we can use Newton’s method to do so:
function findy(x)
= y -> (x^2 + x*y + y^2) - 1
fn = y -> (x + 2y)
fp find_zero((fn, fp), sqrt(1 - x^2), Roots.Newton())
end
findy (generic function with 1 method)
For a fixed x, this solves for \(y\) in the equation: \(F(y) = x^2 + x \cdot y + y^2 - 1 = 0\). It should be that \((x,y)\) is a solution:
= .75
x = findy(x)
y ^2 + x*y + y^2 ## is this 1? x
1.0000000000000002
So we have a means to find \(y(x)\), but it is implicit.
Using find_zero
, find the value \(x\) which maximizes y
by finding a zero of y'
. Use this to find the point \((x,y)\) with largest \(y\) value.
(Using automatic derivatives works for values identified with find_zero
as long as the initial point has its type the same as that of x
.)
Question
In the last problem we used an approximate derivative (forward difference) in place of the derivative. This can introduce an error due to the approximation. Would Newton’s method still converge if the derivative in the algorithm were replaced with an approximate derivative? In general, this can often be done but the convergence can be slower and the sensitivity to a poor initial guess even greater.
Three common approximations are given by the difference quotient for a fixed \(h\): \(f'(x_i) \approx (f(x_i+h)-f(x_i))/h\); the secant line approximation: \(f'(x_i) \approx (f(x_i) - f(x_{i-1})) / (x_i - x_{i-1})\); and the Steffensen approximation \(f'(x_i) \approx (f(x_i + f(x_i)) - f(x_i)) / f(x_i)\) (using \(h=f(x_i)\)).
Let’s revisit the \(4\)-step convergence of Newton’s method to the root of \(f(x) = 1/x - q\) when \(q=0.8\). Will these methods be as fast?
Let’s define the above approximations for a given f
:
= 0.8
q₀ fq(x) = 1/x - q₀
secant_approx(x0,x1) = (fq(x1) - fq(x0)) / (x1 - x0)
diffq_approx(x0, h) = secant_approx(x0, x0+h)
steff_approx(x0) = diffq_approx(x0, fq(x0))
steff_approx (generic function with 1 method)
Then using the difference quotient would look like:
= 1e-6
Δ = 48/17 - 32/17 * q₀
x1 = x1 - fq(x1) / diffq_approx(x1, Δ) # |x1 - xstar| = 0.003660953777242959
x1 = x1 - fq(x1) / diffq_approx(x1, Δ) # |x1 - xstar| = 1.0719137523373945e-5; etc x1
1.2499892808624766
The Steffensen method would look like:
= 48/17 - 32/17 * q₀
x1 = x1 - fq(x1) / steff_approx(x1) # |x1 - xstar| = 0.0014382105783488086
x1 = x1 - fq(x1) / steff_approx(x1) # |x1 - xstar| = 5.944935954627084e-7; etc. x1
1.2499994055064045
And the secant method like:
= 1e-6
Δ = 48/17 - 32/17 * q₀
x1 = x1 - Δ # we need two initial values
x0 = x1, x1 - fq(x1) / secant_approx(x0, x1) # |x1 - xstar| = 0.00366084553494872
x0, x1 = x1, x1 - fq(x1) / secant_approx(x0, x1) # |x1 - xstar| = 0.00019811634659716582; etc. x0, x1
(1.2463391544650513, 1.2501981163465972)
Repeat each of the above algorithms until abs(x1 - 1.25)
is 0
(which will happen for this problem, though not in general). Record the steps.
- Does the difference quotient need more than \(4\) steps?
- Does the secant method need more than \(4\) steps?
- Does the Steffensen method need more than 4 steps?
All methods work quickly with this well-behaved problem. In general the convergence rates are slightly different for each, with the Steffensen method matching Newton’s method and the difference quotient method being slower in general. All can be more sensitive to the initial guess.