• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright Jim Bosch 2010-2012.
2 // Copyright Stefan Seefeld 2016.
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6 
7 #define BOOST_PYTHON_NUMPY_INTERNAL
8 #include <boost/python/numpy/internal.hpp>
9 #include <boost/scoped_array.hpp>
10 
11 namespace boost { namespace python {
12 namespace converter
13 {
14 NUMPY_OBJECT_MANAGER_TRAITS_IMPL(PyArray_Type, numpy::ndarray)
15 } // namespace boost::python::converter
16 
17 namespace numpy
18 {
19 namespace detail
20 {
21 
numpy_to_bitflag(int const f)22 ndarray::bitflag numpy_to_bitflag(int const f)
23 {
24   ndarray::bitflag r = ndarray::NONE;
25   if (f & NPY_ARRAY_C_CONTIGUOUS) r = (r | ndarray::C_CONTIGUOUS);
26   if (f & NPY_ARRAY_F_CONTIGUOUS) r = (r | ndarray::F_CONTIGUOUS);
27   if (f & NPY_ARRAY_ALIGNED) r = (r | ndarray::ALIGNED);
28   if (f & NPY_ARRAY_WRITEABLE) r = (r | ndarray::WRITEABLE);
29   return r;
30 }
31 
bitflag_to_numpy(ndarray::bitflag f)32 int bitflag_to_numpy(ndarray::bitflag f)
33 {
34   int r = 0;
35   if (f & ndarray::C_CONTIGUOUS) r |= NPY_ARRAY_C_CONTIGUOUS;
36   if (f & ndarray::F_CONTIGUOUS) r |= NPY_ARRAY_F_CONTIGUOUS;
37   if (f & ndarray::ALIGNED) r |= NPY_ARRAY_ALIGNED;
38   if (f & ndarray::WRITEABLE) r |= NPY_ARRAY_WRITEABLE;
39   return r;
40 }
41 
is_c_contiguous(std::vector<Py_intptr_t> const & shape,std::vector<Py_intptr_t> const & strides,int itemsize)42 bool is_c_contiguous(std::vector<Py_intptr_t> const & shape,
43 		     std::vector<Py_intptr_t> const & strides,
44 		     int itemsize)
45 {
46   std::vector<Py_intptr_t>::const_reverse_iterator j = strides.rbegin();
47   int total = itemsize;
48   for (std::vector<Py_intptr_t>::const_reverse_iterator i = shape.rbegin(); i != shape.rend(); ++i, ++j)
49   {
50     if (total != *j) return false;
51     total *= (*i);
52   }
53   return true;
54 }
55 
is_f_contiguous(std::vector<Py_intptr_t> const & shape,std::vector<Py_intptr_t> const & strides,int itemsize)56 bool is_f_contiguous(std::vector<Py_intptr_t> const & shape,
57 		     std::vector<Py_intptr_t> const & strides,
58 		     int itemsize)
59 {
60   std::vector<Py_intptr_t>::const_iterator j = strides.begin();
61   int total = itemsize;
62   for (std::vector<Py_intptr_t>::const_iterator i = shape.begin(); i != shape.end(); ++i, ++j)
63   {
64     if (total != *j) return false;
65     total *= (*i);
66   }
67   return true;
68 }
69 
is_aligned(std::vector<Py_intptr_t> const & strides,int itemsize)70 bool is_aligned(std::vector<Py_intptr_t> const & strides,
71 		int itemsize)
72 {
73   for (std::vector<Py_intptr_t>::const_iterator i = strides.begin(); i != strides.end(); ++i)
74   {
75     if (*i % itemsize) return false;
76   }
77   return true;
78 }
79 
incref_dtype(dtype const & dt)80 inline PyArray_Descr * incref_dtype(dtype const & dt)
81 {
82   Py_INCREF(dt.ptr());
83   return reinterpret_cast<PyArray_Descr*>(dt.ptr());
84 }
85 
from_data_impl(void * data,dtype const & dt,python::object const & shape,python::object const & strides,python::object const & owner,bool writeable)86 ndarray from_data_impl(void * data,
87 		       dtype const & dt,
88 		       python::object const & shape,
89 		       python::object const & strides,
90 		       python::object const & owner,
91 		       bool writeable)
92 {
93   std::vector<Py_intptr_t> shape_(len(shape));
94   std::vector<Py_intptr_t> strides_(len(strides));
95   if (shape_.size() != strides_.size())
96   {
97     PyErr_SetString(PyExc_ValueError, "Length of shape and strides arrays do not match.");
98     python::throw_error_already_set();
99   }
100   for (std::size_t i = 0; i < shape_.size(); ++i)
101   {
102     shape_[i] = python::extract<Py_intptr_t>(shape[i]);
103     strides_[i] = python::extract<Py_intptr_t>(strides[i]);
104   }
105   return from_data_impl(data, dt, shape_, strides_, owner, writeable);
106 }
107 
from_data_impl(void * data,dtype const & dt,std::vector<Py_intptr_t> const & shape,std::vector<Py_intptr_t> const & strides,python::object const & owner,bool writeable)108 ndarray from_data_impl(void * data,
109 		       dtype const & dt,
110 		       std::vector<Py_intptr_t> const & shape,
111 		       std::vector<Py_intptr_t> const & strides,
112 		       python::object const & owner,
113 		       bool writeable)
114 {
115   if (shape.size() != strides.size())
116   {
117     PyErr_SetString(PyExc_ValueError, "Length of shape and strides arrays do not match.");
118     python::throw_error_already_set();
119   }
120   int itemsize = dt.get_itemsize();
121   int flags = 0;
122   if (writeable) flags |= NPY_ARRAY_WRITEABLE;
123   if (is_c_contiguous(shape, strides, itemsize)) flags |= NPY_ARRAY_C_CONTIGUOUS;
124   if (is_f_contiguous(shape, strides, itemsize)) flags |= NPY_ARRAY_F_CONTIGUOUS;
125   if (is_aligned(strides, itemsize)) flags |= NPY_ARRAY_ALIGNED;
126   ndarray r(python::detail::new_reference
127     (PyArray_NewFromDescr(&PyArray_Type,
128 			  incref_dtype(dt),
129 			  shape.size(),
130 			  const_cast<Py_intptr_t*>(&shape.front()),
131 			  const_cast<Py_intptr_t*>(&strides.front()),
132 			  data,
133 			  flags,
134 			  NULL)));
135     r.set_base(owner);
136     return r;
137 }
138 
139 } // namespace detail
140 
141 namespace {
normalize_index(int n,int nlim)142     int normalize_index(int n,int nlim) // wraps [-nlim:nlim) into [0:nlim), throw IndexError otherwise
143     {
144         if (n<0)
145             n += nlim; // negative indices work backwards from end
146         if (n < 0 || n >= nlim)
147         {
148             PyErr_SetObject(PyExc_IndexError, Py_None);
149             throw_error_already_set();
150         }
151         return n;
152     }
153 }
154 
shape(int n) const155 Py_intptr_t ndarray::shape(int n) const
156 {
157     return get_shape()[normalize_index(n,get_nd())];
158 }
159 
strides(int n) const160 Py_intptr_t ndarray::strides(int n) const
161 {
162     return get_strides()[normalize_index(n,get_nd())];
163 }
164 
view(dtype const & dt) const165 ndarray ndarray::view(dtype const & dt) const
166 {
167   return ndarray(python::detail::new_reference
168     (PyObject_CallMethod(this->ptr(), const_cast<char*>("view"), const_cast<char*>("O"), dt.ptr())));
169 }
170 
astype(dtype const & dt) const171 ndarray ndarray::astype(dtype const & dt) const
172 {
173   return ndarray(python::detail::new_reference
174     (PyObject_CallMethod(this->ptr(), const_cast<char*>("astype"), const_cast<char*>("O"), dt.ptr())));
175 }
176 
copy() const177 ndarray ndarray::copy() const
178 {
179   return ndarray(python::detail::new_reference
180     (PyObject_CallMethod(this->ptr(), const_cast<char*>("copy"), const_cast<char*>(""))));
181 }
182 
get_dtype() const183 dtype ndarray::get_dtype() const
184 {
185   return dtype(python::detail::borrowed_reference(get_struct()->descr));
186 }
187 
get_base() const188 python::object ndarray::get_base() const
189 {
190   if (get_struct()->base == NULL) return object();
191   return python::object(python::detail::borrowed_reference(get_struct()->base));
192 }
193 
set_base(object const & base)194 void ndarray::set_base(object const & base)
195 {
196   Py_XDECREF(get_struct()->base);
197   if (base.ptr())
198   {
199     Py_INCREF(base.ptr());
200     get_struct()->base = base.ptr();
201   }
202   else
203   {
204     get_struct()->base = NULL;
205   }
206 }
207 
get_flags() const208 ndarray::bitflag ndarray::get_flags() const
209 {
210   return numpy::detail::numpy_to_bitflag(get_struct()->flags);
211 }
212 
transpose() const213 ndarray ndarray::transpose() const
214 {
215   return ndarray(python::detail::new_reference
216     (PyArray_Transpose(reinterpret_cast<PyArrayObject*>(this->ptr()), NULL)));
217 }
218 
squeeze() const219 ndarray ndarray::squeeze() const
220 {
221   return ndarray(python::detail::new_reference
222     (PyArray_Squeeze(reinterpret_cast<PyArrayObject*>(this->ptr()))));
223 }
224 
reshape(python::tuple const & shape) const225 ndarray ndarray::reshape(python::tuple const & shape) const
226 {
227   return ndarray(python::detail::new_reference
228     (PyArray_Reshape(reinterpret_cast<PyArrayObject*>(this->ptr()), shape.ptr())));
229 }
230 
scalarize() const231 python::object ndarray::scalarize() const
232 {
233   Py_INCREF(ptr());
234   return python::object(python::detail::new_reference(PyArray_Return(reinterpret_cast<PyArrayObject*>(ptr()))));
235 }
236 
zeros(python::tuple const & shape,dtype const & dt)237 ndarray zeros(python::tuple const & shape, dtype const & dt)
238 {
239   int nd = len(shape);
240   boost::scoped_array<Py_intptr_t> dims(new Py_intptr_t[nd]);
241   for (int n=0; n<nd; ++n) dims[n] = python::extract<Py_intptr_t>(shape[n]);
242   return ndarray(python::detail::new_reference
243                  (PyArray_Zeros(nd, dims.get(), detail::incref_dtype(dt), 0)));
244 }
245 
zeros(int nd,Py_intptr_t const * shape,dtype const & dt)246 ndarray zeros(int nd, Py_intptr_t const * shape, dtype const & dt)
247 {
248   return ndarray(python::detail::new_reference
249     (PyArray_Zeros(nd, const_cast<Py_intptr_t*>(shape), detail::incref_dtype(dt), 0)));
250 }
251 
empty(python::tuple const & shape,dtype const & dt)252 ndarray empty(python::tuple const & shape, dtype const & dt)
253 {
254   int nd = len(shape);
255   boost::scoped_array<Py_intptr_t> dims(new Py_intptr_t[nd]);
256   for (int n=0; n<nd; ++n) dims[n] = python::extract<Py_intptr_t>(shape[n]);
257   return ndarray(python::detail::new_reference
258                  (PyArray_Empty(nd, dims.get(), detail::incref_dtype(dt), 0)));
259 }
260 
empty(int nd,Py_intptr_t const * shape,dtype const & dt)261 ndarray empty(int nd, Py_intptr_t const * shape, dtype const & dt)
262 {
263   return ndarray(python::detail::new_reference
264     (PyArray_Empty(nd, const_cast<Py_intptr_t*>(shape), detail::incref_dtype(dt), 0)));
265 }
266 
array(python::object const & obj)267 ndarray array(python::object const & obj)
268 {
269   return ndarray(python::detail::new_reference
270     (PyArray_FromAny(obj.ptr(), NULL, 0, 0, NPY_ARRAY_ENSUREARRAY, NULL)));
271 }
272 
array(python::object const & obj,dtype const & dt)273 ndarray array(python::object const & obj, dtype const & dt)
274 {
275   return ndarray(python::detail::new_reference
276     (PyArray_FromAny(obj.ptr(), detail::incref_dtype(dt), 0, 0, NPY_ARRAY_ENSUREARRAY, NULL)));
277 }
278 
from_object(python::object const & obj,dtype const & dt,int nd_min,int nd_max,ndarray::bitflag flags)279 ndarray from_object(python::object const & obj, dtype const & dt, int nd_min, int nd_max, ndarray::bitflag flags)
280 {
281   int requirements = detail::bitflag_to_numpy(flags);
282   return ndarray(python::detail::new_reference
283     (PyArray_FromAny(obj.ptr(),
284 		     detail::incref_dtype(dt),
285 		     nd_min, nd_max,
286 		     requirements,
287 		     NULL)));
288 }
289 
from_object(python::object const & obj,int nd_min,int nd_max,ndarray::bitflag flags)290 ndarray from_object(python::object const & obj, int nd_min, int nd_max, ndarray::bitflag flags)
291 {
292   int requirements = detail::bitflag_to_numpy(flags);
293   return ndarray(python::detail::new_reference
294     (PyArray_FromAny(obj.ptr(),
295 		     NULL,
296 		     nd_min, nd_max,
297 		     requirements,
298 		     NULL)));
299 }
300 
301 }}} // namespace boost::python::numpy
302