4

I am new to matlab and I need to create a function that does n iterations of the Newton-Raphson method with starting approximation x = a. This starting approximation does not count as an interation and another requirement is that a for loop is required. I have looked at other similar questions posted but in my case I do not want to use a while loop.

This is what my inputs are supposed to be:

mynewton(f,a,n) which takes three inputs: 
f: A function handle for a function of x.
a: A real number.
n: A positive integer.

And here is my code so far.

function r=mynewton(f,a,n)
syms x;
z=f(x);
y=a;
for i=1:n    
    y(i+1)=y(i)-(z(i)/diff(z(i)));
end
r=y
end

When I try to call the function I get an error saying:

Error in MuPAD command: DOUBLE cannot convert the input expression into a double    array.
If the input expression contains a symbolic variable, use the VPA function instead.
Error in mynewton (line 6)
y(i+1)=y(i)-(z(i)/diff(z(i)));

The question is how do I use this VPA function? Granted the rest of my code probably isn't 100% correct either but any help that addresses the vpa problem or fixes the other parts of my code would be greatly appreciated.

Thanks!

rayryeng
  • 102,964
  • 22
  • 184
  • 193
  • I think a Newton-Raphson implementation should be something like this http://stackoverflow.com/a/5640295/3839249 – Yvon Aug 05 '14 at 00:21
  • However I need to use a for loop not a while loop –  Aug 05 '14 at 01:01
  • 1) Your problem with the error may lie in the definition of the function handle `f`. Most commonly people define functions as function handles, but you are using it as symbolic expression (you have `syms x`). Would you provide your usage about `f` (how do you define it from the caller)? 2) Frankly, `for` loop isn't good for N-R algo (or most of the iterative methods). But anyway, your error isn't about the `for` loop, but something else. – Yvon Aug 05 '14 at 01:37
  • @Yvon - I figured out why it wasn't working. Check my post if you're interested – rayryeng Aug 05 '14 at 03:48
  • @zillakilla27 - I solved your problem. Check my post if you have time. – rayryeng Aug 05 '14 at 03:49
  • 1
    It should be worth mentioning `fzero`, which you could use to check your outcomes. – Rody Oldenhuis Aug 05 '14 at 10:17
  • @RodyOldenhuis - Totally agree! – rayryeng Aug 05 '14 at 12:54
  • Rolled back to previous version. Obvious vandalism happened. – rayryeng Aug 05 '14 at 23:29
  • @rayryeng The revision was by OP. It seems that OP is still confused why he/she is getting "vpa function errors", which is obviously not related to `vpa`. – Yvon Aug 05 '14 at 23:34
  • @Yvon - I figured it was vandalism because they were such huge edits... and on top of that, the OP changed the tag from MATLAB to Python... what?! The funny thing is that my answer resolved those errors... yet the OP keeps going on about the errors. – rayryeng Aug 05 '14 at 23:37
  • 1
    No idea.... Let's just keep this question since your answer is great (forgot to +1 it). BTW I guess the reason to strangely use a `for` loop is the need to see converging behavior as the loops carry on. – Yvon Aug 05 '14 at 23:41
  • @Yvon - Thanks :) I also +1'ed your big data mean post. I totally forgot to do it, and just did. Also your reasoning makes sense! – rayryeng Aug 05 '14 at 23:57

2 Answers2

8

There are two things that aren't quite correct with your Newton-Raphson technique... but certainly fixable! After we fix this, there is no need for the VPA error that you're speaking of.


Error #1 - The iteration update

The first one is the iteration itself. Recall the definition of the Newton-Raphson technique:

blah
(source: mit.edu)

For the next iteration, you use the previous iteration's value. What you're doing is using the loop counter and substituting this into your f(x), which is not correct. It must be the previous iteration's value.

Error #2 - Mixing symbolic values with numeric values

If you take a look at how you're coding your function, you define your function symbolically, yet you are trying to substitute numeric values into your function. This unfortunately doesn't fly with MATLAB. If you actually want to substitute values in, you have to use subs. This will substitute an actual value for you as a function of x or whichever independent variable your function is using. Once you do this, your value is still a sym type. You need to cast this as double in order to be able to use this numerically.


Also for efficiency, there is no need to make y an array. Just make this a single value that updates itself at each iteration. With all of this being said, your code is updated to look like this. Mind you, I took the derivative of the function before the loop to decrease the amount of computation you need to take. I also split up the numerator and denominator terms for the Newton-Raphson iterations to make things clear, and to make this more palatable for subs. Without further ado:

function r = mynewton(f,a,n)
syms x;
z = f(x);
diffZ = diff(z); %// Edit - Include derivative
y = a; %// Initial root

for idx = 1 : n    
    numZ = subs(z,x,y); %// Numerator - Substitute f(x) for f(y)
    denZ = subs(diffZ,x,y); %// Denominator - Substitute for f'(x) for f'(y)
    y = y - double(numZ)/double(denZ); %// Update - Cast to double to get the numerical value
end
r = y; %// Send to output
end

Take note that I replaced i with idx in the loop. The reason why is because it is actually not recommended to use i or j as loop indices as these letters are reserved to represent complex numbers. If you take a look at this post by Shai, you'll see that it's actually slower to use these variables as loop indices: Using i and j as variables in Matlab

In any case, to test this out, suppose our function was y = sin(x), and my initial root was x0 = 2, with 5 iterations, we do:

f = @(x) sin(x);
r = mynewton(f, 2, 5)

r =

3.1416

This agrees with our knowledge of sin(x), as the intercepts of sin(x) are located at integer multiples of pi. x0 = 2 is located near pi so this does work as we expected.


Little bonus for you

Your original code had the values of the root stored at each iteration in y. If you really want to do that, you'll have to modify your code so that it looks something like this. Bear in mind that I pre-allocated y to make things more efficient:

function r = mynewton(f,a,n)
syms x;
z = f(x);
diffZ = diff(z);
y = zeros(1,n+1); %// Pre-allocate output array 
y(1) = a; %// First entry is the initial root

for idx = 1 : n    
    numZ = subs(z,x,y(idx)); %// Remember to use PREVIOUS guess for next guess
    denZ = subs(diffZ,x,y(idx));
    y(idx+1) = y(idx) - double(numZ)/double(denZ); %// Place next guess in right spot  
end
r = y; %// Send to output
end

By running this code using the exact same parameters as above, we get:

f = @(x) sin(x);
r = mynewton(f, 2, 5)

r =

    2.0000    4.1850    2.4679    3.2662    3.1409    3.1416

Each value in r tells you the guess of the root at that particular iteration. The first element of the array is the initial guess (of course). The next values are the guesses at each iteration of the Newton-Raphson root. Take note that the last element of the array is our final iteration, which is roughly equal to pi.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
rayryeng
  • 102,964
  • 22
  • 184
  • 193
  • Oh yeah so that's what OP means by `diff(z)`.... Another amazing work by rayryeng! By the way, would you consider speeding up this function by converting `z` and `diff(z)` before the loop? http://www.mathworks.com/help/symbolic/create-matlab-functions-from-mupad-expressions.html – Yvon Aug 05 '14 at 04:18
  • @Yvon - Of course. Didn't consider that. That'll definitely speed things up!... and thanks for the compliments :) – rayryeng Aug 05 '14 at 04:37
  • 1
    @rayryeng I would rather use a tolerance, `while ( dy>tol ) & ( idx – patrik Aug 05 '14 at 09:28
  • @patrik I totally agree. This is how I have always implemented NR but the OP wants a for loop. Thanks for your compliments :-) – rayryeng Aug 05 '14 at 12:32
  • How would this solution change if there were to be two inputs? I haven't written Matlab in a long time and I'm trying to refresh my memory but can't nail this down. – Tanner Feb 03 '16 at 02:13
  • @tannman357 The method to do this for two dimensions is different. You are required to calculate the Jacobian and the update rule changes. This was one of my earliest answers when I started out: http://stackoverflow.com/questions/20468776/using-matlab-to-write-a-function-that-implements-newtons-method-in-two-dimensio/21747741#21747741 – rayryeng Feb 03 '16 at 02:50
  • 1
    @rayryeng that looks great. Thanks! – Tanner Feb 03 '16 at 03:21
0

You need to use eval

So z(x)=f(x), then eval(z(1)) to evaluate the function at x = 1

Full code:

function r=mynewton(fun,a,n)
% e.g. for mynewton(@sin, 1, 2)
syms x;
z(x)=fun(x);  % sin(x)
y=a;
for i=1:n    
    y(i+1)=y(i)-(eval(z(i))/eval(diff(z(i))));
end
r=y
end

Not sure about the VPA part of the question, I usually ignore MUPAD errors :P

legas
  • 408
  • 4
  • 10
  • 1
    I guess that it would be preferred to do the differentiation before the loop. Also `subs` is created to evaluate symbolic expressions, so I think it is better to first use `subs` and then cast the output using `double`. If it is not necessary I would avoid `eval`, because `eval` is evaluated at runtime :( (=hard to debug among other stuff). – patrik Aug 06 '14 at 06:47
  • FYI, the use of `eval` is considered bad practice. There is no need for its use here. Consult Loren Shure's MathWorks article on the topic: http://blogs.mathworks.com/loren/2005/12/28/evading-eval/ – rayryeng Dec 15 '14 at 17:38