# HG changeset patch # User Mike Miller # Date 1471493998 25200 # Node ID eac35d84ef0d1ece3602d4ee84d32554876b4f58 # Parent 040aff46e4dbe111cca0853e8f3c698c9d02a2b2 Convert Octave numeric vectors into Python array.array * oct-py-types.cc, oct-py-types.h (pytave::make_py_array): New functions to convert an Octave numeric array into Python array object of appropriate type. * octave_to_python.cc (pytave::octvalue_to_pyobj): Add numeric vector case. * pycall.cc: Delete %!tests that relied on conversion to numpy array. diff -r 040aff46e4db -r eac35d84ef0d oct-py-types.cc --- a/oct-py-types.cc Wed Aug 17 14:02:33 2016 -0700 +++ b/oct-py-types.cc Wed Aug 17 21:19:58 2016 -0700 @@ -29,6 +29,7 @@ #include #include "exceptions.h" +#include "oct-py-eval.h" #include "oct-py-types.h" // FIXME: only here to bootstrap nested conversions needed in this file @@ -134,6 +135,112 @@ } PyObject * +make_py_array (const void *data, size_t len, char typecode) +{ + if (! typecode) + throw object_convert_exception ("unable to create array from Octave data"); + + std::string arg { typecode }; + PyObject *array = py_call_function ("array.array", ovl (arg)); + + if (len > 0) + { + // create a byte buffer containing a copy of the array binary data + const char *cdata = reinterpret_cast (data); + PyObject *buf = PyBytes_FromStringAndSize (cdata, len); + if (! buf) + octave_throw_bad_alloc (); + + PyObject *frombytes = (PyObject_HasAttrString (array, "frombytes") ? + PyObject_GetAttrString (array, "frombytes") : + PyObject_GetAttrString (array, "fromstring")); + PyObject *args = PyTuple_Pack (1, buf); + py_call_function (frombytes, args); + Py_DECREF (args); + Py_DECREF (buf); + } + + return array; +} + +// Prefer the 'q' and 'Q' typecodes if they are available (if Python 3 and +// built with support for long long integers) + +#if (PY_VERSION_HEX >= 0x03000000) && defined (HAVE_LONG_LONG) +# define ARRAY_INT64_TYPECODE 'q' +# define ARRAY_UINT64_TYPECODE 'Q' +#elif (SIZEOF_LONG == 8) +# define ARRAY_INT64_TYPECODE 'l' +# define ARRAY_UINT64_TYPECODE 'L' +#else +# define ARRAY_INT64_TYPECODE 0 +# define ARRAY_UINT64_TYPECODE 0 +#endif + +template +struct py_array_info { }; + +template <> +struct py_array_info { static const char typecode = 'b'; }; + +template <> +struct py_array_info { static const char typecode = 'h'; }; + +template <> +struct py_array_info { static const char typecode = 'i'; }; + +template <> +struct py_array_info +{ + static const char typecode = ARRAY_INT64_TYPECODE; +}; + +template <> +struct py_array_info { static const char typecode = 'B'; }; + +template <> +struct py_array_info { static const char typecode = 'H'; }; + +template <> +struct py_array_info { static const char typecode = 'I'; }; + +template <> +struct py_array_info { + static const char typecode = ARRAY_UINT64_TYPECODE; +}; + +PyObject * +make_py_array (const NDArray& nda) +{ + return make_py_array (nda.data (), nda.numel () * sizeof (double), 'd'); +} + +PyObject * +make_py_array (const FloatNDArray& nda) +{ + return make_py_array (nda.data (), nda.numel () * sizeof (float), 'f'); +} + +template +PyObject * +make_py_array (const intNDArray& nda) +{ + return make_py_array (nda.data (), nda.numel () * sizeof (T), + py_array_info::typecode); +} + +// Instantiate all possible integer array template functions needed + +template PyObject * make_py_array (const int8NDArray&); +template PyObject * make_py_array (const int16NDArray&); +template PyObject * make_py_array (const int32NDArray&); +template PyObject * make_py_array (const int64NDArray&); +template PyObject * make_py_array (const uint8NDArray&); +template PyObject * make_py_array (const uint16NDArray&); +template PyObject * make_py_array (const uint32NDArray&); +template PyObject * make_py_array (const uint64NDArray&); + +PyObject * make_py_numeric_value (const octave_value& value) { if (value.is_scalar_type ()) @@ -169,6 +276,40 @@ return 0; } +PyObject * +make_py_array (const octave_value& value) +{ + if (value.is_numeric_type () && ! value.is_complex_type () + && value.ndims () == 2 && (value.columns () == 1 || value.rows () == 1)) + { + if (value.is_double_type ()) + return make_py_array (value.array_value ()); + else if (value.is_single_type ()) + return make_py_array (value.float_array_value ()); + + else if (value.is_int8_type ()) + return make_py_array (value.int8_array_value ()); + else if (value.is_int16_type ()) + return make_py_array (value.int16_array_value ()); + else if (value.is_int32_type ()) + return make_py_array (value.int32_array_value ()); + else if (value.is_int64_type ()) + return make_py_array (value.int64_array_value ()); + + else if (value.is_uint8_type ()) + return make_py_array (value.uint8_array_value ()); + else if (value.is_uint16_type ()) + return make_py_array (value.uint16_array_value ()); + else if (value.is_uint32_type ()) + return make_py_array (value.uint32_array_value ()); + else if (value.is_uint64_type ()) + return make_py_array (value.uint64_array_value ()); + } + + throw value_convert_exception ("unhandled Octave numeric vector type"); + return 0; +} + inline PyObject * wrap_octvalue_to_pyobj (const octave_value& value) { diff -r 040aff46e4db -r eac35d84ef0d oct-py-types.h --- a/oct-py-types.h Wed Aug 17 14:02:33 2016 -0700 +++ b/oct-py-types.h Wed Aug 17 21:19:58 2016 -0700 @@ -28,6 +28,9 @@ #include class Cell; +class FloatNDArray; +class NDArray; +template class intNDArray; class octave_scalar_map; class octave_value; @@ -128,6 +131,41 @@ PyObject * make_py_int (uint64_t value); +//! Create a Python array object with the value of the given Octave array. +//! +//! @param nda array value +//! @return Python array object +PyObject * +make_py_array (const NDArray& nda); + +//! Create a Python array object with the value of the given Octave array. +//! +//! @param nda array value +//! @return Python array object +PyObject * +make_py_array (const FloatNDArray& nda); + +//! Create a Python array object with the value of the given Octave array. +//! +//! @param nda array value +//! @return Python array object +template +PyObject * +make_py_array (const intNDArray& nda); + +//! Create a Python array object from the given Octave numeric vector. +//! +//! All Octave real floating point and integer values are converted to +//! corresponding Python array types by this function. +//! +//! @warning Depending on the version of Python and how it is configured, +//! @c int64 and @c uint64 vectors may not be supported. +//! +//! @param value Octave numeric or boolean scalar value +//! @return Python array object +PyObject * +make_py_array (const octave_value& value); + //! Create a Python tuple object from the given Octave cell array value. //! //! The values contained in the cell array are recursively converted to diff -r 040aff46e4db -r eac35d84ef0d octave_to_python.cc --- a/octave_to_python.cc Wed Aug 17 14:02:33 2016 -0700 +++ b/octave_to_python.cc Wed Aug 17 21:19:58 2016 -0700 @@ -178,6 +178,12 @@ PyObject *obj = make_py_tuple (octvalue.cell_value ()); py_object = object (handle (obj)); } + else if (octvalue.is_numeric_type () && octvalue.ndims () == 2 + && (octvalue.columns () == 1 || octvalue.rows () == 1)) + { + PyObject *obj = make_py_array (octvalue); + py_object = object (handle (obj)); + } else if (octvalue.is_numeric_type () || octvalue.is_string () || octvalue.is_bool_type ()) octvalue_to_pyarr (py_object, octvalue); diff -r 040aff46e4db -r eac35d84ef0d pycall.cc --- a/pycall.cc Wed Aug 17 14:02:33 2016 -0700 +++ b/pycall.cc Wed Aug 17 21:19:58 2016 -0700 @@ -193,8 +193,7 @@ ## Test round trip type preservation / conversion %!test %! pyexec ("def roundtrip(x): return x"); -%! values = { 0, pi, 2j, eps, false, true, version, "Hello world", ... -%! [1, 2, 3], eye (4) }; +%! values = { 0, pi, 2j, eps, false, true, version, "Hello world" }; %! for i = 1:numel (values) %! assert (pycall ("roundtrip", values{i}), values{i}); %! endfor @@ -248,11 +247,6 @@ %!assert (isa (pycall ("int", 2^100), "pyobject")) %!test -%! pyexec ("def pyfunc(x): return 2*x"); -%! z = pycall ("pyfunc", [20 20]); -%! assert (z, [40 40]) - -%!test %! pyexec (["def pyfunc(x):\n" ... %! " if x is True:\n return 30\n" ... %! " elif x is False:\n return 20\n" ...