NumCpp  2.11.0
A Templatized Header Only C++ Implementation of the Python NumPy Library
BoostNumpyNdarrayHelper.hpp
Go to the documentation of this file.
1 #pragma once
29 
30 #if defined(NUMCPP_INCLUDE_BOOST_PYTHON_INTERFACE) && !defined(NUMCPP_NO_USE_BOOST)
31 
32 #include <cmath>
33 #include <iostream>
34 #include <string>
35 #include <utility>
36 #include <vector>
37 
38 #include "boost/python.hpp"
39 #include "boost/python/numpy.hpp"
40 
42 #include "NumCpp/Core/Types.hpp"
43 #include "NumCpp/Utils/num2str.hpp"
44 
45 namespace nc
46 {
47  namespace boostPythonInterface
48  {
49  //================================================================================
51  template<typename dtype>
52  class BoostNdarrayHelper
53  {
54  public:
55  //================================================================================
57  enum class Order
58  {
59  F,
60  C
61  };
62 
63  //============================================================================
68  explicit BoostNdarrayHelper(const boost::python::numpy::ndarray& inArray) :
69  theArray_(inArray.astype(boost::python::numpy::dtype::get_builtin<dtype>())),
70  numDimensions_(static_cast<uint8>(inArray.get_nd())),
71  shape_(numDimensions_),
72  strides_(numDimensions_),
73  order_(Order::C)
74 
75  {
76  Py_intptr_t const* shapePtr = inArray.get_shape();
77  for (uint8 i = 0; i < numDimensions_; ++i)
78  {
79  strides_[i] = static_cast<uint32>(theArray_.strides(i));
80  shape_[i] = shapePtr[i];
81  }
82 
83  if (numDimensions_ > 1 && inArray.strides(0) < inArray.strides(1))
84  {
85  order_ = Order::F;
86  }
87  }
88 
89  //============================================================================
94  explicit BoostNdarrayHelper(boost::python::tuple inShape) :
95  theArray_(boost::python::numpy::zeros(inShape, boost::python::numpy::dtype::get_builtin<dtype>())),
96  numDimensions_(static_cast<uint8>(theArray_.get_nd())),
97  shape_(numDimensions_),
98  strides_(numDimensions_),
99  order_(Order::C)
100  {
101  Py_intptr_t const* shapePtr = theArray_.get_shape();
102  for (uint8 i = 0; i < numDimensions_; ++i)
103  {
104  strides_[i] = static_cast<uint32>(theArray_.strides(i));
105  shape_[i] = shapePtr[i];
106  }
107 
108  if (numDimensions_ > 1 && theArray_.strides(0) < theArray_.strides(1))
109  {
110  order_ = Order::F;
111  }
112  }
113 
114  //============================================================================
119  const boost::python::numpy::ndarray& getArray() noexcept
120  {
121  return theArray_;
122  }
123 
124  //============================================================================
129  boost::python::numpy::matrix getArrayAsMatrix()
130  {
131  return boost::python::numpy::matrix(theArray_);
132  }
133 
134  //============================================================================
139  uint8 numDimensions() noexcept
140  {
141  return numDimensions_;
142  }
143 
144  //============================================================================
149  const std::vector<Py_intptr_t>& shape() noexcept
150  {
151  return shape_;
152  }
153 
154  //============================================================================
159  uint32 size()
160  {
161  uint32 theSize = 1;
162  for (auto dimSize : shape_)
163  {
164  theSize *= static_cast<uint32>(dimSize);
165  }
166  return theSize;
167  }
168 
169  //============================================================================
174  const std::vector<uint32>& strides()
175  {
176  return strides_;
177  }
178 
179  //============================================================================
184  Order order()
185  {
186  return order_;
187  }
188 
189  //============================================================================
196  bool shapeEqual(BoostNdarrayHelper& otherNdarrayHelper)
197  {
198  if (shape_.size() != otherNdarrayHelper.shape_.size())
199  {
200  return false;
201  }
202 
203  return stl_algorithms::equal(shape_.begin(), shape_.end(), otherNdarrayHelper.shape_.begin());
204  }
205 
206  //============================================================================
213  dtype& operator()(uint32 index)
214  {
215  checkIndices1D(index);
216 
217  return *reinterpret_cast<dtype*>(theArray_.get_data() + strides_.front() * index);
218  }
219 
220  //============================================================================
228  dtype& operator()(uint32 index1, uint32 index2)
229  {
230  checkIndices2D(index1, index2);
231 
232  return *reinterpret_cast<dtype*>(theArray_.get_data() + strides_.front() * index1 +
233  strides_[1] * index2);
234  }
235 
236  //============================================================================
239  void printArray1D()
240  {
241  printf("array = \n");
242  if (numDimensions_ != 1)
243  {
244  std::cout << "printArray1D can only be used on a 1D array." << std::endl;
245  return;
246  }
247 
248  for (int32 i = 0; i < shape_.front(); ++i)
249  {
250  printf("\t%f\n", operator()(i));
251  }
252  }
253 
254  //============================================================================
257  void printArray2D()
258  {
259  printf("array = \n");
260  if (numDimensions_ != 2)
261  {
262  std::cout << "printArray2D can only be used on a 2D array." << std::endl;
263  return;
264  }
265 
266  for (int32 index1 = 0; index1 < shape_.front(); ++index1)
267  {
268  for (int32 index2 = 0; index2 < shape_.back(); ++index2)
269  {
270  printf("\t%f", operator()(index1, index2));
271  }
272  printf('\n');
273  }
274  }
275 
276  private:
277  //====================================Attributes==============================
278  boost::python::numpy::ndarray theArray_;
279  uint8 numDimensions_;
280  std::vector<Py_intptr_t> shape_;
281  std::vector<uint32> strides_;
282  Order order_;
283 
284  //============================================================================
289  void checkIndicesGeneric(boost::python::tuple indices)
290  {
291  if (boost::python::len(indices) != numDimensions_)
292  {
293  std::string errStr =
294  "Error: BoostNdarrayHelper::checkIndicesGeneric: Array has " + utils::num2str(numDimensions_);
295  errStr += " dimensions, you asked for " +
296  utils::num2str(static_cast<int>(boost::python::len(indices))) + "!";
297  PyErr_SetString(PyExc_RuntimeError, errStr.c_str());
298  }
299 
300  for (int i = 0; i < numDimensions_; ++i)
301  {
302  int index = boost::python::extract<int>(indices[i]);
303  if (index > shape_[i])
304  {
305  std::string errStr =
306  "Error: BoostNdarrayHelper::checkIndicesGeneric: Input index [" + utils::num2str(index);
307  errStr += "] is larger than the size of the array [" + utils::num2str(shape_[i]) + "].";
308  PyErr_SetString(PyExc_RuntimeError, errStr.c_str());
309  }
310  }
311  }
312 
313  //============================================================================
318  void checkIndices1D(uint32 index)
319  {
320  boost::python::tuple indices = boost::python::make_tuple(index);
321  checkIndicesGeneric(indices);
322  }
323 
324  //============================================================================
330  void checkIndices2D(uint32 index1, uint32 index2)
331  {
332  boost::python::tuple indices = boost::python::make_tuple(index1, index2);
333  checkIndicesGeneric(indices);
334  }
335  };
336  } // namespace boostPythonInterface
337 } // namespace nc
338 
339 #endif // #if defined(NUMCPP_INCLUDE_BOOST_PYTHON_INTERFACE) && !defined(NUMCPP_NO_USE_BOOST)
bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2) noexcept
Definition: StlAlgorithms.hpp:140
std::string num2str(dtype inNumber)
Definition: num2str.hpp:44
Definition: Cartesian.hpp:40
NdArray< dtypeOut > astype(const NdArray< dtype > inArray)
Definition: astype.hpp:45
uint32 size(const NdArray< dtype > &inArray) noexcept
Definition: size.hpp:43
NdArray< dtype > zeros(uint32 inSquareSize)
Definition: zeros.hpp:48
std::int32_t int32
Definition: Types.hpp:36
std::uint8_t uint8
Definition: Types.hpp:42
Shape shape(const NdArray< dtype > &inArray) noexcept
Definition: Functions/Shape.hpp:42
std::uint32_t uint32
Definition: Types.hpp:40