Skip to content

Instantly share code, notes, and snippets.

@dfm
Created August 3, 2012 13:41
Show Gist options
  • Save dfm/3247796 to your computer and use it in GitHub Desktop.
Save dfm/3247796 to your computer and use it in GitHub Desktop.
How to wrap C code in Python
#include <Python.h>
#include <numpy/arrayobject.h>
#include "chi2.h"
/* Docstrings */
static char module_docstring[] =
"This module provides an interface for calculating chi-squared using C.";
static char chi2_docstring[] =
"Calculate the chi-squared of some data given a model.";
/* Available functions */
static PyObject *chi2_chi2(PyObject *self, PyObject *args);
/* Module specification */
static PyMethodDef module_methods[] = {
{"chi2", chi2_chi2, METH_VARARGS, chi2_docstring},
{NULL, NULL, 0, NULL}
};
/* Initialize the module */
PyMODINIT_FUNC init_chi2(void)
{
PyObject *m = Py_InitModule3("_chi2", module_methods, module_docstring);
if (m == NULL)
return;
/* Load `numpy` functionality. */
import_array();
}
static PyObject *chi2_chi2(PyObject *self, PyObject *args)
{
double m, b;
PyObject *x_obj, *y_obj, *yerr_obj;
/* Parse the input tuple */
if (!PyArg_ParseTuple(args, "ddOOO", &m, &b, &x_obj, &y_obj,
&yerr_obj))
return NULL;
/* Interpret the input objects as numpy arrays. */
PyObject *x_array = PyArray_FROM_OTF(x_obj, NPY_DOUBLE, NPY_IN_ARRAY);
PyObject *y_array = PyArray_FROM_OTF(y_obj, NPY_DOUBLE, NPY_IN_ARRAY);
PyObject *yerr_array = PyArray_FROM_OTF(yerr_obj, NPY_DOUBLE,
NPY_IN_ARRAY);
/* If that didn't work, throw an exception. */
if (x_array == NULL || y_array == NULL || yerr_array == NULL) {
Py_XDECREF(x_array);
Py_XDECREF(y_array);
Py_XDECREF(yerr_array);
return NULL;
}
/* How many data points are there? */
int N = (int)PyArray_DIM(x_array, 0);
/* Get pointers to the data as C-types. */
double *x = (double*)PyArray_DATA(x_array);
double *y = (double*)PyArray_DATA(y_array);
double *yerr = (double*)PyArray_DATA(yerr_array);
/* Call the external C function to compute the chi-squared. */
double value = chi2(m, b, x, y, yerr, N);
/* Clean up. */
Py_DECREF(x_array);
Py_DECREF(y_array);
Py_DECREF(yerr_array);
if (value < 0.0) {
PyErr_SetString(PyExc_RuntimeError,
"Chi-squared returned an impossible value.");
return NULL;
}
/* Build the output tuple */
PyObject *ret = Py_BuildValue("d", value);
return ret;
}
#include "chi2.h"
double chi2(double m, double b, double *x, double *y, double *yerr, int N) {
int n;
double result = 0.0, diff;
for (n = 0; n < N; n++) {
diff = (y[n] - (m * x[n] + b)) / yerr[n];
result += diff * diff;
}
return result;
}
double chi2(double m, double b, double *x, double *y, double *yerr, int N);
from distutils.core import setup, Extension
import numpy.distutils.misc_util
c_ext = Extension("_chi2", ["_chi2.c", "chi2.c"])
setup(
ext_modules=[c_ext],
include_dirs=numpy.distutils.misc_util.get_numpy_include_dirs(),
)
@beaylott
Copy link

How about a ctypes version: https://gist.github.com/3313315 ...

@AraHaan
Copy link

AraHaan commented Dec 27, 2016

Nice code however what if my module does not need fucntions but need to do something like this where it calls classes from a backend module.

from importlib.machinery import FileFinder
from ._pycx_backend import *
import sys

extension_importers = [JsonImporter(), PycxImporter()]
hook_list = []
for importer in extension_importers:
    hook_list.append((importer.find_loader, importer.extensions))

sys.path_hooks.insert(0, FileFinder.path_hook(*hook_list))
sys.path_importer_cache.clear()

Basically I want to write this in using C Code similar to how you wrote this without using all too annoying Cython because it is entirely unreadable.

@RitamDey
Copy link

RitamDey commented Apr 11, 2017

Can't get it work on Python3.6. Importing in REPL tells me that Py_InitModule3

screenshot from 2017-04-11 21-11-32

Edit: Confirmed its initialization function outdated. Here is the updated gist from StackOverflow: https://gist.github.com/GreenJoey/b08528d6abe62da70f28f73c39c0efd0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment