Python/NAG Part 3 – Callback functions

April 10th, 2009 | Categories: NAG Library, programming, python | Tags:

This is part 3 of a series of articles on how to use the NAG C Library with Python using the ctypes module.  Click here for the introduction and index to the series.

Now that we have seen how to call the simplest of NAG functions from Python, it is time to move on and consider callback functions.  If you don’t know what a callback function is then I think that the current version of the relevant Wikipedia article explains it well:

A callback is executable code that is passed as an argument to other code.

For example, say you want to want to find the zero of the function exp(-x)/x using a routine from the NAG library.  Your first step would be to create a function that evaluates exp(-x)/x for any given x.    You’ll later pass this function to the NAG routine to allow the routine to evaluate exp(-x)/x wherever it needs to.

Let’s take a look at some Python code that performs the calculation described above.  The NAG routine I am using locates a zero of a continuous function in a given interval by a combination of the methods of linear interpolation, extrapolation and bisection and is called c05adc.

 #!/usr/bin/env python #Example 2 using NAG C libray and ctypes #Mike Croucher - April 2008 #Concept Introduced : Callback functions  from ctypes import * from math import * libnag = cdll.LoadLibrary("/opt/NAG/cllux08dgl/lib/libnagc_nag.so.8")  c05adc = libnag.c05adc c05adc.restype=None 

#define python function
def py_func(x):
return exp(-x)-x


 #create the type for the c callback function C_FUNC = CFUNCTYPE(c_double,c_double )  #now create the c callback function c_func = C_FUNC(py_func)  a=c_double(0.0) b=c_double(1.0) xtol=c_double(0.00001) ftol=c_double(0.0) x=c_double(0.0) fail=c_int(0)  c05adc(a, b, byref(x), c_func, xtol, ftol, fail );  print "result = %s" % (x.value)

Copy and paste (or download) this to a file called c05adc.py, make it executable and run it by typing the following at a bash prompt.
 chmod +x ./c05adc.py ./c05adc.py 
If all has gone well then you should get following output

result = 0.567143306605

Most of the code above will be familiar to anyone who worked through part 1 of this series.  The first unfamiliar part might be



#define python function
def py_func(x):
return exp(-x)-x


Which is nothing more than a standard Python function for calculating exp(-x)-x.
 C_FUNC = CFUNCTYPE(c_double,c_double ) 

Here I have created a new C function prototype called C_FUNC by using the CFUNCTYPE function from the ctypes module. The general syntax of CFUNCTYPE is

CFUNCTYPE(return_type,input_arg1,input_arg2,input_arg3)

where the list of input arguments can be as long as you like.  The C function I want to create from my Python function should return a c_double and have a single c_double input argument so I end up with CFUNCTYPE(c_double,c_double )

 c_func = C_FUNC(py_func) 
Here I have used the function prototype created earlier to generate a C function called c_func based on my Python function py_func.
 c05adc(a, b, byref(x), c_func, xtol, ftol, fail );

Finally, I call the NAG routine c05adc using all of the arguments set up earlier.   Note that I can pass my C function, c_func, to the NAG routine just as easily as I would any other argument. For reference, here is the original C prototype of the NAG routine along with a list of its arguments and what they mean
 void nag_zero_cont_func_bd(double a, double b, double *x, double (*f)(double x), double xtol, double ftol, NagError *fail) 

• a – (Input) The lower bound of the interval [a,b] in which the search for a zero will take place.
• b – (Input) The upper bound of the interval
• x – (Output) The approximation to the zero.  Note that the C prototype uses a pointer to double and I have emulated this in python using the byref() function to pass x by reference.
• f – (Input) A pointer to the function who’s zeros we want to find.  The C prototype might look complicated but, as you have seen, the implementation in Python is quite straightforward.
• xtol – (Input) The absolute tolerance to which the zero is required.
• ftol – (Input)  a value such that if |f (x)| < ftol, x is accepted as the zero.  ftol may be specified as0.0
• fail – The NAG error parameter.  We haven’t covered structures yet so we can’t use NAGError properly but I have used the trick that the NAG routines will accept c_int(0) in order to accept the default error handling behaviour.

That’s it for now. Next time I will be looking at numerical integration using the NAG library which will require the introduction of structures.