diff options
author | Travis Oliphant <oliphant@enthought.com> | 2006-03-13 07:05:41 +0000 |
---|---|---|
committer | Travis Oliphant <oliphant@enthought.com> | 2006-03-13 07:05:41 +0000 |
commit | 3afb449634c2f1aac55e0a0d9d0693d098d66d8c (patch) | |
tree | c451d7d0c33788c6ef02e274f230aacb2c195273 | |
parent | 64c7a3cb6e30bee5bfbffb65b5e651362ccdaf32 (diff) | |
download | numpy-3afb449634c2f1aac55e0a0d9d0693d098d66d8c.tar.gz |
Adding swig and pyrex examples
-rw-r--r-- | numpy/core/include/numpy/c_numpy.pxd | 59 | ||||
-rw-r--r-- | numpy/core/include/numpy/numpy.i | 492 | ||||
-rw-r--r-- | numpy/core/src/arrayobject.c | 4 | ||||
-rw-r--r-- | numpy/doc/ufuncs.txt | 5 | ||||
-rwxr-xr-x | setup.py | 4 |
5 files changed, 560 insertions, 4 deletions
diff --git a/numpy/core/include/numpy/c_numpy.pxd b/numpy/core/include/numpy/c_numpy.pxd new file mode 100644 index 000000000..6482101a0 --- /dev/null +++ b/numpy/core/include/numpy/c_numpy.pxd @@ -0,0 +1,59 @@ +# :Author: Robert Kern +# :Copyright: 2004, Enthought, Inc. +# :License: BSD Style + + +cdef extern from "numpy/arrayobject.h": + ctypedef enum PyArray_TYPES: + PyArray_BOOL + PyArray_BYTE + PyArray_UBYTE + PyArray_SHORT + PyArray_USHORT + PyArray_INT + PyArray_UINT + PyArray_LONG + PyArray_ULONG + PyArray_LONGLONG + PyArray_ULONGLONG + PyArray_FLOAT + PyArray_DOUBLE + PyArray_LONGDOUBLE + PyArray_CFLOAT + PyArray_CDOUBLE + PyArray_CLONGDOUBLE + PyArray_OBJECT + PyArray_STRING + PyArray_UNICODE + PyArray_VOID + PyArray_NTYPES + PyArray_NOTYPE + + ctypedef int intp + + ctypedef extern class numpy.dtype [object PyArray_Descr]: + cdef int type_num, elsize, alignment + cdef char type, kind, byteorder, hasobject + cdef object fields, typeobj + + ctypedef extern class numpy.ndarray [object PyArrayObject]: + cdef char *data + cdef int nd + cdef intp *dimensions + cdef intp *strides + cdef object base + cdef dtype descr + cdef int flags + + ndarray PyArray_SimpleNew(int ndims, intp* dims, int item_type) + int PyArray_Check(object obj) + ndarray PyArray_ContiguousFromObject(object obj, PyArray_TYPES type, + int mindim, int maxdim) + intp PyArray_SIZE(ndarray arr) + void *PyArray_DATA(ndarray arr) + ndarray PyArray_FromAny(object obj, dtype newtype, int mindim, int maxdim, + int requirements, object context) + ndarray PyArray_NewFromDescr(object subtype, dtype newtype, int nd, intp* dims, + intp* strides, void* data, int flags, object parent) + + void import_array() diff --git a/numpy/core/include/numpy/numpy.i b/numpy/core/include/numpy/numpy.i new file mode 100644 index 000000000..0d34ef46b --- /dev/null +++ b/numpy/core/include/numpy/numpy.i @@ -0,0 +1,492 @@ +// -*- C -*- (not really, but good for syntax highlighting) +%{ +#ifndef SWIG_FILE_WITH_INIT +# define NO_IMPORT_ARRAY +#endif +#include "stdio.h" +#include <numpy/arrayobject.h> + +/* The following code originally appeared in enthought/kiva/agg/src/numeric.i, + * author unknown. It was translated from C++ to C by John Hunter. Bill + * Spotz has modified it slightly to fix some minor bugs, add some comments + * and some functionality. + */ + +/* Macros to extract array attributes. + */ +#define is_array(a) ((a) && PyArray_Check((PyArrayObject *)a)) +#define array_type(a) (int)(((PyArrayObject *)a)->descr->type_num) +#define array_dimensions(a) (((PyArrayObject *)a)->nd) +#define array_size(a,i) (((PyArrayObject *)a)->dimensions[i]) +#define array_is_contiguous(a) (PyArray_ISCONTIGUOUS(a)) + +/* Given a PyObject, return a string describing its type. + */ +char* pytype_string(PyObject* py_obj) { + if (py_obj == NULL ) return "C NULL value"; + if (PyCallable_Check(py_obj)) return "callable" ; + if (PyString_Check( py_obj)) return "string" ; + if (PyInt_Check( py_obj)) return "int" ; + if (PyFloat_Check( py_obj)) return "float" ; + if (PyDict_Check( py_obj)) return "dict" ; + if (PyList_Check( py_obj)) return "list" ; + if (PyTuple_Check( py_obj)) return "tuple" ; + if (PyFile_Check( py_obj)) return "file" ; + if (PyModule_Check( py_obj)) return "module" ; + if (PyInstance_Check(py_obj)) return "instance" ; + + return "unkown type"; +} + +/* Given a Numeric typecode, return a string describing the type. + */ +char* typecode_string(int typecode) { + char* type_names[20] = {"char","unsigned byte","byte","short", + "unsigned short","int","unsigned int","long", + "float","double","complex float","complex double", + "object","ntype","unkown"}; + return type_names[typecode]; +} + +/* Make sure input has correct numeric type. Allow character and byte + * to match. Also allow int and long to match. + */ +int type_match(int actual_type, int desired_type) { + int match = 1; + if ( actual_type != desired_type && + !(desired_type == PyArray_CHAR && actual_type == PyArray_SBYTE) && + !(desired_type == PyArray_SBYTE && actual_type == PyArray_CHAR) && + !(desired_type == PyArray_INT && actual_type == PyArray_LONG) && + !(desired_type == PyArray_LONG && actual_type == PyArray_INT)) + match = 0; + return match; +} + +/* Given a PyObject pointer, cast it to a PyArrayObject pointer if + * legal. If not, set the python error string appropriately and + * return NULL. + */ +PyArrayObject* obj_to_array_no_conversion(PyObject* input, int typecode) { + PyArrayObject* ary = NULL; + if (is_array(input) && (typecode == PyArray_NOTYPE || array_type(input) == typecode)) { + ary = (PyArrayObject*) input; + } + else if is_array(input) { + char* desired_type = typecode_string(typecode); + char* actual_type = typecode_string(array_type(input)); + PyErr_Format(PyExc_TypeError, + "Array of type '%s' required. Array of type '%s' given", + desired_type, actual_type); + ary = NULL; + } + else { + char * desired_type = typecode_string(typecode); + char * actual_type = pytype_string(input); + PyErr_Format(PyExc_TypeError, + "Array of type '%s' required. A %s was given", + desired_type, actual_type); + ary = NULL; + } + return ary; +} + +/* Convert the given PyObject to a Numeric array with the given + * typecode. On Success, return a valid PyArrayObject* with the + * correct type. On failure, the python error string will be set and + * the routine returns NULL. + */ +PyArrayObject* obj_to_array_allow_conversion(PyObject* input, int typecode, + int* is_new_object) +{ + PyArrayObject* ary = NULL; + PyObject* py_obj; + if (is_array(input) && (typecode == PyArray_NOTYPE || type_match(array_type(input),typecode))) { + ary = (PyArrayObject*) input; + *is_new_object = 0; + } + else { + py_obj = PyArray_FromObject(input, typecode, 0, 0); + // If NULL, PyArray_FromObject will have set python error value. + ary = (PyArrayObject*) py_obj; + *is_new_object = 1; + } + return ary; +} + +/* Given a PyArrayObject, check to see if it is contiguous. If so, + * return the input pointer and flag it as not a new object. If it is + * not contiguous, create a new PyArrayObject using the original data, + * flag it as a new object and return the pointer. + */ +PyArrayObject* make_contiguous(PyArrayObject* ary, int* is_new_object, + int min_dims, int max_dims) +{ + PyArrayObject* result; + if (array_is_contiguous(ary)) { + result = ary; + *is_new_object = 0; + } + else { + result = (PyArrayObject*) PyArray_ContiguousFromObject((PyObject*)ary, + array_type(ary), + min_dims, + max_dims); + *is_new_object = 1; + } + return result; +} + +/* Convert a given PyObject to a contiguous PyArrayObject of the + * specified type. If the input object is not a contiguous + * PyArrayObject, a new one will be created and the new object flag + * will be set. + */ +PyArrayObject* obj_to_array_contiguous_allow_conversion(PyObject* input, + int typecode, + int* is_new_object) { + int is_new1 = 0; + int is_new2 = 0; + PyArrayObject* ary2; + PyArrayObject* ary1 = obj_to_array_allow_conversion(input, typecode, + &is_new1); + if (ary1) { + ary2 = make_contiguous(ary1, &is_new2, 0, 0); + if ( is_new1 && is_new2) { + Py_DECREF(ary1); + } + ary1 = ary2; + } + *is_new_object = is_new1 || is_new2; + return ary1; +} + +/* Test whether a python object is contiguous. If array is + * contiguous, return 1. Otherwise, set the python error string and + * return 0. + */ +int require_contiguous(PyArrayObject* ary) { + int contiguous = 1; + if (!array_is_contiguous(ary)) { + PyErr_SetString(PyExc_TypeError, "Array must be contiguous. A discontiguous array was given"); + contiguous = 0; + } + return contiguous; +} + +/* Require the given PyArrayObject to have a specified number of + * dimensions. If the array has the specified number of dimensions, + * return 1. Otherwise, set the python error string and return 0. + */ +int require_dimensions(PyArrayObject* ary, int exact_dimensions) { + int success = 1; + if (array_dimensions(ary) != exact_dimensions) { + PyErr_Format(PyExc_TypeError, + "Array must be have %d dimensions. Given array has %d dimensions", + exact_dimensions, array_dimensions(ary)); + success = 0; + } + return success; +} + +/* Require the given PyArrayObject to have one of a list of specified + * number of dimensions. If the array has one of the specified number + * of dimensions, return 1. Otherwise, set the python error string + * and return 0. + */ +int require_dimensions_n(PyArrayObject* ary, int* exact_dimensions, int n) { + int success = 0; + int i; + char dims_str[255] = ""; + char s[255]; + for (i = 0; i < n && !success; i++) { + if (array_dimensions(ary) == exact_dimensions[i]) { + success = 1; + } + } + if (!success) { + for (i = 0; i < n-1; i++) { + sprintf(s, "%d, ", exact_dimensions[i]); + strcat(dims_str,s); + } + sprintf(s, " or %d", exact_dimensions[n-1]); + strcat(dims_str,s); + PyErr_Format(PyExc_TypeError, + "Array must be have %s dimensions. Given array has %d dimensions", + dims_str, array_dimensions(ary)); + } + return success; +} + +/* Require the given PyArrayObject to have a specified shape. If the + * array has the specified shape, return 1. Otherwise, set the python + * error string and return 0. + */ +int require_size(PyArrayObject* ary, int* size, int n) { + int i; + int success = 1; + int len; + char desired_dims[255] = "["; + char s[255]; + char actual_dims[255] = "["; + for(i=0; i < n;i++) { + if (size[i] != -1 && size[i] != array_size(ary,i)) { + success = 0; + } + } + if (!success) { + for (i = 0; i < n; i++) { + if (size[i] == -1) { + sprintf(s, "*,"); + } + else + { + sprintf(s, "%d,", size[i]); + } + strcat(desired_dims,s); + } + len = strlen(desired_dims); + desired_dims[len-1] = ']'; + for (i = 0; i < n; i++) { + sprintf(s, "%d,", array_size(ary,i)); + strcat(actual_dims,s); + } + len = strlen(actual_dims); + actual_dims[len-1] = ']'; + PyErr_Format(PyExc_TypeError, + "Array must be have shape of %s. Given array has shape of %s", + desired_dims, actual_dims); + } + return success; +} +/* End John Hunter translation (with modifications by Bill Spotz) */ + +%} + +/* TYPEMAP_IN macros + * + * This family of typemaps allows pure input C arguments of the form + * + * (type* IN_ARRAY1, int DIM1) + * (type* IN_ARRAY2, int DIM1, int DIM2) + * + * where "type" is any type supported by the Numeric module, to be + * called in python with an argument list of a single array (or any + * python object that can be passed to the Numeric.array constructor + * to produce an arrayof te specified shape). This can be applied to + * a existing functions using the %apply directive: + * + * %apply (double* IN_ARRAY1, int DIM1) {double* series, int length} + * %apply (double* IN_ARRAY2, int DIM1, int DIM2) {double* mx, int rows, int cols} + * double sum(double* series, int length); + * double max(double* mx, int rows, int cols); + * + * or with + * + * double sum(double* IN_ARRAY1, int DIM1); + * double max(double* IN_ARRAY2, int DIM1, int DIM2); + */ + +/* One dimensional input arrays */ +%define TYPEMAP_IN1(type,typecode) +%typemap(in) (type* IN_ARRAY1, int DIM1) + (PyArrayObject* array=NULL, int is_new_object) { + array = obj_to_array_contiguous_allow_conversion($input, typecode, &is_new_object); + int size[1] = {-1}; + if (!array || !require_dimensions(array,1) || !require_size(array,size,1)) SWIG_fail; + $1 = (type*) array->data; + $2 = array->dimensions[0]; +} +%typemap(freearg) (type* IN_ARRAY1, int DIM1) { + if (is_new_object$argnum && array$argnum) Py_DECREF(array$argnum); +} +%enddef + +/* Define concrete examples of the TYPEMAP_IN1 macros */ +TYPEMAP_IN1(char, PyArray_CHAR ) +TYPEMAP_IN1(unsigned char, PyArray_UBYTE ) +TYPEMAP_IN1(signed char, PyArray_SBYTE ) +TYPEMAP_IN1(short, PyArray_SHORT ) +TYPEMAP_IN1(int, PyArray_INT ) +TYPEMAP_IN1(long, PyArray_LONG ) +TYPEMAP_IN1(float, PyArray_FLOAT ) +TYPEMAP_IN1(double, PyArray_DOUBLE) +TYPEMAP_IN1(PyObject, PyArray_OBJECT) + +#undef TYPEMAP_IN1 + + /* Two dimensional input arrays */ +%define TYPEMAP_IN2(type,typecode) + %typemap(in) (type* IN_ARRAY2, int DIM1, int DIM2) + (PyArrayObject* array=NULL, int is_new_object) { + array = obj_to_array_contiguous_allow_conversion($input, typecode, &is_new_object); + int size[2] = {-1,-1}; + if (!array || !require_dimensions(array,2) || !require_size(array,size,1)) SWIG_fail; + $1 = (type*) array->data; + $2 = array->dimensions[0]; + $3 = array->dimensions[1]; +} +%typemap(freearg) (type* IN_ARRAY2, int DIM1, int DIM2) { + if (is_new_object$argnum && array$argnum) Py_DECREF(array$argnum); +} +%enddef + +/* Define concrete examples of the TYPEMAP_IN2 macros */ +TYPEMAP_IN2(char, PyArray_CHAR ) +TYPEMAP_IN2(unsigned char, PyArray_UBYTE ) +TYPEMAP_IN2(signed char, PyArray_SBYTE ) +TYPEMAP_IN2(short, PyArray_SHORT ) +TYPEMAP_IN2(int, PyArray_INT ) +TYPEMAP_IN2(long, PyArray_LONG ) +TYPEMAP_IN2(float, PyArray_FLOAT ) +TYPEMAP_IN2(double, PyArray_DOUBLE) +TYPEMAP_IN2(PyObject, PyArray_OBJECT) + +#undef TYPEMAP_IN2 + +/* TYPEMAP_INPLACE macros + * + * This family of typemaps allows input/output C arguments of the form + * + * (type* INPLACE_ARRAY1, int DIM1) + * (type* INPLACE_ARRAY2, int DIM1, int DIM2) + * + * where "type" is any type supported by the Numeric module, to be + * called in python with an argument list of a single contiguous + * Numeric array. This can be applied to an existing function using + * the %apply directive: + * + * %apply (double* INPLACE_ARRAY1, int DIM1) {double* series, int length} + * %apply (double* INPLACE_ARRAY2, int DIM1, int DIM2) {double* mx, int rows, int cols} + * void negate(double* series, int length); + * void normalize(double* mx, int rows, int cols); + * + * + * or with + * + * void sum(double* INPLACE_ARRAY1, int DIM1); + * void sum(double* INPLACE_ARRAY2, int DIM1, int DIM2); + */ + + /* One dimensional input/output arrays */ +%define TYPEMAP_INPLACE1(type,typecode) +%typemap(in) (type* INPLACE_ARRAY1, int DIM1) (PyArrayObject* temp=NULL) { + temp = obj_to_array_no_conversion($input,typecode); + if (!temp || !require_contiguous(temp)) SWIG_fail; + $1 = (type*) temp->data; + $2 = 1; + for (int i=0; i<temp->nd; ++i) $2 *= temp->dimensions[i]; +} +%enddef + +/* Define concrete examples of the TYPEMAP_INPLACE1 macro */ +TYPEMAP_INPLACE1(char, PyArray_CHAR ) +TYPEMAP_INPLACE1(unsigned char, PyArray_UBYTE ) +TYPEMAP_INPLACE1(signed char, PyArray_SBYTE ) +TYPEMAP_INPLACE1(short, PyArray_SHORT ) +TYPEMAP_INPLACE1(int, PyArray_INT ) +TYPEMAP_INPLACE1(long, PyArray_LONG ) +TYPEMAP_INPLACE1(float, PyArray_FLOAT ) +TYPEMAP_INPLACE1(double, PyArray_DOUBLE) +TYPEMAP_INPLACE1(PyObject, PyArray_OBJECT) + +#undef TYPEMAP_INPLACE1 + + /* Two dimensional input/output arrays */ +%define TYPEMAP_INPLACE2(type,typecode) + %typemap(in) (type* INPLACE_ARRAY2, int DIM1, int DIM2) (PyArrayObject* temp=NULL) { + temp = obj_to_array_no_conversion($input,typecode); + if (!temp || !require_contiguous(temp)) SWIG_fail; + $1 = (type*) temp->data; + $2 = temp->dimensions[0]; + $3 = temp->dimensions[1]; +} +%enddef + +/* Define concrete examples of the TYPEMAP_INPLACE2 macro */ +TYPEMAP_INPLACE2(char, PyArray_CHAR ) +TYPEMAP_INPLACE2(unsigned char, PyArray_UBYTE ) +TYPEMAP_INPLACE2(signed char, PyArray_SBYTE ) +TYPEMAP_INPLACE2(short, PyArray_SHORT ) +TYPEMAP_INPLACE2(int, PyArray_INT ) +TYPEMAP_INPLACE2(long, PyArray_LONG ) +TYPEMAP_INPLACE2(float, PyArray_FLOAT ) +TYPEMAP_INPLACE2(double, PyArray_DOUBLE) +TYPEMAP_INPLACE2(PyObject, PyArray_OBJECT) + +#undef TYPEMAP_INPLACE2 + +/* TYPEMAP_ARGOUT macros + * + * This family of typemaps allows output C arguments of the form + * + * (type* ARGOUT_ARRAY[ANY]) + * (type* ARGOUT_ARRAY[ANY][ANY]) + * + * where "type" is any type supported by the Numeric module, to be + * called in python with an argument list of a single contiguous + * Numeric array. This can be applied to an existing function using + * the %apply directive: + * + * %apply (double* ARGOUT_ARRAY[ANY] {double series, int length} + * %apply (double* ARGOUT_ARRAY[ANY][ANY]) {double* mx, int rows, int cols} + * void negate(double* series, int length); + * void normalize(double* mx, int rows, int cols); + * + * + * or with + * + * void sum(double* ARGOUT_ARRAY[ANY]); + * void sum(double* ARGOUT_ARRAY[ANY][ANY]); + */ + + /* One dimensional input/output arrays */ +%define TYPEMAP_ARGOUT1(type,typecode) +%typemap(in,numinputs=0) type ARGOUT_ARRAY[ANY] { + $1 = (type*) malloc($1_dim0*sizeof(type)); + if (!$1) { + PyErr_SetString(PyExc_RuntimeError, "Failed to allocate memory"); + SWIG_fail; + } +} +%typemap(argout) ARGOUT_ARRAY[ANY] { + int dimensions[1] = {$1_dim0}; + PyObject* outArray = PyArray_FromDimsAndData(1, dimensions, typecode, (char*)$1); +} +%enddef + +/* Define concrete examples of the TYPEMAP_ARGOUT1 macro */ +TYPEMAP_ARGOUT1(char, PyArray_CHAR ) +TYPEMAP_ARGOUT1(unsigned char, PyArray_UBYTE ) +TYPEMAP_ARGOUT1(signed char, PyArray_SBYTE ) +TYPEMAP_ARGOUT1(short, PyArray_SHORT ) +TYPEMAP_ARGOUT1(int, PyArray_INT ) +TYPEMAP_ARGOUT1(long, PyArray_LONG ) +TYPEMAP_ARGOUT1(float, PyArray_FLOAT ) +TYPEMAP_ARGOUT1(double, PyArray_DOUBLE) +TYPEMAP_ARGOUT1(PyObject, PyArray_OBJECT) + +#undef TYPEMAP_ARGOUT1 + + /* Two dimensional input/output arrays */ +%define TYPEMAP_ARGOUT2(type,typecode) + %typemap(in) (type* ARGOUT_ARRAY2, int DIM1, int DIM2) (PyArrayObject* temp=NULL) { + temp = obj_to_array_no_conversion($input,typecode); + if (!temp || !require_contiguous(temp)) SWIG_fail; + $1 = (type*) temp->data; + $2 = temp->dimensions[0]; + $3 = temp->dimensions[1]; +} +%enddef + +/* Define concrete examples of the TYPEMAP_ARGOUT2 macro */ +TYPEMAP_ARGOUT2(char, PyArray_CHAR ) +TYPEMAP_ARGOUT2(unsigned char, PyArray_UBYTE ) +TYPEMAP_ARGOUT2(signed char, PyArray_SBYTE ) +TYPEMAP_ARGOUT2(short, PyArray_SHORT ) +TYPEMAP_ARGOUT2(int, PyArray_INT ) +TYPEMAP_ARGOUT2(long, PyArray_LONG ) +TYPEMAP_ARGOUT2(float, PyArray_FLOAT ) +TYPEMAP_ARGOUT2(double, PyArray_DOUBLE) +TYPEMAP_ARGOUT2(PyObject, PyArray_OBJECT) + +#undef TYPEMAP_ARGOUT2 diff --git a/numpy/core/src/arrayobject.c b/numpy/core/src/arrayobject.c index 3b57c22c6..bb6d101cf 100644 --- a/numpy/core/src/arrayobject.c +++ b/numpy/core/src/arrayobject.c @@ -4048,7 +4048,9 @@ PyArray_NewFromDescr(PyTypeObject *subtype, PyArray_Descr *descr, int nd, self->data = data; /* call the __array_finalize__ - method if a subtype and some object passed in */ + method if a subtype. + If obj is NULL, then call method with Py_None + */ if ((subtype != &PyArray_Type)) { PyObject *res, *func, *args; static PyObject *str=NULL; diff --git a/numpy/doc/ufuncs.txt b/numpy/doc/ufuncs.txt index 2d4fa5048..345aa0a72 100644 --- a/numpy/doc/ufuncs.txt +++ b/numpy/doc/ufuncs.txt @@ -1,6 +1,9 @@ BUFFERED General Ufunc explanation: +Note: This was implemented already, but the notes are kept here for historical + and explanatory purposes. + We need to optimize the section of ufunc code that handles mixed-type and misbehaved arrays. In particular, we need to fix it so that items are not copied into the buffer if they don't have to be. @@ -95,4 +98,4 @@ If there are object arrays involved then loop->obj gets set to 1. Then there ar -
\ No newline at end of file + @@ -43,8 +43,8 @@ def setup_package(): try: config = Configuration( - maintainer = "SciPy Developers", - maintainer_email = "scipy-dev@scipy.org", + maintainer = "NumPy Developers", + maintainer_email = "numpy-discussion@lists.sourceforge.net", description = DOCLINES[0], long_description = "\n".join(DOCLINES[2:]), url = "http://numeric.scipy.org", |