Image Blog Access IMSL C Functions From Python
March 17, 2016

How to Access IMSL C Functions from Python

Embedded Analytics

In this article, we describe how to access IMSL C Numerical Library functions from Python using the IMSL Python Numerical Library (PyNL) as a framework. PyNL consists of a set of Python wrappers for a subset of the functions in IMSL C. It also features a foundation of components that can be used to add additional wrappers, extending it to call any function in IMSL C.

The details discussed assume that PyNL is installed. The discussion is based on developing a Python wrapper for the imsls_d_empirical_quantiles() function in IMSL C.

Note: The wrappers provided with PyNL are more extensive and include features such as robust error handling and documentation using Sphinx. These wrappers should be considered an additional source for guidance on how to access IMSL C functions from Python.

Guide to Creating a New Python Package

Once PyNL is installed, we recommend creating a new package within the PyNL area in your Python site-packages area. This allows multiple user-written wrappers to coexist with the wrappers already provided with PyNL.

The following steps serve as a guide to creating a new package, where PYNL_AREA refers to the site-packages/imsl folder in your Python installation.

  1. Create a new folder named PYNL_AREA/user_lib.
     
  2. Add a file named __init__.py to the PYNL_AREA/user_lib folder. The existing file, PYNL_AREA/cluster/__init__.py can be used as a guide for the contents of this file.
     
  3. Add the package name ‘user_lib’ to the file PYNL_AREA/__init__.py.
     
  4. Expose the name of the IMSL C function to be wrapped in PYNL_AREA/_imsllib.py. For example, to expose the IMSL C function imsls_d_empirical_quantiles(), add the following line at the end of the set of other functions that are exposed:
    self.expose("imsls_d_empirical_quantiles", None, _err_check, "stat")

    Use the file PYNL_AREA/_imsllib.py as a guide on where to add this line. This sets up the exception handling of PyNL to throw Python exceptions when errors are detected.

Note: If you are wrapping a C/Math function, the final argument to self.expose should be "math" rather than "stat".

Steps to Wrapping an IMSL C Function Using PyNL

There are three basic steps to wrapping an IMSL C function using PyNL as a framework:

  1. Performing data conversion
  2. Setting up arguments to be passed to the IMSL C function
  3. Packaging results from the IMSL C function

This example wraps the C/Stat function imsls_d_empirical_quantiles(), illustrating these three basic steps.

Place the wrapper file empirical_quantiles.py in PYNL_AREA/user_lib.

"""Empirical Quantiles related functions."""
import ctypes as _ctypes
import numpy as _numpy
import imsl._constants as _constants
import imsl._imsllib as _imsllib
import collections as _collections
def empirical_quantiles(x, qprop):
    r"""Compute empirical quantiles."""
    ref_type = _numpy.float64

    # Convert Data
    _x = _numpy.asarray(x, order='C', dtype=ref_type)
    if _x.ndim != 1:
        raise ValueError("x must be a 1-D array")
    _qprop = _numpy.asarray(qprop, order='C', dtype=ref_type)
    if _qprop.ndim != 1:
        raise ValueError("qprop must be a 1-D array")
    _n_obs = _x.shape[0]
    _n_qprop = _qprop.shape[0]

    # Set up arguments to be passed to the CNL function.
    args = []
    # Required input argument list
    args.append(_ctypes.c_int(_n_obs))
    args.append(_x.ctypes.data_as(_ctypes.c_void_p))
    args.append(_ctypes.c_int(_n_qprop))
    args.append(_qprop.ctypes.data_as(_ctypes.c_void_p))

    # Create space for results
    _result = _numpy.empty(_n_qprop, dtype=ref_type)
    args.append(_constants.IMSLS_RETURN_USER)
    args.append(_result.ctypes.data_as(_ctypes.c_void_p))
    _n_missing = _ctypes.c_int32()
    args.append(_constants.IMSLS_N_MISSING)
    args.append(_ctypes.byref(_n_missing))
    _xlo = _numpy.empty(_n_qprop, dtype=ref_type)
    args.append(_constants.IMSLS_XLO_USER)
    args.append(_xlo.ctypes.data_as(_ctypes.c_void_p))
    _xhi = _numpy.empty(_n_qprop, dtype=ref_type)
    args.append(_constants.IMSLS_XHI_USER)
    args.append(_xhi.ctypes.data_as(_ctypes.c_void_p))
    args.append(0) # Terminating zero for CNL argument list.

    # Call the CNL function
    _imsllib.imsllib.imsls_d_empirical_quantiles(*args)

    # Package results from the CNL function
    result = _collections.namedtuple("empirical_quantiles",
                                     ["quantiles", "n_missing",
                                      "xlo", "xhi"])
    result.quantiles = _result
    result.n_missing = _n_missing
    result.xlo = _xlo
    result.xhi = _xhi
    return result


Example of Using PyNL Wrappers

In this example, five empirical quantiles from a sample of size 30 are obtained. Notice that the 0.5 quantile corresponds to the sample median. The data are from Hinkley (1977) and Velleman and Hoaglin (1981). They are the measurements (in inches) of precipitation in Minneapolis/St. Paul during the month of March for 30 consecutive years.

import imsl.user_lib as user_lib

x = [0.77, 1.74, 0.81, 1.20, 1.95,
     1.20, 0.47, 1.43, 3.37, 2.20,
     3.00, 3.09, 1.51, 2.10, 0.52,
     1.62, 1.31, 0.32, 0.59, 0.81,
     2.81, 1.87, 1.18, 1.35, 4.75,
     2.48, 0.96, 1.89, 0.90, 2.05]

qprop = [0.01, 0.5, 0.9, 0.95, 0.99]

result = user_lib.empirical_quantiles(x, prop)

print("           Smaller Empirical Larger")
print("Quantile Datum Quantile Datum")
for i in range(5):
    print("{:6.2f} {:9.2f} {:9.2f} {:9.2f}".format(
          qprop[i], result.xlo[i], result.quantiles[i],
          result.xhi[i]))


Example Output


             Smaller   Empirical    Larger
Quantile     Datum     Quantile     Datum
 0.01         0.32      0.32         0.32
 0.50         1.43      1.47         1.51
 0.90         3.00      3.08         3.09
 0.95         3.37      3.99         4.75
 0.99         4.75      4.75         4.75


Additional Resources and Next Steps

Want to try IMSL on your project? Request a trial today by clicking the button below.

Free Trial

Additional Resources