num_util.cpp

Go to the documentation of this file.
00001 // Copyright 2006  Phil Austin (http://www.eos.ubc.ca/personal/paustin)
00002 // Distributed under the Boost Software License, Version 1.0. (See
00003 // accompanying file LICENSE_1_0.txt or copy at
00004 // http://www.boost.org/LICENSE_1_0.txt)
00005 
00006 #define PY_ARRAY_UNIQUE_SYMBOL HippoPyArrayHandle
00007 #define NO_IMPORT_ARRAY
00008 #include "num_util.h"
00009 
00010 namespace { const char* rcsid = "$Id: num__util_8cpp-source.html,v 1.36 2007/07/26 18:21:57 pfkeb Exp $"; }
00011 
00012 using namespace boost::python;
00013 
00014 namespace num_util{
00015 
00016   //specializations for use by makeNum
00017 
00018 
00019   template <>
00020   PyArray_TYPES getEnum<unsigned char>(void)
00021   {
00022     return PyArray_UBYTE;
00023   }
00024 
00025 
00026   template <>
00027   PyArray_TYPES getEnum<signed char>(void)
00028   {
00029 #ifdef HAVE_NUMPY
00030     return PyArray_BYTE;
00031 #else
00032     return PyArray_SBYTE;
00033 #endif
00034   }
00035 
00036   template <>
00037   PyArray_TYPES getEnum<short>(void)
00038   {
00039     return PyArray_SHORT;
00040   }
00041 
00042   template <>
00043   PyArray_TYPES getEnum<unsigned short>(void)
00044   {
00045     return PyArray_USHORT;
00046   }
00047 
00048 
00049   template <>
00050   PyArray_TYPES getEnum<unsigned int>(void)
00051   {
00052     return PyArray_UINT;
00053   }
00054 
00055   template <>
00056   PyArray_TYPES getEnum<int>(void)
00057   {
00058     return PyArray_INT;
00059   }
00060 
00061   template <>
00062   PyArray_TYPES getEnum<long>(void)
00063   {
00064     return PyArray_LONG;
00065   }
00066 
00067 #ifdef HAVE_NUMPY
00068   template <>
00069   PyArray_TYPES getEnum<unsigned long>(void)
00070   {
00071     return PyArray_ULONG;
00072   }
00073 
00074   template <>
00075   PyArray_TYPES getEnum<long long>(void)
00076   {
00077     return PyArray_LONGLONG;
00078   }
00079 
00080   template <>
00081   PyArray_TYPES getEnum<unsigned long long>(void)
00082   {
00083     return PyArray_ULONGLONG;
00084   }
00085 #endif
00086 
00087   template <>
00088   PyArray_TYPES getEnum<float>(void)
00089   {
00090     return PyArray_FLOAT;
00091   }
00092 
00093   template <>
00094   PyArray_TYPES getEnum<double>(void)
00095   {
00096     return PyArray_DOUBLE;
00097   }
00098 
00099 #ifdef HAVE_NUMPY    
00100   template <>
00101   PyArray_TYPES getEnum<long double>(void)
00102   {
00103     return PyArray_LONGDOUBLE;
00104   }
00105 #endif
00106   template <>
00107   PyArray_TYPES getEnum<std::complex<float> >(void)
00108   {
00109     return PyArray_CFLOAT;
00110   }
00111 
00112 
00113   template <>
00114   PyArray_TYPES getEnum<std::complex<double> >(void)
00115   {
00116     return PyArray_CDOUBLE;
00117   }
00118 
00119 #ifdef HAVE_NUMPY
00120   template <>
00121   PyArray_TYPES getEnum<std::complex<long double> >(void)
00122   {
00123     return PyArray_CLONGDOUBLE;
00124   }
00125 #endif
00126 
00127   template <> boost::python::numeric::array makeNum<double> (double * data, std::vector<int> dims){
00128     intp total = std::accumulate(dims.begin(),dims.end(),1,std::multiplies<intp>());
00129     boost::python::object obj(boost::python::handle<>(PyArray_FromDims(dims.size(),&dims[0], PyArray_DOUBLE)));
00130 #ifdef HAVE_NUMPY
00131     void *arr_data = PyArray_DATA((PyArrayObject*) obj.ptr());
00132     memcpy(arr_data, data, PyArray_ITEMSIZE((PyArrayObject*) obj.ptr()) * total);
00133 #else
00134   char *arr_data = ((PyArrayObject*) obj.ptr())->data; 
00135   memcpy(arr_data, data, sizeof(double) * total); // copies the input data to 
00136                                            // PyArrayObject->data
00137 #endif
00138     return boost::python::extract<boost::python::numeric::array>(obj);
00139   }
00140 
00141 
00142 typedef KindStringMap::value_type  KindStringMapEntry;
00143 KindStringMapEntry kindStringMapEntries[] =
00144   {
00145     KindStringMapEntry(PyArray_UBYTE,  "PyArray_UBYTE"),
00146 #ifdef HAVE_NUMPY
00147     KindStringMapEntry(PyArray_BYTE,   "PyArray_BYTE"),
00148 #else
00149     KindStringMapEntry(PyArray_SBYTE,   "PyArray_SBYTE"),
00150 #endif
00151     KindStringMapEntry(PyArray_SHORT,  "PyArray_SHORT"),
00152     KindStringMapEntry(PyArray_INT,    "PyArray_INT"),
00153     KindStringMapEntry(PyArray_LONG,   "PyArray_LONG"),
00154     KindStringMapEntry(PyArray_FLOAT,  "PyArray_FLOAT"),
00155     KindStringMapEntry(PyArray_DOUBLE, "PyArray_DOUBLE"),
00156     KindStringMapEntry(PyArray_CFLOAT, "PyArray_CFLOAT"),
00157     KindStringMapEntry(PyArray_CDOUBLE,"PyArray_CDOUBLE"),
00158     KindStringMapEntry(PyArray_OBJECT, "PyArray_OBJECT"),
00159     KindStringMapEntry(PyArray_NTYPES, "PyArray_NTYPES"),
00160     KindStringMapEntry(PyArray_NOTYPE ,"PyArray_NOTYPE")
00161   };
00162 
00163 typedef KindCharMap::value_type  KindCharMapEntry;
00164 KindCharMapEntry kindCharMapEntries[] =
00165   {
00166     KindCharMapEntry(PyArray_UBYTE,  'B'),
00167 #ifdef HAVE_NUMPY
00168     KindCharMapEntry(PyArray_BYTE,   'b'),
00169 #else
00170     KindCharMapEntry(PyArray_SBYTE,   'b'),
00171 #endif
00172     KindCharMapEntry(PyArray_SHORT,  'h'),
00173     KindCharMapEntry(PyArray_INT,    'i'),
00174     KindCharMapEntry(PyArray_LONG,   'l'),
00175     KindCharMapEntry(PyArray_FLOAT,  'f'),
00176     KindCharMapEntry(PyArray_DOUBLE, 'd'),
00177     KindCharMapEntry(PyArray_CFLOAT, 'F'),
00178     KindCharMapEntry(PyArray_CDOUBLE,'D'),
00179     KindCharMapEntry(PyArray_OBJECT, 'O')
00180   };
00181   
00182 typedef KindTypeMap::value_type  KindTypeMapEntry;
00183 KindTypeMapEntry kindTypeMapEntries[] =
00184   {
00185     KindTypeMapEntry('B',PyArray_UBYTE),
00186 #ifdef HAVE_NUMPY
00187     KindTypeMapEntry('b',PyArray_BYTE),
00188 #else
00189     KindTypeMapEntry('b',PyArray_SBYTE),
00190 #endif
00191     KindTypeMapEntry('h',PyArray_SHORT),
00192     KindTypeMapEntry('i',PyArray_INT),
00193     KindTypeMapEntry('l',PyArray_LONG),
00194     KindTypeMapEntry('f',PyArray_FLOAT),
00195     KindTypeMapEntry('d',PyArray_DOUBLE),
00196     KindTypeMapEntry('F',PyArray_CFLOAT),
00197     KindTypeMapEntry('D',PyArray_CDOUBLE),
00198     KindTypeMapEntry('O',PyArray_OBJECT)
00199   };
00200 
00201   
00202 int numStringEntries = sizeof(kindStringMapEntries)/sizeof(KindStringMapEntry);
00203 int numCharEntries = sizeof(kindCharMapEntries)/sizeof(KindCharMapEntry);
00204 int numTypeEntries = sizeof(kindTypeMapEntries)/sizeof(KindTypeMapEntry);
00205 
00206 
00207 using namespace boost::python;
00208   
00209 static KindStringMap kindstrings(kindStringMapEntries,
00210                                    kindStringMapEntries + numStringEntries);
00211 
00212 static KindCharMap kindchars(kindCharMapEntries,
00213                                    kindCharMapEntries + numCharEntries);
00214 
00215 static KindTypeMap kindtypes(kindTypeMapEntries,
00216                                    kindTypeMapEntries + numTypeEntries);
00217 
00218 //Create a numarray referencing Python sequence object
00219 numeric::array makeNum(object x){
00220   if (!PySequence_Check(x.ptr())){
00221     PyErr_SetString(PyExc_ValueError, "expected a sequence");
00222     throw_error_already_set();
00223   }
00224   object obj(handle<>
00225              (PyArray_ContiguousFromObject(x.ptr(),PyArray_NOTYPE,0,0)));
00226   check_PyArrayElementType(obj);
00227   return extract<numeric::array>(obj); 
00228 }
00229 
00230 //Create a one-dimensional Numeric array of length n and Numeric type t
00231 numeric::array makeNum(int n, PyArray_TYPES t=PyArray_DOUBLE){
00232   object obj(handle<>(PyArray_FromDims(1, &n, t)));
00233   return extract<numeric::array>(obj);
00234 }
00235   
00236 //Create a Numeric array with dimensions dimens and Numeric type t
00237 numeric::array makeNum(std::vector<int> dimens, 
00238                        PyArray_TYPES t=PyArray_DOUBLE){
00239   object obj(handle<>(PyArray_FromDims(dimens.size(), &dimens[0], t)));
00240   return extract<numeric::array>(obj);
00241 }
00242 
00243 numeric::array makeNum(const numeric::array& arr){
00244   //Returns a reference of arr by calling numeric::array copy constructor.
00245   //The copy constructor increases arr's reference count.
00246   return numeric::array(arr);
00247 } 
00248 
00249 PyArray_TYPES type(numeric::array arr){
00250 #ifdef HAVE_NUMPY
00251   return PyArray_TYPES(PyArray_TYPE(arr.ptr()));
00252 #else
00253   return char2type(arr.typecode());
00254 #endif
00255 }
00256 
00257 void check_type(boost::python::numeric::array arr, 
00258                 PyArray_TYPES expected_type){
00259   PyArray_TYPES actual_type = type(arr);
00260   if (actual_type != expected_type) {
00261     std::ostringstream stream;
00262     stream << "expected Numeric type " << kindstrings[expected_type]
00263            << ", found Numeric type " << kindstrings[actual_type] << std::ends;
00264     PyErr_SetString(PyExc_TypeError, stream.str().c_str());
00265     throw_error_already_set();
00266   }
00267   return;
00268 }
00269 
00270 //Return the number of dimensions
00271 int rank(numeric::array arr){
00272   //std::cout << "inside rank" << std::endl;
00273   if(!PyArray_Check(arr.ptr())){
00274     PyErr_SetString(PyExc_ValueError, "expected a PyArrayObject");
00275     throw_error_already_set();
00276   }
00277 #ifdef HAVE_NUMPY
00278   return PyArray_NDIM(arr.ptr());
00279 #else
00280   return ((PyArrayObject*) arr.ptr())->nd;
00281 #endif
00282 }
00283 
00284 void check_rank(boost::python::numeric::array arr, int expected_rank){
00285   int actual_rank = rank(arr);
00286   if (actual_rank != expected_rank) {
00287     std::ostringstream stream;
00288     stream << "expected rank " << expected_rank 
00289            << ", found rank " << actual_rank << std::ends;
00290     PyErr_SetString(PyExc_RuntimeError, stream.str().c_str());
00291     throw_error_already_set();
00292   }
00293   return;
00294 }
00295 
00296 intp size(numeric::array arr)
00297 {
00298   if(!PyArray_Check(arr.ptr())){
00299     PyErr_SetString(PyExc_ValueError, "expected a PyArrayObject");
00300     throw_error_already_set();
00301   }
00302   return PyArray_Size(arr.ptr());
00303 }
00304 
00305 void check_size(boost::python::numeric::array arr, intp expected_size){
00306   intp actual_size = size(arr);
00307   if (actual_size != expected_size) {
00308     std::ostringstream stream;
00309     stream << "expected size " << expected_size 
00310            << ", found size " << actual_size << std::ends;
00311     PyErr_SetString(PyExc_RuntimeError, stream.str().c_str());
00312     throw_error_already_set();
00313   }
00314   return;
00315 }
00316 
00317 std::vector<intptr_t> shape(numeric::array arr){
00318   std::vector<intptr_t> out_dims;
00319   if(!PyArray_Check(arr.ptr())){
00320     PyErr_SetString(PyExc_ValueError, "expected a PyArrayObject");
00321     throw_error_already_set();
00322   }
00323 #ifdef HAVE_NUMPY
00324   intp* dims_ptr = PyArray_DIMS(arr.ptr());
00325 #else
00326   int* dims_ptr = ((PyArrayObject*) arr.ptr())->dimensions;
00327 #endif
00328   int the_rank = rank(arr);
00329   for (int i = 0; i < the_rank; i++){
00330     out_dims.push_back(*(dims_ptr + i));
00331   }
00332   return out_dims;
00333 }
00334 
00335 intp get_dim(boost::python::numeric::array arr, int dimnum){
00336   assert(dimnum >= 0);
00337   int the_rank=rank(arr);
00338   if(the_rank < dimnum){
00339     std::ostringstream stream;
00340     stream << "Error: asked for length of dimension ";
00341     stream << dimnum << " but rank of array is " << the_rank << std::ends;
00342     PyErr_SetString(PyExc_RuntimeError, stream.str().c_str());       
00343     throw_error_already_set();
00344   }
00345   std::vector<intptr_t> actual_dims = shape(arr);
00346   return actual_dims[dimnum];
00347 }
00348 
00349 void check_shape(boost::python::numeric::array arr, std::vector<intptr_t> expected_dims){
00350   std::vector<intptr_t> actual_dims = shape(arr);
00351   if (actual_dims != expected_dims) {
00352     std::ostringstream stream;
00353     stream << "expected dimensions " << vector_str(expected_dims)
00354            << ", found dimensions " << vector_str(actual_dims) << std::ends;
00355     PyErr_SetString(PyExc_RuntimeError, stream.str().c_str());
00356     throw_error_already_set();
00357   }
00358   return;
00359 }
00360 
00361 void check_dim(boost::python::numeric::array arr, int dimnum, intp dimsize){
00362   std::vector<intptr_t> actual_dims = shape(arr);
00363   if(actual_dims[dimnum] != dimsize){
00364     std::ostringstream stream;
00365     stream << "Error: expected dimension number ";
00366     stream << dimnum << " to be length " << dimsize;
00367     stream << ", but found length " << actual_dims[dimnum]  << std::ends;
00368     PyErr_SetString(PyExc_RuntimeError, stream.str().c_str());       
00369     throw_error_already_set();
00370   }
00371   return;
00372 }
00373 
00374 bool iscontiguous(numeric::array arr)
00375 {
00376   //  return arr.iscontiguous();
00377   return PyArray_ISCONTIGUOUS(arr.ptr());
00378 }
00379 
00380 void check_contiguous(numeric::array arr)
00381 {
00382   if (!iscontiguous(arr)) {
00383     PyErr_SetString(PyExc_RuntimeError, "expected a contiguous array");
00384     throw_error_already_set();
00385   }
00386   return;
00387 }
00388 
00389 void* data(numeric::array arr){
00390   if(!PyArray_Check(arr.ptr())){
00391     PyErr_SetString(PyExc_ValueError, "expected a PyArrayObject");
00392     throw_error_already_set();
00393   }
00394 #ifdef HAVE_NUMPY
00395   return PyArray_DATA(arr.ptr());
00396 #else
00397   return ((PyArrayObject*) arr.ptr())->data;
00398 #endif
00399 }
00400 
00401 //Copy data into the array
00402 void copy_data(boost::python::numeric::array arr, char* new_data){
00403   char* arr_data = (char*) data(arr);
00404   intp nbytes = PyArray_NBYTES(arr.ptr());
00405   for (intp i = 0; i < nbytes; i++) {
00406     arr_data[i] = new_data[i];
00407   }
00408   return;
00409 } 
00410 
00411 //Return a clone of this array
00412 numeric::array clone(numeric::array arr){
00413 #ifdef HAVE_NUMPY
00414   object obj(handle<>(PyArray_NewCopy((PyArrayObject*)arr.ptr(),PyArray_CORDER)));
00415 #else
00416   object obj(handle<>(PyArray_Copy((PyArrayObject*)arr.ptr())));
00417 #endif
00418   return makeNum(obj);
00419 }
00420 
00421   
00422 //Return a clone of this array with a new type
00423 numeric::array astype(boost::python::numeric::array arr, PyArray_TYPES t){
00424   return (numeric::array) arr.astype(type2char(t));
00425 }
00426 
00427 std::vector<intp> strides(numeric::array arr){
00428   std::vector<intp> out_strides;
00429   if(!PyArray_Check(arr.ptr())){
00430     PyErr_SetString(PyExc_ValueError, "expected a PyArrayObject");
00431     throw_error_already_set();
00432   }
00433 #ifdef HAVE_NUMPY
00434   intp* strides_ptr = PyArray_STRIDES(arr.ptr());
00435 #else
00436   int* strides_ptr = ((PyArrayObject*) arr.ptr())->strides;
00437 #endif
00438   intp the_rank = rank(arr);
00439   for (intp i = 0; i < the_rank; i++){
00440     out_strides.push_back(*(strides_ptr + i));
00441   }
00442   return out_strides;
00443 }
00444 
00445 int refcount(numeric::array arr){
00446   return REFCOUNT(arr.ptr());
00447 }
00448 
00449 void check_PyArrayElementType(object newo){
00450 #ifdef HAVE_NUMPY
00451   PyArray_TYPES theType=PyArray_TYPES(PyArray_TYPE(newo.ptr()));
00452 #else
00453   PyArrayObject* PyArrayPtr = (PyArrayObject*) newo.ptr();
00454   PyArray_TYPES theType=(PyArray_TYPES) PyArrayPtr->descr->type_num;
00455 #endif
00456   if(theType == PyArray_OBJECT){
00457       std::ostringstream stream;
00458       stream << "array elments have been cast to PyArray_OBJECT, "
00459              << "numhandle can only accept arrays with numerical elements" 
00460              << std::ends;
00461       PyErr_SetString(PyExc_TypeError, stream.str().c_str());
00462       throw_error_already_set();
00463   }
00464   return;
00465 }
00466 
00467 std::string type2string(PyArray_TYPES t_type){
00468   return kindstrings[t_type];
00469 }
00470 
00471 char type2char(PyArray_TYPES t_type){
00472   return kindchars[t_type];
00473 }
00474 
00475 PyArray_TYPES char2type(char e_type){
00476   return kindtypes[e_type];
00477 }
00478 
00479 template <class T>
00480 inline std::string vector_str(const std::vector<T>& vec)
00481 {
00482   std::ostringstream stream;
00483   stream << "(" << vec[0];
00484 
00485   for(std::size_t i = 1; i < vec.size(); i++){
00486     stream << ", " << vec[i];
00487   }
00488   stream << ")";
00489   return stream.str();
00490 }
00491 
00492 inline void check_size_match(std::vector<intp> dims, intp n)
00493 {
00494   intp total = std::accumulate(dims.begin(),dims.end(),1,std::multiplies<intp>());
00495   if (total != n) {
00496     std::ostringstream stream;
00497     stream << "expected array size " << n
00498            << ", dimensions give array size " << total << std::ends;
00499     PyErr_SetString(PyExc_TypeError, stream.str().c_str());
00500     throw_error_already_set();
00501   }
00502   return;
00503 }
00504 
00505 } //namespace num_util
00506 

Generated for HippoDraw Class Library by doxygen