Movatterモバイル変換


[0]ホーム

URL:


SciPy

numpy.i: a SWIG Interface File for NumPy

Introduction

The Simple Wrapper and Interface Generator (orSWIG) is a powerful tool for generating wrappercode for interfacing to a wide variety of scripting languages.SWIG can parse header files, and using only the code prototypes,create an interface to the target language. ButSWIG is notomnipotent. For example, it cannot know from the prototype:

doublerms(double*seq,intn);

what exactlyseq is. Is it a single value to be altered in-place?Is it an array, and if so what is its length? Is it input-only?Output-only? Input-output?SWIG cannot determine these details,and does not attempt to do so.

If we designedrms, we probably made it a routine that takes aninput-only array of lengthn ofdouble values calledseqand returns the root mean square. The default behavior ofSWIG,however, will be to create a wrapper function that compiles, but isnearly impossible to use from the scripting language in the way the Croutine was intended.

For Python, the preferred way of handling contiguous (or technically,strided) blocks of homogeneous data is with NumPy, which provides fullobject-oriented access to multidimensial arrays of data. Therefore, the mostlogical Python interface for therms function would be (including docstring):

defrms(seq):"""    rms: return the root mean square of a sequence    rms(numpy.ndarray) -> double    rms(list) -> double    rms(tuple) -> double    """

whereseq would be a NumPy array ofdouble values, and itslengthn would be extracted fromseq internally before beingpassed to the C routine. Even better, since NumPy supportsconstruction of arrays from arbitrary Python sequences,seqitself could be a nearly arbitrary sequence (so long as each elementcan be converted to adouble) and the wrapper code wouldinternally convert it to a NumPy array before extracting its dataand length.

SWIG allows these types of conversions to be defined via amechanism calledtypemaps. This document provides information onhow to usenumpy.i, aSWIG interface file that defines a seriesof typemaps intended to make the type of array-related conversionsdescribed above relatively simple to implement. For example, supposethat therms function prototype defined above was in a header filenamedrms.h. To obtain the Python interface discussed above, yourSWIG interface file would need the following:

%{#define SWIG_FILE_WITH_INIT#include "rms.h"%}%include"numpy.i"%init%{import_array();%}%apply(double*IN_ARRAY1,intDIM1){(double*seq,intn)};%include"rms.h"

Typemaps are keyed off a list of one or more function arguments,either by type or by type and name. We will refer to such lists assignatures. One of the many typemaps defined bynumpy.i is usedabove and has the signature(double*IN_ARRAY1,intDIM1). Theargument names are intended to suggest that thedouble* argumentis an input array of one dimension and that theint represents thesize of that dimension. This is precisely the pattern in thermsprototype.

Most likely, no actual prototypes to be wrapped will have the argumentnamesIN_ARRAY1 andDIM1. We use theSWIG%applydirective to apply the typemap for one-dimensional input arrays oftypedouble to the actual prototype used byrms. Usingnumpy.i effectively, therefore, requires knowing what typemaps areavailable and what they do.

ASWIG interface file that includes theSWIG directives givenabove will produce wrapper code that looks something like:

 1 PyObject *_wrap_rms(PyObject *args) { 2   PyObject *resultobj = 0; 3   double *arg1 = (double *) 0 ; 4   int arg2 ; 5   double result; 6   PyArrayObject *array1 = NULL ; 7   int is_new_object1 = 0 ; 8   PyObject * obj0 = 0 ; 910   if (!PyArg_ParseTuple(args,(char *)"O:rms",&obj0)) SWIG_fail;11   {12     array1 = obj_to_array_contiguous_allow_conversion(13                  obj0, NPY_DOUBLE, &is_new_object1);14     npy_intp size[1] = {15       -116     };17     if (!array1 || !require_dimensions(array1, 1) ||18         !require_size(array1, size, 1)) SWIG_fail;19     arg1 = (double*) array1->data;20     arg2 = (int) array1->dimensions[0];21   }22   result = (double)rms(arg1,arg2);23   resultobj = SWIG_From_double((double)(result));24   {25     if (is_new_object1 && array1) Py_DECREF(array1);26   }27   return resultobj;28 fail:29   {30     if (is_new_object1 && array1) Py_DECREF(array1);31   }32   return NULL;33 }

The typemaps fromnumpy.i are responsible for the following linesof code: 12–20, 25 and 30. Line 10 parses the input to thermsfunction. From the format string"O:rms", we can see that theargument list is expected to be a single Python object (specifiedby theO before the colon) and whose pointer is stored inobj0. A number of functions, supplied bynumpy.i, are calledto make and check the (possible) conversion from a generic Pythonobject to a NumPy array. These functions are explained in thesectionHelper Functions, but hopefully their names areself-explanatory. At line 12 we useobj0 to construct a NumPyarray. At line 17, we check the validity of the result: that it isnon-null and that it has a single dimension of arbitrary length. Oncethese states are verified, we extract the data buffer and length inlines 19 and 20 so that we can call the underlying C function at line22. Line 25 performs memory management for the case where we havecreated a new array that is no longer needed.

This code has a significant amount of error handling. Note theSWIG_fail is a macro forgotofail, referring to the label atline 28. If the user provides the wrong number of arguments, thiswill be caught at line 10. If construction of the NumPy arrayfails or produces an array with the wrong number of dimensions, theseerrors are caught at line 17. And finally, if an error is detected,memory is still managed correctly at line 30.

Note that if the C function signature was in a different order:

doublerms(intn,double*seq);

thatSWIG would not match the typemap signature given above withthe argument list forrms. Fortunately,numpy.i has a set oftypemaps with the data pointer given last:

%apply(intDIM1,double*IN_ARRAY1){(intn,double*seq)};

This simply has the effect of switching the definitions ofarg1andarg2 in lines 3 and 4 of the generated code above, and theirassignments in lines 19 and 20.

Using numpy.i

Thenumpy.i file is currently located in thetools/swigsub-directory under thenumpy installation directory. Typically,you will want to copy it to the directory where you are developingyour wrappers.

A simple module that only uses a singleSWIG interface file shouldinclude the following:

%{#define SWIG_FILE_WITH_INIT%}%include"numpy.i"%init%{import_array();%}

Within a compiled Python module,import_array() should only getcalled once. This could be in a C/C++ file that you have written andis linked to the module. If this is the case, then none of yourinterface files should#defineSWIG_FILE_WITH_INIT or callimport_array(). Or, this initialization call could be in awrapper file generated bySWIG from an interface file that has the%init block as above. If this is the case, and you have more thanoneSWIG interface file, then only one interface file should#defineSWIG_FILE_WITH_INIT and callimport_array().

Available Typemaps

The typemap directives provided bynumpy.i for arrays of differentdata types, saydouble andint, and dimensions of differenttypes, sayint orlong, are identical to one another exceptfor the C and NumPy type specifications. The typemaps aretherefore implemented (typically behind the scenes) via a macro:

%numpy_typemaps(DATA_TYPE,DATA_TYPECODE,DIM_TYPE)

that can be invoked for appropriate(DATA_TYPE,DATA_TYPECODE,DIM_TYPE) triplets. For example:

%numpy_typemaps(double,NPY_DOUBLE,int)%numpy_typemaps(int,NPY_INT,int)

Thenumpy.i interface file uses the%numpy_typemaps macro toimplement typemaps for the following C data types andintdimension types:

  • signedchar
  • unsignedchar
  • short
  • unsignedshort
  • int
  • unsignedint
  • long
  • unsignedlong
  • longlong
  • unsignedlonglong
  • float
  • double

In the following descriptions, we reference a genericDATA_TYPE, whichcould be any of the C data types listed above, andDIM_TYPE whichshould be one of the many types of integers.

The typemap signatures are largely differentiated on the name given tothe buffer pointer. Names withFARRAY are for Fortran-orderedarrays, and names withARRAY are for C-ordered (or 1D arrays).

Input Arrays

Input arrays are defined as arrays of data that are passed into aroutine but are not altered in-place or returned to the user. ThePython input array is therefore allowed to be almost any Pythonsequence (such as a list) that can be converted to the requested typeof array. The input array signatures are

1D:

  • (DATA_TYPEIN_ARRAY1[ANY])
  • (DATA_TYPE*IN_ARRAY1,intDIM1)
  • (intDIM1,DATA_TYPE*IN_ARRAY1)

2D:

  • (DATA_TYPEIN_ARRAY2[ANY][ANY])
  • (DATA_TYPE*IN_ARRAY2,intDIM1,intDIM2)
  • (intDIM1,intDIM2,DATA_TYPE*IN_ARRAY2)
  • (DATA_TYPE*IN_FARRAY2,intDIM1,intDIM2)
  • (intDIM1,intDIM2,DATA_TYPE*IN_FARRAY2)

3D:

  • (DATA_TYPEIN_ARRAY3[ANY][ANY][ANY])
  • (DATA_TYPE*IN_ARRAY3,intDIM1,intDIM2,intDIM3)
  • (intDIM1,intDIM2,intDIM3,DATA_TYPE*IN_ARRAY3)
  • (DATA_TYPE*IN_FARRAY3,intDIM1,intDIM2,intDIM3)
  • (intDIM1,intDIM2,intDIM3,DATA_TYPE*IN_FARRAY3)

4D:

  • (DATA_TYPEIN_ARRAY4[ANY][ANY][ANY][ANY])
  • (DATA_TYPE*IN_ARRAY4,DIM_TYPEDIM1,DIM_TYPEDIM2,DIM_TYPEDIM3,DIM_TYPEDIM4)
  • (DIM_TYPEDIM1,DIM_TYPEDIM2,DIM_TYPEDIM3,,DIM_TYPEDIM4,DATA_TYPE*IN_ARRAY4)
  • (DATA_TYPE*IN_FARRAY4,DIM_TYPEDIM1,DIM_TYPEDIM2,DIM_TYPEDIM3,DIM_TYPEDIM4)
  • (DIM_TYPEDIM1,DIM_TYPEDIM2,DIM_TYPEDIM3,DIM_TYPEDIM4,DATA_TYPE*IN_FARRAY4)

The first signature listed,(DATA_TYPEIN_ARRAY[ANY]) is forone-dimensional arrays with hard-coded dimensions. Likewise,(DATA_TYPEIN_ARRAY2[ANY][ANY]) is for two-dimensional arrayswith hard-coded dimensions, and similarly for three-dimensional.

In-Place Arrays

In-place arrays are defined as arrays that are modified in-place. Theinput values may or may not be used, but the values at the time thefunction returns are significant. The provided Python argumentmust therefore be a NumPy array of the required type. The in-placesignatures are

1D:

  • (DATA_TYPEINPLACE_ARRAY1[ANY])
  • (DATA_TYPE*INPLACE_ARRAY1,intDIM1)
  • (intDIM1,DATA_TYPE*INPLACE_ARRAY1)

2D:

  • (DATA_TYPEINPLACE_ARRAY2[ANY][ANY])
  • (DATA_TYPE*INPLACE_ARRAY2,intDIM1,intDIM2)
  • (intDIM1,intDIM2,DATA_TYPE*INPLACE_ARRAY2)
  • (DATA_TYPE*INPLACE_FARRAY2,intDIM1,intDIM2)
  • (intDIM1,intDIM2,DATA_TYPE*INPLACE_FARRAY2)

3D:

  • (DATA_TYPEINPLACE_ARRAY3[ANY][ANY][ANY])
  • (DATA_TYPE*INPLACE_ARRAY3,intDIM1,intDIM2,intDIM3)
  • (intDIM1,intDIM2,intDIM3,DATA_TYPE*INPLACE_ARRAY3)
  • (DATA_TYPE*INPLACE_FARRAY3,intDIM1,intDIM2,intDIM3)
  • (intDIM1,intDIM2,intDIM3,DATA_TYPE*INPLACE_FARRAY3)

4D:

  • (DATA_TYPEINPLACE_ARRAY4[ANY][ANY][ANY][ANY])
  • (DATA_TYPE*INPLACE_ARRAY4,DIM_TYPEDIM1,DIM_TYPEDIM2,DIM_TYPEDIM3,DIM_TYPEDIM4)
  • (DIM_TYPEDIM1,DIM_TYPEDIM2,DIM_TYPEDIM3,,DIM_TYPEDIM4,DATA_TYPE*INPLACE_ARRAY4)
  • (DATA_TYPE*INPLACE_FARRAY4,DIM_TYPEDIM1,DIM_TYPEDIM2,DIM_TYPEDIM3,DIM_TYPEDIM4)
  • (DIM_TYPEDIM1,DIM_TYPEDIM2,DIM_TYPEDIM3,DIM_TYPEDIM4,DATA_TYPE*INPLACE_FARRAY4)

These typemaps now check to make sure that theINPLACE_ARRAYarguments use native byte ordering. If not, an exception is raised.

There is also a “flat” in-place array for situations in whichyou would like to modify or process each element, regardless of thenumber of dimensions. One example is a “quantization” function thatquantizes each element of an array in-place, be it 1D, 2D or whatever.This form checks for continuity but allows either C or Fortran ordering.

ND:

  • (DATA_TYPE*INPLACE_ARRAY_FLAT,DIM_TYPEDIM_FLAT)

Argout Arrays

Argout arrays are arrays that appear in the input arguments in C, butare in fact output arrays. This pattern occurs often when there ismore than one output variable and the single return argument istherefore not sufficient. In Python, the conventional way to returnmultiple arguments is to pack them into a sequence (tuple, list, etc.)and return the sequence. This is what the argout typemaps do. If awrapped function that uses these argout typemaps has more than onereturn argument, they are packed into a tuple or list, depending onthe version of Python. The Python user does not pass thesearrays in, they simply get returned. For the case where a dimensionis specified, the python user must provide that dimension as anargument. The argout signatures are

1D:

  • (DATA_TYPEARGOUT_ARRAY1[ANY])
  • (DATA_TYPE*ARGOUT_ARRAY1,intDIM1)
  • (intDIM1,DATA_TYPE*ARGOUT_ARRAY1)

2D:

  • (DATA_TYPEARGOUT_ARRAY2[ANY][ANY])

3D:

  • (DATA_TYPEARGOUT_ARRAY3[ANY][ANY][ANY])

4D:

  • (DATA_TYPEARGOUT_ARRAY4[ANY][ANY][ANY][ANY])

These are typically used in situations where in C/C++, you wouldallocate a(n) array(s) on the heap, and call the function to fill thearray(s) values. In Python, the arrays are allocated for you andreturned as new array objects.

Note that we supportDATA_TYPE* argout typemaps in 1D, but not 2Dor 3D. This is because of a quirk with theSWIG typemap syntax andcannot be avoided. Note that for these types of 1D typemaps, thePython function will take a single argument representingDIM1.

Argout View Arrays

Argoutview arrays are for when your C code provides you with a view ofits internal data and does not require any memory to be allocated bythe user. This can be dangerous. There is almost no way to guaranteethat the internal data from the C code will remain in existence forthe entire lifetime of the NumPy array that encapsulates it. Ifthe user destroys the object that provides the view of the data beforedestroying the NumPy array, then using that array may result in badmemory references or segmentation faults. Nevertheless, there aresituations, working with large data sets, where you simply have noother choice.

The C code to be wrapped for argoutview arrays are characterized bypointers: pointers to the dimensions and double pointers to the data,so that these values can be passed back to the user. The argoutviewtypemap signatures are therefore

1D:

  • (DATA_TYPE**ARGOUTVIEW_ARRAY1,DIM_TYPE*DIM1)
  • (DIM_TYPE*DIM1,DATA_TYPE**ARGOUTVIEW_ARRAY1)

2D:

  • (DATA_TYPE**ARGOUTVIEW_ARRAY2,DIM_TYPE*DIM1,DIM_TYPE*DIM2)
  • (DIM_TYPE*DIM1,DIM_TYPE*DIM2,DATA_TYPE**ARGOUTVIEW_ARRAY2)
  • (DATA_TYPE**ARGOUTVIEW_FARRAY2,DIM_TYPE*DIM1,DIM_TYPE*DIM2)
  • (DIM_TYPE*DIM1,DIM_TYPE*DIM2,DATA_TYPE**ARGOUTVIEW_FARRAY2)

3D:

  • (DATA_TYPE**ARGOUTVIEW_ARRAY3,DIM_TYPE*DIM1,DIM_TYPE*DIM2,DIM_TYPE*DIM3)
  • (DIM_TYPE*DIM1,DIM_TYPE*DIM2,DIM_TYPE*DIM3,DATA_TYPE**ARGOUTVIEW_ARRAY3)
  • (DATA_TYPE**ARGOUTVIEW_FARRAY3,DIM_TYPE*DIM1,DIM_TYPE*DIM2,DIM_TYPE*DIM3)
  • (DIM_TYPE*DIM1,DIM_TYPE*DIM2,DIM_TYPE*DIM3,DATA_TYPE**ARGOUTVIEW_FARRAY3)

4D:

  • (DATA_TYPE**ARGOUTVIEW_ARRAY4,DIM_TYPE*DIM1,DIM_TYPE*DIM2,DIM_TYPE*DIM3,DIM_TYPE*DIM4)
  • (DIM_TYPE*DIM1,DIM_TYPE*DIM2,DIM_TYPE*DIM3,DIM_TYPE*DIM4,DATA_TYPE**ARGOUTVIEW_ARRAY4)
  • (DATA_TYPE**ARGOUTVIEW_FARRAY4,DIM_TYPE*DIM1,DIM_TYPE*DIM2,DIM_TYPE*DIM3,DIM_TYPE*DIM4)
  • (DIM_TYPE*DIM1,DIM_TYPE*DIM2,DIM_TYPE*DIM3,DIM_TYPE*DIM4,DATA_TYPE**ARGOUTVIEW_FARRAY4)

Note that arrays with hard-coded dimensions are not supported. Thesecannot follow the double pointer signatures of these typemaps.

Memory Managed Argout View Arrays

A recent addition tonumpy.i are typemaps that permit argoutarrays with views into memory that is managed. See the discussionhere.

1D:

  • (DATA_TYPE**ARGOUTVIEWM_ARRAY1,DIM_TYPE*DIM1)
  • (DIM_TYPE*DIM1,DATA_TYPE**ARGOUTVIEWM_ARRAY1)

2D:

  • (DATA_TYPE**ARGOUTVIEWM_ARRAY2,DIM_TYPE*DIM1,DIM_TYPE*DIM2)
  • (DIM_TYPE*DIM1,DIM_TYPE*DIM2,DATA_TYPE**ARGOUTVIEWM_ARRAY2)
  • (DATA_TYPE**ARGOUTVIEWM_FARRAY2,DIM_TYPE*DIM1,DIM_TYPE*DIM2)
  • (DIM_TYPE*DIM1,DIM_TYPE*DIM2,DATA_TYPE**ARGOUTVIEWM_FARRAY2)

3D:

  • (DATA_TYPE**ARGOUTVIEWM_ARRAY3,DIM_TYPE*DIM1,DIM_TYPE*DIM2,DIM_TYPE*DIM3)
  • (DIM_TYPE*DIM1,DIM_TYPE*DIM2,DIM_TYPE*DIM3,DATA_TYPE**ARGOUTVIEWM_ARRAY3)
  • (DATA_TYPE**ARGOUTVIEWM_FARRAY3,DIM_TYPE*DIM1,DIM_TYPE*DIM2,DIM_TYPE*DIM3)
  • (DIM_TYPE*DIM1,DIM_TYPE*DIM2,DIM_TYPE*DIM3,DATA_TYPE**ARGOUTVIEWM_FARRAY3)

4D:

  • (DATA_TYPE**ARGOUTVIEWM_ARRAY4,DIM_TYPE*DIM1,DIM_TYPE*DIM2,DIM_TYPE*DIM3,DIM_TYPE*DIM4)
  • (DIM_TYPE*DIM1,DIM_TYPE*DIM2,DIM_TYPE*DIM3,DIM_TYPE*DIM4,DATA_TYPE**ARGOUTVIEWM_ARRAY4)
  • (DATA_TYPE**ARGOUTVIEWM_FARRAY4,DIM_TYPE*DIM1,DIM_TYPE*DIM2,DIM_TYPE*DIM3,DIM_TYPE*DIM4)
  • (DIM_TYPE*DIM1,DIM_TYPE*DIM2,DIM_TYPE*DIM3,DIM_TYPE*DIM4,DATA_TYPE**ARGOUTVIEWM_FARRAY4)

Output Arrays

Thenumpy.i interface file does not support typemaps for outputarrays, for several reasons. First, C/C++ return arguments arelimited to a single value. This prevents obtaining dimensioninformation in a general way. Second, arrays with hard-coded lengthsare not permitted as return arguments. In other words:

double[3]newVector(doublex,doubley,doublez);

is not legal C/C++ syntax. Therefore, we cannot provide typemaps ofthe form:

%typemap(out)(TYPE[ANY]);

If you run into a situation where a function or method is returning apointer to an array, your best bet is to write your own version of thefunction to be wrapped, either with%extend for the case of classmethods or%ignore and%rename for the case of functions.

Other Common Types: bool

Note that C++ typebool is not supported in the list in theAvailable Typemaps section. NumPy bools are a single byte, whilethe C++bool is four bytes (at least on my system). Therefore:

%numpy_typemaps(bool,NPY_BOOL,int)

will result in typemaps that will produce code that referenceimproper data lengths. You can implement the following macroexpansion:

%numpy_typemaps(bool,NPY_UINT,int)

to fix the data length problem, andInput Arrays will work fine,butIn-Place Arrays might fail type-checking.

Other Common Types: complex

Typemap conversions for complex floating-point types is also notsupported automatically. This is because Python and NumPy arewritten in C, which does not have native complex types. BothPython and NumPy implement their own (essentially equivalent)struct definitions for complex variables:

/*Python*/typedefstruct{doublereal;doubleimag;}Py_complex;/*NumPy*/typedefstruct{floatreal,imag;}npy_cfloat;typedefstruct{doublereal,imag;}npy_cdouble;

We could have implemented:

%numpy_typemaps(Py_complex,NPY_CDOUBLE,int)%numpy_typemaps(npy_cfloat,NPY_CFLOAT,int)%numpy_typemaps(npy_cdouble,NPY_CDOUBLE,int)

which would have provided automatic type conversions for arrays oftypePy_complex,npy_cfloat andnpy_cdouble. However, itseemed unlikely that there would be any independent (non-Python,non-NumPy) application code that people would be usingSWIG togenerate a Python interface to, that also used these definitionsfor complex types. More likely, these application codes will definetheir own complex types, or in the case of C++, usestd::complex.Assuming these data structures are compatible with Python andNumPy complex types,%numpy_typemap expansions as above (withthe user’s complex type substituted for the first argument) shouldwork.

NumPy Array Scalars and SWIG

SWIG has sophisticated type checking for numerical types. Forexample, if your C/C++ routine expects an integer as input, the codegenerated bySWIG will check for both Python integers andPython long integers, and raise an overflow error if the providedPython integer is too big to cast down to a C integer. With theintroduction of NumPy scalar arrays into your Python code, youmight conceivably extract an integer from a NumPy array and attemptto pass this to aSWIG-wrapped C/C++ function that expects anint, but theSWIG type checking will not recognize the NumPyarray scalar as an integer. (Often, this does in fact work – itdepends on whether NumPy recognizes the integer type you are usingas inheriting from the Python integer type on the platform you areusing. Sometimes, this means that code that works on a 32-bit machinewill fail on a 64-bit machine.)

If you get a Python error that looks like the following:

TypeError:inmethod'MyClass_MyMethod',argument2oftype'int'

and the argument you are passing is an integer extracted from aNumPy array, then you have stumbled upon this problem. Thesolution is to modify theSWIG type conversion system to acceptNumPy array scalars in addition to the standard integer types.Fortunately, this capability has been provided for you. Simply copythe file:

pyfragments.swg

to the working build directory for you project, and this problem willbe fixed. It is suggested that you do this anyway, as it onlyincreases the capabilities of your Python interface.

Why is There a Second File?

TheSWIG type checking and conversion system is a complicatedcombination of C macros,SWIG macros,SWIG typemaps andSWIGfragments. Fragments are a way to conditionally insert code into yourwrapper file if it is needed, and not insert it if not needed. Ifmultiple typemaps require the same fragment, the fragment only getsinserted into your wrapper code once.

There is a fragment for converting a Python integer to a Clong. There is a different fragment that converts a Pythoninteger to a Cint, that calls the routine defined in thelong fragment. We can make the changes we want here by changingthe definition for thelong fragment.SWIG determines theactive definition for a fragment using a “first come, first served”system. That is, we need to define the fragment forlongconversions prior toSWIG doing it internally.SWIG allows usto do this by putting our fragment definitions in the filepyfragments.swg. If we were to put the new fragment definitionsinnumpy.i, they would be ignored.

Helper Functions

Thenumpy.i file contains several macros and routines that ituses internally to build its typemaps. However, these functions maybe useful elsewhere in your interface file. These macros and routinesare implemented as fragments, which are described briefly in theprevious section. If you try to use one or more of the followingmacros or functions, but your compiler complains that it does notrecognize the symbol, then you need to force these fragments to appearin your code using:

%fragment("NumPy_Fragments");

in yourSWIG interface file.

Macros

is_array(a)
Evaluates as true ifa is non-NULL and can be cast to aPyArrayObject*.
array_type(a)
Evaluates to the integer data type code ofa, assuminga canbe cast to aPyArrayObject*.
array_numdims(a)
Evaluates to the integer number of dimensions ofa, assuminga can be cast to aPyArrayObject*.
array_dimensions(a)
Evaluates to an array of typenpy_intp and lengtharray_numdims(a), giving the lengths of all of the dimensionsofa, assuminga can be cast to aPyArrayObject*.
array_size(a,i)
Evaluates to thei-th dimension size ofa, assumingacan be cast to aPyArrayObject*.
array_strides(a)
Evaluates to an array of typenpy_intp and lengtharray_numdims(a), giving the stridess of all of the dimensionsofa, assuminga can be cast to aPyArrayObject*. Astride is the distance in bytes between an element and itsimmediate neighbor along the same axis.
array_stride(a,i)
Evaluates to thei-th stride ofa, assuminga can becast to aPyArrayObject*.
array_data(a)
Evaluates to a pointer of typevoid* that points to the databuffer ofa, assuminga can be cast to aPyArrayObject*.
array_descr(a)
Returns a borrowed reference to the dtype property(PyArray_Descr*) ofa, assuminga can be cast to aPyArrayObject*.
array_flags(a)
Returns an integer representing the flags ofa, assumingacan be cast to aPyArrayObject*.
array_enableflags(a,f)
Sets the flag represented byf ofa, assuminga can becast to aPyArrayObject*.
array_is_contiguous(a)
Evaluates as true ifa is a contiguous array. Equivalent to(PyArray_ISCONTIGUOUS(a)).
array_is_native(a)
Evaluates as true if the data buffer ofa uses native byteorder. Equivalent to(PyArray_ISNOTSWAPPED(a)).
array_is_fortran(a)
Evaluates as true ifa is FORTRAN ordered.

Routines

pytype_string()

Return type:constchar*

Arguments:

  • PyObject*py_obj, a general Python object.

Return a string describing the type ofpy_obj.

typecode_string()

Return type:constchar*

Arguments:

  • inttypecode, a NumPy integer typecode.

Return a string describing the type corresponding to the NumPytypecode.

type_match()

Return type:int

Arguments:

  • intactual_type, the NumPy typecode of a NumPy array.
  • intdesired_type, the desired NumPy typecode.

Make sure thatactual_type is compatible withdesired_type. For example, this allows character andbyte types, or int and long types, to match. This is nowequivalent toPyArray_EquivTypenums().

obj_to_array_no_conversion()

Return type:PyArrayObject*

Arguments:

  • PyObject*input, a general Python object.
  • inttypecode, the desired NumPy typecode.

Castinput to aPyArrayObject* if legal, and ensure thatit is of typetypecode. Ifinput cannot be cast, or thetypecode is wrong, set a Python error and returnNULL.

obj_to_array_allow_conversion()

Return type:PyArrayObject*

Arguments:

  • PyObject*input, a general Python object.
  • inttypecode, the desired NumPy typecode of the resultingarray.
  • int*is_new_object, returns a value of 0 if no conversionperformed, else 1.

Convertinput to a NumPy array with the giventypecode.On success, return a validPyArrayObject* with the correcttype. On failure, the Python error string will be set and theroutine returnsNULL.

make_contiguous()

Return type:PyArrayObject*

Arguments:

  • PyArrayObject*ary, a NumPy array.
  • int*is_new_object, returns a value of 0 if no conversionperformed, else 1.
  • intmin_dims, minimum allowable dimensions.
  • intmax_dims, maximum allowable dimensions.

Check to see ifary is contiguous. If so, return the inputpointer and flag it as not a new object. If it is not contiguous,create a newPyArrayObject* using the original data, flag itas a new object and return the pointer.

make_fortran()

Return type:PyArrayObject*

Arguments

  • PyArrayObject*ary, a NumPy array.
  • int*is_new_object, returns a value of 0 if no conversionperformed, else 1.

Check to see ifary is Fortran contiguous. If so, return theinput pointer and flag it as not a new object. If it is notFortran contiguous, create a newPyArrayObject* using theoriginal data, flag it as a new object and return the pointer.

obj_to_array_contiguous_allow_conversion()

Return type:PyArrayObject*

Arguments:

  • PyObject*input, a general Python object.
  • inttypecode, the desired NumPy typecode of the resultingarray.
  • int*is_new_object, returns a value of 0 if no conversionperformed, else 1.

Convertinput to a contiguousPyArrayObject* of thespecified type. If the input object is not a contiguousPyArrayObject*, a new one will be created and the new objectflag will be set.

obj_to_array_fortran_allow_conversion()

Return type:PyArrayObject*

Arguments:

  • PyObject*input, a general Python object.
  • inttypecode, the desired NumPy typecode of the resultingarray.
  • int*is_new_object, returns a value of 0 if no conversionperformed, else 1.

Convertinput to a Fortran contiguousPyArrayObject* ofthe specified type. If the input object is not a FortrancontiguousPyArrayObject*, a new one will be created and thenew object flag will be set.

require_contiguous()

Return type:int

Arguments:

  • PyArrayObject*ary, a NumPy array.

Test whetherary is contiguous. If so, return 1. Otherwise,set a Python error and return 0.

require_native()

Return type:int

Arguments:

  • PyArray_Object*ary, a NumPy array.

Require thatary is not byte-swapped. If the array is notbyte-swapped, return 1. Otherwise, set a Python error andreturn 0.

require_dimensions()

Return type:int

Arguments:

  • PyArrayObject*ary, a NumPy array.
  • intexact_dimensions, the desired number of dimensions.

Requireary to have a specified number of dimensions. If thearray has the specified number of dimensions, return 1.Otherwise, set a Python error and return 0.

require_dimensions_n()

Return type:int

Arguments:

  • PyArrayObject*ary, a NumPy array.
  • int*exact_dimensions, an array of integers representingacceptable numbers of dimensions.
  • intn, the length ofexact_dimensions.

Requireary to have one of a list of specified number ofdimensions. If the array has one of the specified number ofdimensions, return 1. Otherwise, set the Python error stringand return 0.

require_size()

Return type:int

Arguments:

  • PyArrayObject*ary, a NumPy array.
  • npy_int*size, an array representing the desired lengths ofeach dimension.
  • intn, the length ofsize.

Requireary to have a specified shape. If the array has thespecified shape, return 1. Otherwise, set the Python errorstring and return 0.

require_fortran()

Return type:int

Arguments:

  • PyArrayObject*ary, a NumPy array.

Require the givenPyArrayObject to to be Fortran ordered. IfthePyArrayObject is already Fortran ordered, do nothing.Else, set the Fortran ordering flag and recompute the strides.

Beyond the Provided Typemaps

There are many C or C++ array/NumPy array situations not covered bya simple%include"numpy.i" and subsequent%apply directives.

A Common Example

Consider a reasonable prototype for a dot product function:

doubledot(intlen,double*vec1,double*vec2);

The Python interface that we want is:

defdot(vec1,vec2):"""    dot(PyObject,PyObject) -> double    """

The problem here is that there is one dimension argument and two arrayarguments, and our typemaps are set up for dimensions that apply to asingle array (in fact,SWIG does not provide a mechanism forassociatinglen withvec2 that takes two Python inputarguments). The recommended solution is the following:

%apply (int DIM1, double* IN_ARRAY1) {(int len1, double* vec1),                                      (int len2, double* vec2)}%rename (dot) my_dot;%exception my_dot {    $action    if (PyErr_Occurred()) SWIG_fail;}%inline %{double my_dot(int len1, double* vec1, int len2, double* vec2) {    if (len1 != len2) {        PyErr_Format(PyExc_ValueError,                     "Arrays of lengths (%d,%d) given",                     len1, len2);        return 0.0;    }    return dot(len1, vec1, vec2);}%}

If the header file that contains the prototype fordoubledot()also contains other prototypes that you want to wrap, so that you needto%include this header file, then you will also need a%ignoredot; directive, placed after the%rename and before the%include directives. Or, if the function in question is a classmethod, you will want to use%extend rather than%inline inaddition to%ignore.

A note on error handling: Note thatmy_dot returns adouble but that it can also raise a Python error. Theresulting wrapper function will return a Python floatrepresentation of 0.0 when the vector lengths do not match. Sincethis is notNULL, the Python interpreter will not know to checkfor an error. For this reason, we add the%exception directiveabove formy_dot to get the behavior we want (note that$action is a macro that gets expanded to a valid call tomy_dot). In general, you will probably want to write aSWIGmacro to perform this task.

Other Situations

There are other wrapping situations in whichnumpy.i may behelpful when you encounter them.

  • In some situations, it is possible that you could use the%numpy_typemaps macro to implement typemaps for your owntypes. See theOther Common Types: bool orOther CommonTypes: complex sections for examples. Another situation is ifyour dimensions are of a type other thanint (saylong forexample):

    %numpy_typemaps(double,NPY_DOUBLE,long)
  • You can use the code innumpy.i to write your own typemaps.For example, if you had a five-dimensional array as a functionargument, you could cut-and-paste the appropriate four-dimensionaltypemaps into your interface file. The modifications for thefourth dimension would be trivial.

  • Sometimes, the best approach is to use the%extend directiveto define new methods for your classes (or overload existing ones)that take aPyObject* (that either is or can be converted to aPyArrayObject*) instead of a pointer to a buffer. In thiscase, the helper routines innumpy.i can be very useful.

  • Writing typemaps can be a bit nonintuitive. If you have specificquestions about writingSWIG typemaps for NumPy, thedevelopers ofnumpy.i do monitor theNumpy-discussion andSwig-user mail lists.

A Final Note

When you use the%apply directive, as is usually necessary to usenumpy.i, it will remain in effect until you tellSWIG that itshouldn’t be. If the arguments to the functions or methods that youare wrapping have common names, such aslength orvector,these typemaps may get applied in situations you do not expect orwant. Therefore, it is always a good idea to add a%cleardirective after you are done with a specific typemap:

%apply(double*IN_ARRAY1,intDIM1){(double*vector,intlength)}%include"my_header.h"%clear(double*vector,intlength);

In general, you should target these typemap signatures specificallywhere you want them, and then clear them after you are done.

Summary

Out of the box,numpy.i provides typemaps that support conversionbetween NumPy arrays and C arrays:

  • That can be one of 12 different scalar types:signedchar,unsignedchar,short,unsignedshort,int,unsignedint,long,unsignedlong,longlong,unsignedlonglong,float anddouble.
  • That support 74 different argument signatures for each data type,including:
    • One-dimensional, two-dimensional, three-dimensional andfour-dimensional arrays.
    • Input-only, in-place, argout, argoutview, and memory managedargoutview behavior.
    • Hard-coded dimensions, data-buffer-then-dimensionsspecification, and dimensions-then-data-buffer specification.
    • Both C-ordering (“last dimension fastest”) or Fortran-ordering(“first dimension fastest”) support for 2D, 3D and 4D arrays.

Thenumpy.i interface file also provides additional tools forwrapper developers, including:

  • ASWIG macro (%numpy_typemaps) with three arguments forimplementing the 74 argument signatures for the user’s choice of(1) C data type, (2) NumPy data type (assuming they match), and(3) dimension type.
  • Fourteen C macros and fifteen C functions that can be used towrite specialized typemaps, extensions, or inlined functions thathandle cases not covered by the provided typemaps. Note that themacros and functions are coded specifically to work with the NumPyC/API regardless of NumPy version number, both before and afterthe deprecation of some aspects of the API after version 1.6.

Table Of Contents

Previous topic

NumPy and SWIG

Next topic

Testing the numpy.i Typemaps

Quick search

  • © Copyright 2008-2018, The SciPy community.
  • Last updated on Jul 24, 2018.
  • Created usingSphinx 1.6.6.

[8]ページ先頭

©2009-2025 Movatter.jp