changeset 51:b001edc0f81a

implement Python->Octave conversions of Character and PyObject arrays
author Jaroslav Hajek <highegg@gmail.com>
date Wed, 03 Jun 2009 16:41:44 +0200
parents 2a2f1e2f2be3 (current diff) 22e74c46bea2 (diff)
children a5beba1b4611 57f243330695 43a413b7c151
files ChangeLog python_to_octave.cc test/test.py
diffstat 4 files changed, 107 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Thu May 28 11:20:17 2009 +0200
+++ b/ChangeLog	Wed Jun 03 16:41:44 2009 +0200
@@ -1,3 +1,21 @@
+2009-06-03  Jaroslav Hajek  <highegg@gmail.com>
+
+	* python_to_octave.cc (copy_pyarrobj_to_octarray<PyObject *,
+	Cell>): New template specialization.
+	(matching_type<PyObject *, octave_value>): Ditto.
+	(copy_pyarrobj_to_octarray_boot): Include PyArray_CHAR
+	and PyArray_Object cases.
+	(pyarr_to_octvalue): Likewise.
+	* test/test.py: Add tests for the new conversions.
+
+2009-06-03  David Grundberg  <individ@acc.umu.se>
+
+	* octave_to_python.cc (create_uint_array, create_sint_array):
+	Prefer int to other datatypes of identical size.
+	(is_1xn_or_0x0): Whitespace.
+	* python_to_octave.cc (copy_pyarrobj_to_octarray_dispatch): Prefer
+	int to other datatypes of identical size.
+
 2009-05-28  Jaroslav Hajek  <highegg@gmail.com>
 
 	* configure.ac: Remove --enable-float-matrices option.
--- a/octave_to_python.cc	Thu May 28 11:20:17 2009 +0200
+++ b/octave_to_python.cc	Wed Jun 03 16:41:44 2009 +0200
@@ -117,13 +117,14 @@
    template <class CLASS, size_t bytes>
    inline static PyArrayObject *create_uint_array(CLASS value) {
       if (bytes == sizeof(int)) {
-         boost::integral_constant<bool, bytes==sizeof(int)> inst;
+         boost::integral_constant<bool, bytes == sizeof(int)> inst;
          return create_array<unsigned int, CLASS>(value, PyArray_UINT, inst);
       } else if (bytes == sizeof(char)) {
-         boost::integral_constant<bool, bytes==sizeof(char)> inst;
+         boost::integral_constant<bool, bytes == sizeof(char)> inst;
          return create_array<unsigned char, CLASS>(value, PyArray_UBYTE, inst);
       } else if (bytes == sizeof(short)) {
-         boost::integral_constant<bool, bytes==sizeof(short)> inst;
+         boost::integral_constant<bool,
+            bytes == sizeof(short) && bytes != sizeof(int)> inst;
          return create_array<unsigned short, CLASS>(value, PyArray_USHORT, inst);
       } else {
          ostringstream os;
@@ -135,17 +136,21 @@
 
    template <class CLASS, size_t bytes>
    inline static PyArrayObject *create_sint_array(CLASS value) {
-      if (bytes == sizeof(long)) {
-         boost::integral_constant<bool, bytes==sizeof(long)> inst;
+      if (bytes == sizeof(int)) {
+         // We test int first since we prefer int to other datatypes of the
+         // same size.
+         boost::integral_constant<bool, bytes == sizeof(int)> inst;
+         return create_array<signed int, CLASS>(value, PyArray_INT, inst);
+      } else if (bytes == sizeof(long)) {
+         boost::integral_constant<bool,
+            bytes==sizeof(long) && bytes != sizeof(int)> inst;
          return create_array<long, CLASS>(value, PyArray_LONG, inst);
-      } else if (bytes == sizeof(int)) {
-         boost::integral_constant<bool, bytes==sizeof(int)> inst;
-         return create_array<signed int, CLASS>(value, PyArray_INT, inst);
       } else if (bytes == sizeof(char)) {
-         boost::integral_constant<bool, bytes==sizeof(char)> inst;
+         boost::integral_constant<bool, bytes == sizeof(char)> inst;
          return create_array<signed char, CLASS>(value, PyArray_SBYTE, inst);
       } else if (bytes == sizeof(short)) {
-         boost::integral_constant<bool, bytes==sizeof(short)> inst;
+         boost::integral_constant<bool,
+            bytes==sizeof(short) && bytes != sizeof(int)> inst;
          return create_array<signed short, CLASS>(value, PyArray_SHORT, inst);
       } else {
          ostringstream os;
@@ -218,7 +223,7 @@
    }
 
    static inline bool is_1xn_or_0x0(const dim_vector& dv) {
-         return (dv.length() == 2 && (dv(0) == 1 || (dv(0) == 0 && dv(1) == 0)));
+      return (dv.length() == 2 && (dv(0) == 1 || (dv(0) == 0 && dv(1) == 0)));
    }
 
    static void octcell_to_pyobject(boost::python::object &py_object,
--- a/python_to_octave.cc	Thu May 28 11:20:17 2009 +0200
+++ b/python_to_octave.cc	Wed Jun 03 16:41:44 2009 +0200
@@ -69,6 +69,35 @@
       }
    }
 
+   template <>
+   void copy_pyarrobj_to_octarray<PyObject *, Cell>(Cell &matrix,
+                                  const PyArrayObject* const pyarr,
+                                  const int unsigned matindex,
+                                  const unsigned int matstride,
+                                  const int dimension,
+                                  const unsigned int offset) {
+      unsigned char *ptr = (unsigned char*) pyarr->data;
+      if (dimension == pyarr->nd - 1) {
+         // Last dimension, base case
+         for (int i = 0; i < pyarr->dimensions[dimension]; i++) {
+            PyObject *pobj = *(PyObject **)
+               &ptr[offset + i*pyarr->strides[dimension]];
+            pyobj_to_octvalue (matrix.elem(matindex + i*matstride), 
+                               object(handle<PyObject> (borrowed (pobj))));
+         }
+      } else {
+         for (int i = 0; i < pyarr->dimensions[dimension]; i++) {
+            copy_pyarrobj_to_octarray<PyObject *, Cell>(
+               matrix,
+               pyarr,
+               matindex + i*matstride,
+               matstride * pyarr->dimensions[dimension],
+               dimension + 1,
+               offset + i*pyarr->strides[dimension]);
+         }
+      }
+   }
+
    template <class PythonPrimitive, class OctaveBase>
    static void copy_pyarrobj_to_octarray_dispatch(OctaveBase &matrix,
                                        const PyArrayObject* const pyarr,
@@ -89,6 +118,7 @@
    template <class X> class matching_type<X, octave_int<X> > : public boost::true_type { };
    template <> class matching_type<float, double> : public boost::true_type { };
    template <> class matching_type<FloatComplex, Complex> : public boost::true_type { };
+   template <> class matching_type<PyObject *, octave_value> : public boost::true_type { };
 
    template <class PythonPrimitive, class OctaveBase>
    static void copy_pyarrobj_to_octarray_dispatch(OctaveBase &matrix,
@@ -106,8 +136,29 @@
          (matrix, pyarr); \
          break; \
 
-      switch (pyarr->descr->type_num) {
-//         ARRAYCASE(PyArray_CHAR, )
+      // Prefer int to other types of the same size.
+      // E.g. on 32-bit x86 architectures: sizeof(long) == sizeof(int).
+      int type_num = pyarr->descr->type_num;
+      switch (type_num) {
+         case PyArray_LONG:
+            if (sizeof(long) == sizeof(int)) {
+               type_num = PyArray_INT;
+            }
+            break;
+         case PyArray_SHORT:
+            if (sizeof(short) == sizeof(int)) {
+               type_num = PyArray_INT;
+            }
+            break;
+         case PyArray_USHORT:
+            if (sizeof(unsigned short) == sizeof(unsigned int)) {
+               type_num = PyArray_UINT;
+            }
+            break;
+      }
+
+      switch (type_num) {
+         ARRAYCASE(PyArray_CHAR,            char)
          ARRAYCASE(PyArray_UBYTE,  unsigned char)
          ARRAYCASE(PyArray_SBYTE,  signed   char)
          ARRAYCASE(PyArray_SHORT,  signed   short)
@@ -127,7 +178,9 @@
 
          /* Commonly Numeric.array(..., Numeric.Complex) */
          ARRAYCASE(PyArray_CDOUBLE, Complex)
-//         ARRAYCASE(PyArray_OBJECT, )
+
+         ARRAYCASE(PyArray_OBJECT, PyObject *)
+
          default:
             throw object_convert_exception(
                PyEval_GetFuncName((PyObject*)pyarr)
@@ -213,6 +266,14 @@
          case PyArray_CDOUBLE:
             pyarrobj_to_octvalueNd<ComplexNDArray>(octvalue, pyarr, dims);
             break;
+         case PyArray_CHAR:
+            pyarrobj_to_octvalueNd<charNDArray>(octvalue, pyarr, dims);
+            // FIXME: is the following needed?
+            octvalue = octvalue.convert_to_str(true, true, '"');
+            break;
+         case PyArray_OBJECT:
+            pyarrobj_to_octvalueNd<Cell>(octvalue, pyarr, dims);
+            break;
          default:
             throw object_convert_exception(
                PyEval_GetFuncDesc((PyObject*)(pyarr)) + string(" ")
@@ -287,11 +348,6 @@
             if (error_state)
                throw object_convert_exception("Octave error");
 
-            // Some things are assumed since we have converted a Python list to
-            // a cell.
-            assert(c.dims().length() == 2);
-            assert(c.dim1() == 1);
-
             // We do not bother measuring 1x1 values, since they are replicated
             // to fill up the necessary dimensions.
             if(!(c.dims().length() == 2 && c.dims()(0) == 1 && c.dims()(1) == 1)) {
--- a/test/test.py	Thu May 28 11:20:17 2009 +0200
+++ b/test/test.py	Wed Jun 03 16:41:44 2009 +0200
@@ -22,6 +22,10 @@
 arr3f = Numeric.array([[[1.32, 2, 3, 4],[5,6,7,8]],[[9, 10, 11, 12],[13,14,15,16]]], Numeric.Float32)
 arr1c = Numeric.array([[1+2j, 3+4j, 5+6j, 7+0.5j]], Numeric.Complex)
 arr1fc = Numeric.array([[1+2j, 3+4j, 5+6j, 7+0.5j]], Numeric.Complex32)
+arr1o = Numeric.array([["abc",1.0,2+3j]],Numeric.PyObject)
+arr2o = Numeric.array([["abc",1.0,2+3j],[4.0,arr1i,"def"]],Numeric.PyObject)
+arr1ch = Numeric.array(["abc"],Numeric.Character)
+arr2ch = Numeric.array(["abc","def"],Numeric.Character)
 
 alimit_int32 = Numeric.array([[-2147483648, 2147483647]], Numeric.Int32);
 alimit_int16 = Numeric.array([[-32768, 32767, -32769, 32768]], Numeric.Int16);
@@ -270,3 +274,8 @@
 testsetget("xxx", [1,2,3])
 
 testlocalscope(5)
+
+# Try converting Numeric arrays of objects and characters
+
+testexpect(arr1o,arr1o[0].tolist())
+testexpect(arr1ch,arr1ch.tostring())