CU Mechanical Engineering

Numerical Methods Matlab tutorials



Writing Matlab Scripts: solving for the root of a nonlinear equation

In this example, we will write a function that finds a root of a nonlinear equation using (a) bisection method, (b) secant method, and (c) Newton’s method. Each of these methods will be defined as separate functions.

Unlike functions, scripts in Matlab do not require any initial statements - they are simply a collection of commands run serially. In essence, you could get the same thing by typing the lines one after the other at the command line however the power of scripts lies in the fact that changes and interations are simple and the time savings are well worth the work. This script to solve the roots of nonlinear equtions begins by defining several variables to be used later on:

syms f x
eps =0.00001;

The 'syms' command is a way to construct symbolic objects. The 'eps' variable is the tolerance for finding a root.

This project requires two major steps: the case considering the function exp(x)-2 and another case considering the function atan(x-1/sqrt(2)). The code for the first case looke like this:

% Case a:
f=exp(x)-2;
[x_root_bisec,x_bisec,f_bisec]=bisection(0,1,f,eps);
[x_root_secant,x_secant,f_secant]=secant(0,1,f,eps);
[x_root_newton,x_newton,f_newton]=newton(1,f,eps);

disp(['x_exact=',num2str(log(2)),' x_bisection=',num2str(x_root_bisec),...
	' x_secant=',num2str(x_root_secant),' x_newton=',num2str(x_root_newton)])
figure(1)
hold off
semilogy(abs(f_bisec),'r-')
ylabel('error')
xlabel('iteration')
hold on
plot(abs(f_secant),'m-')
plot(abs(f_newton),'b-')

This script utilizes functions external to this script. It is important to note that inline functions can only be used within functions and not scripts. for case (a), the function is defined and then each of the functions are called. The next segment of code plots the results from the first case. The 'figure(1)' command tells matlab that the following commands are for a single plot. The plot is identified to be a semi-log plot with the log scale on the y-axis. The 'hold on' command indicates that all following plot commands will be plotted on the same graph.

Since there are three ways to evaluate this root, it is necessary to look at each way seperately. Each has it's own block of code. The first is the bisection:

function [x_root,x_guess,f_guess]=bisection(x1,x2,fun,eps)

The function declaration for these different methods generally take four inputs including the interval, the function and the tolerance value.

xh1=min([x1 x2]);
xh2=max([x1,x2]);
x_guess = [xh1 xh2];
f_guess = subs(fun,x_guess);
if (f_guess(1)*f_guess(2)) > 0 
    disp('wrong guess');
    x_root=NaN;
    return
end
if min(abs(f_guess)) < eps
   [f_min,i_min] = min(abs(f_guess));
   x_root=x_guess(i_min);
   disp('excellent guess')
   return
end
iter = 2;
check = logical(1);

while check
    iter = iter +1;
    x_guess(iter)=(xh1+xh2)/2;
    f_guess(iter) = subs(fun,x_guess(iter));
    if min(abs(f_guess)) < eps
        [f_min,i_min] = min(abs(f_guess));
        x_root=x_guess(i_min);
        check = logical(0);
    else
        if f_guess(iter) < 0
            xh1=x_guess(iter);
        elseif f_guess(iter) > 0
            xh2=x_guess(iter);
        end
    end
end

The fist part of the above segment looks at the first two arguments of the function to determine the interval and defines that interval as the vector 'x_guess'. The next line uses the 'subs' built-in matlab function to evaluate the given function with the values in the 'x_guess' vector and then determines if either one of those points are within the tolerance value of zero. If so, it returns the value and uses the 'return' command to stop the script. This part also determines if the function values at the endpoints of the interval are diffenrent signs. If not, the script terminates with errors.

The following 'while' loop begins the iteration process to converge on a solution. It begins by taking the initial interval and dividing it in half and evaluating the new midpoint to determine if it is a root. If not, the sign is compared at that point to both of the end points to determine the interval it will use for the next iteration. This loop continues to bisect the intervals, always evaluating at the midpoint until the function value at that point is within the tolerance of zero at which point the root is returned.

function [x_root,x_guess,f_guess]=secant(x1,x2,fun,eps)

This function takes the same inputs as the secant method: interval values, function and prespecified tolerance.

xh1=min([x1 x2]);
xh2=max([x1,x2]);
x_guess = [xh1 xh2];
f_guess = subs(fun,x_guess);
fh1=f_guess(1);
fh2=f_guess(2);
if (f_guess(1)*f_guess(2)) > 0 
    disp('wrong guess');
    x_root=NaN;
    return
end
if min(abs(f_guess)) < eps
   [f_min,i_min] = min(abs(f_guess));
   x_root=x_guess(i_min);
   disp('excellent guess')
   return
end
iter = 2;
check = logical(1);

while check
    iter = iter +1;
    x_guess(iter)=xh1-fh1*(xh1-xh2)/(fh1-fh2);
    f_guess(iter) = subs(fun,x_guess(iter));
    if min(abs(f_guess)) < eps
        [f_min,i_min] = min(abs(f_guess));
        x_root=x_guess(i_min);
        check = logical(0);
    else
        if f_guess(iter) < 0
            xh1=x_guess(iter);
            fh1=f_guess(iter);
        elseif f_guess(iter) > 0
            xh2=x_guess(iter);
            fh2=f_guess(iter);
        end
    end
end

This function begins in the same way as the secant method by evaluating the end points of the interval. If neither of these points are roots, the iterations begin by running the secant algorithm for determining roots. The while loop systematically guesses and evaluates the function at those values to determine if it has found a root. If not, the algorithm continues until a root within the tolerance if found.

Finally there is the Newton Method:

function [x_root,x_guess,f_guess]=newton(x1,fun,eps)
x_guess(1) = [x1];
f_guess(1) = subs(fun,x_guess);
df=subs(diff(fun),x_guess(1));
if abs(f_guess) < eps
   x_root=x_guess;
   disp('excellent guess');
   return
end
iter = 1;
check = logical(1);

while check
    iter = iter +1;
    x_guess(iter)=0;
    f_guess(iter)=0;
    x_guess(iter)=x_guess(iter-1)-f_guess(iter-1)/df;
    f_guess(iter) = subs(fun,x_guess(iter));
    df=subs(diff(fun),x_guess(iter));
    if abs(f_guess(iter)) < eps
        x_root=x_guess(iter);
        check = logical(0);
    end
end

Once again, the beginning of this function is the same as the previous two methods. The next section uses the 'diff' function to calculate the derivitive of the input function. The 'subs' function is once again used to dermine the value at this point.

Now we'll consder the second case, when the function is defined as atan(x-1/sqrt(2)):

%Case b:
f=atan(x-1/sqrt(2));
[x_root_bisec,x_bisec,f_bisec]=bisection(0,1,f,eps);
[x_root_secant,x_secant,f_secant]=secant(0,1,f,eps);
[x_root_newton,x_newton,f_newton]=newton(1,f,eps);

disp(['x_exact=',num2str(log(2)),' x_bisection=',num2str(x_root_bisec),...
	' x_secant=',num2str(x_root_secant),' x_newton=',num2str(x_root_newton)])
figure(2)
hold off
semilogy(abs(f_bisec),'r-')
ylabel('error')
xlabel('iteration')
hold on
plot(abs(f_secant),'m-')
plot(abs(f_newton),'b-')

This is the same as the first example but using a different function. Refer to the above for more in depth detail.

The m-file can now be saved as 'root.m' The following are several sample outputs from the code illustrating the ways to call the function with different types of return arguments.

>> root
x_exact=0.69315 x_bisection=0.69315 x_secant=0.69315 x_newton=0.69315
x_exact=0.69315 x_bisection=0.70711 x_secant=0.70711 x_newton=0.70711
figure 1
figure 2

The entire matlab script can be seen below or downloaded here (right-click to save):

%******************************************************************************
%*                         Computational Project # 2                          *
%******************************************************************************
syms f x
eps =0.00001;
% Case a:
f=exp(x)-2;
[x_root_bisec,x_bisec,f_bisec]=bisection(0,1,f,eps);
[x_root_secant,x_secant,f_secant]=secant(0,1,f,eps);
[x_root_newton,x_newton,f_newton]=newton(1,f,eps);

disp(['x_exact=',num2str(log(2)),' x_bisection=',num2str(x_root_bisec),..
	' x_secant=',num2str(x_root_secant),' x_newton=',num2str(x_root_newton)])
figure(1)
hold off
semilogy(abs(f_bisec),'r-')
ylabel('error')
xlabel('iteration')
hold on
plot(abs(f_secant),'m-')
plot(abs(f_newton),'b-')

% Case b:
f=atan(x-1/sqrt(2));
[x_root_bisec,x_bisec,f_bisec]=bisection(0,1,f,eps);
[x_root_secant,x_secant,f_secant]=secant(0,1,f,eps);
[x_root_newton,x_newton,f_newton]=newton(1,f,eps);

disp(['x_exact=',num2str(log(2)),' x_bisection=',num2str(x_root_bisec),...
	' x_secant=',num2str(x_root_secant),' x_newton=',num2str(x_root_newton)])
figure(2)
hold off
semilogy(abs(f_bisec),'r-')
ylabel('error')
xlabel('iteration')
hold on
plot(abs(f_secant),'m-')
plot(abs(f_newton),'b-')
%-------------------------------------------------------------

%Bisection
function [x_root,x_guess,f_guess]=bisection(x1,x2,fun,eps)

xh1=min([x1 x2]);
xh2=max([x1,x2]);
x_guess = [xh1 xh2];
f_guess = subs(fun,x_guess);
if (f_guess(1)*f_guess(2)) > 0 
    disp('wrong guess');
    x_root=NaN;
    return
end
if min(abs(f_guess)) < eps
   [f_min,i_min] = min(abs(f_guess));
   x_root=x_guess(i_min);
   disp('excellent guess')
   return
end
iter = 2;
check = logical(1);

while check
    iter = iter +1;
    x_guess(iter)=(xh1+xh2)/2;
    f_guess(iter) = subs(fun,x_guess(iter));
    if min(abs(f_guess)) < eps
        [f_min,i_min] = min(abs(f_guess));
        x_root=x_guess(i_min);
        check = logical(0);
    else
        if f_guess(iter) < 0
            xh1=x_guess(iter);
        elseif f_guess(iter) > 0
            xh2=x_guess(iter);
        end
    end
end
%----------------------------------------------------------

%Secant
function [x_root,x_guess,f_guess]=secant(x1,x2,fun,eps)

xh1=min([x1 x2]);
xh2=max([x1,x2]);
x_guess = [xh1 xh2];
f_guess = subs(fun,x_guess);
fh1=f_guess(1);
fh2=f_guess(2);
if (f_guess(1)*f_guess(2)) > 0 
    disp('wrong guess');
    x_root=NaN;
    return
end
if min(abs(f_guess)) < eps
   [f_min,i_min] = min(abs(f_guess));
   x_root=x_guess(i_min);
   disp('excellent guess')
   return
end
iter = 2;
check = logical(1);

while check
    iter = iter +1;
    x_guess(iter)=xh1-fh1*(xh1-xh2)/(fh1-fh2);
    f_guess(iter) = subs(fun,x_guess(iter));
    if min(abs(f_guess)) < eps
        [f_min,i_min] = min(abs(f_guess));
        x_root=x_guess(i_min);
        check = logical(0);
    else
        if f_guess(iter) < 0
            xh1=x_guess(iter);
            fh1=f_guess(iter);
        elseif f_guess(iter) > 0
            xh2=x_guess(iter);
            fh2=f_guess(iter);
        end
    end
end
    
%------------------------------------------------------

%Newton
function [x_root,x_guess,f_guess]=newton(x1,fun,eps)
x_guess(1) = [x1];
f_guess(1) = subs(fun,x_guess);
df=subs(diff(fun),x_guess(1));
if abs(f_guess) < eps
   x_root=x_guess;
   disp('excellent guess');
   return
end
iter = 1;
check = logical(1);

while check
    iter = iter +1;
    x_guess(iter)=0;
    f_guess(iter)=0;
    x_guess(iter)=x_guess(iter-1)-f_guess(iter-1)/df;
    f_guess(iter) = subs(fun,x_guess(iter));
    df=subs(diff(fun),x_guess(iter));
    if abs(f_guess(iter)) < eps
        x_root=x_guess(iter);
        check = logical(0);
    end
end