Sunday, November 16, 2008

M - Files: III


Debugging

There are three ways of debugging a user-defined function:

1. Brute Force 1: Break the whole function into its (logical) parts and examine what happens as you change inputs and go through a set of commands one by one. May be just copy and paste the sequence of instructions on the command window and what happens to the values of the variables created / modified within the function. It's equivalent to writing the function as a script or breaking it down to smaller scripts.

2. Brute Force 2: Just remove the semicolon at the end of all commands in the function. This will print on the command screen anything or everything that your function does to the variables defined / created within its body. It can potentially, however, result in a very cluttered command window making it difficult to figure out where the program is going wrong (especially when the number of commands is large and you are working with empirical data).

3. Use keyboard & return: Use of the command keyboard within an M-file interrupts the execution of the function and transfers control to the user. In that case, the command prompt symbol is changed to 'K>>'. One can examine or change variables (all MATLAB commands remain valid). To terminate the keyboard mode use the command return.

A related command is pause. You have to enable it first though (use pause on). When an M-file encounters pause it halts execution temporarily until the user presses any key. This can also be a useful debugging tool - or may be just to see after a set of steps if your function is doing what it is supposed to do.

Requesting User Input

It's often needed (or efficient or just plain simple useful) to be able to ask user for inputs (as to whether to continue beyond a point or what values to give to any of the variables or where to read the data from or what name to use to save a variable or the workspace etc etc). The syntax is:

>> user_entry = input('prompt')

A couple of useful extensions include:

>> user_entry = input('prompt', 's')

% In this case the user_entry is taken as a string (even if the user input is numeric)

>> user_entry = input('prompt\n')

% In this case the user_entry input is taken on the 'next line'. Try yourself to see the difference.

An Example

1. Solving the equation f (x) = exp (- x) - sin (x)

function [x, n] = newton1(x0, tol, maxit)

% Using the Newton’s method for solving the equation:
% f (x) = exp (-x) - sin (x)

x = x0; n = 0; done = 0;

while ~done
n = n + 1;
x_new = x -(exp(-x) - sin(x)) / (-exp(-x) - cos(x));
done = (n >= maxit) ( lt (abs(x_new - x), tol);
x = x_new;
end

2. The command feval

The command feval executes the function at pre-specified (argument) values.

For example say you have a function called myfun which takes a single argument 'x'. Then

>> feval('myfun', x0)

evaluates myfun(x) at value x0.

See help feval for more details.

3. Let our myfun be a function that evaluates the function and also calculates the first derivative as in newton1 above:

function [fx, fx_prime] = myfun(x)

% Evaluate f (x) = exp (x) - sin(x) and its first derivative

fx = exp (-x) - sin (x);
fx_prime = - exp (- x) - cos (x);

4. Now let's use myfun(x) and feval together to modify the newton1 function above.

(This also illustrates that you can use a custom function created as an M-file (in this case myfun(x)) inside another custom function (in this case newton2). It's not only more efficient and neater that way, it also makes for easier debugging. And of course, it also illustrates the use of feval)

function [x, n] = newton2 (fname, x0, tol, maxit)

% Using the Newton’s method for solving the equation/function given by fname
% function fname to be entered as a string
% For example if the function name fname, is myfun, it should be entered as 'myfun'

x = x0; n = 0; done = 0;

while ~done
n = n + 1;
[fx, fx_prime] = feval ('myfun', x);
x_new = x – fx / fx_prime;
done = (n > maxit) ( lt (abs(x_new - x), tol);
x = x_new;
end

Note that this now becomes a general function for solving any equation by Newton's method - of course, as long as the function fname is appropriately created. To the extent possible, this is how you should try to create your own functions. Divide the whole into logically sensible parts, and make them as general as possible.

No comments: