forked from sbu-python-class/python-science
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnumpy-ex.c
More file actions
116 lines (88 loc) · 3.52 KB
/
numpy-ex.c
File metadata and controls
116 lines (88 loc) · 3.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/* see
http://wiki.scipy.org/Cookbook/C_Extensions/NumPy_arrays and
http://scipy-lectures.github.io/advanced/interfacing_with_c/interfacing_with_c.html
*/
#include <Python.h>
#include <numpy/arrayobject.h>
#include <math.h>
/* a static function in C limits its scope to this file -- the linker
won't complain about clashes */
static PyObject* ex_function(PyObject* self, PyObject* args)
{
PyArrayObject *iarray, *oarray;
double **iA, **oA;
int i, j, m, n, dims[2];
/* parse the inputs -- we need to know what the arguments of our
call were. We'll assume:
ex_function(array)
and that we return a new array of the same dimensions
In the parsing, you can do O or O! here (from the docs):
O (object) [PyObject *]
Store a Python object (without any conversion) in a C object
pointer. The C program thus receives the actual object that was
passed. The object’s reference count is not increased. The
pointer stored is not NULL.
O! (object) [typeobject, PyObject *]
Store a Python object in a C object pointer. This is similar to
O, but takes two C arguments: the first is the address of a
Python type object, the second is the address of the C variable
(of type PyObject*) into which the object pointer is stored. If
the Python object does not have the required type, TypeError is
raised.
O! seems safer and preferred
*/
if (!PyArg_ParseTuple(args, "O!", &PyArray_Type, &iarray)) return NULL;
if (NULL == iarray) return NULL;
/* check to make sure we are a double type */
if (iarray->descr->type_num != NPY_DOUBLE ||
iarray->nd != 2) {
PyErr_SetString(PyExc_ValueError, "wrong input array type");
return NULL;
}
/* get the dimensions */
n = dims[0] = iarray->dimensions[0];
m = dims[1] = iarray->dimensions[1];
/* the new C interface can create iteration "object" using NpyIter, but we
are not going to do that here, we want to explicitly see the different
dimensions
*/
/* make a NumPy double matrix with the same dimensions -- this will
be contiguous, and will be our output (note, there is also a
PyArray_NewLikeArray function) */
oarray = (PyArrayObject *) PyArray_FromDims(2, dims, NPY_DOUBLE);
/* change contigous arrays into C ** arrays -- we need to have a
vector of pointers that point to the correct location in the
contiguous block of memory that stores the multi-dimensional
array data */
iA = (double **) malloc( (size_t) (n*sizeof(double)));
for (i = 0; i < n; i++) {
iA[i] = (double *) iarray->data + i*m;
}
oA = (double **) malloc( (size_t) (n*sizeof(double)));
for (i = 0; i < n; i++) {
oA[i] = (double *) oarray->data + i*m;
}
/* now we can do our manipulation */
for (i = 0; i < n; i ++) {
for (j = 0; j < m; j++) {
oA[i][j] = iA[i][j]*iA[i][j];
}
}
/* free up the memory we allocated for the array indexing */
free (iA);
free (oA);
/* return our python array */
return PyArray_Return(oarray);
}
/* this is the table for function names that Python will see */
static PyMethodDef numpy_in_cMethods[] = {
{"example", ex_function, METH_VARARGS,
"a simple example: square the elements of an array"},
{NULL, NULL}
};
/* this tells python what to do when it first imports this module --
the name follows directly from the table name above */
PyMODINIT_FUNC initnumpy_in_c(void) {
(void) Py_InitModule("numpy_in_c", numpy_in_cMethods);
import_array(); // this deals with the NumPy stuff
}