Logo ROOT  
Reference Guide
RTensorPyz.cxx
Go to the documentation of this file.
1 // Author: Stefan Wunsch CERN 07/2019
2 // Original PyROOT code by Wim Lavrijsen, LBL
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2018, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 #include "CPyCppyy.h"
13 #include "CPPInstance.h"
14 #include "ProxyWrappers.h"
15 #include "PyROOTPythonize.h"
16 #include "TInterpreter.h"
17 #include "PyzCppHelpers.hxx"
18 
19 #include <sstream>
20 
21 ////////////////////////////////////////////////////////////////////////////
22 /// \brief Adopt memory of a Python object with array interface using an RTensor
23 /// \param[in] obj PyObject with array interface
24 ///
25 /// This function returns an RTensor which adopts the memory of the given
26 /// PyObject. The RTensor takes the data pointer and the shape from the array
27 /// interface dictionary.
29 {
30  if (!obj) {
31  PyErr_SetString(PyExc_RuntimeError, "Object not convertible: Invalid Python object.");
32  return NULL;
33  }
34 
35  // Get array interface of object
36  auto pyinterface = GetArrayInterface(obj);
37  if (pyinterface == NULL)
38  return NULL;
39 
40  // Get the data-pointer
41  const auto data = GetDataPointerFromArrayInterface(pyinterface);
42  if (data == 0)
43  return NULL;
44 
45  // Get the size of the contiguous memory
46  auto pyshape = PyDict_GetItemString(pyinterface, "shape");
47  if (!pyshape) {
48  PyErr_SetString(PyExc_RuntimeError, "Object not convertible: __array_interface__['shape'] does not exist.");
49  return NULL;
50  }
51  std::vector<std::size_t> shape;
52  for (Py_ssize_t i = 0; i < PyTuple_Size(pyshape); i++) {
53  const auto s = PyLong_AsLong(PyTuple_GetItem(pyshape, i));
54  shape.push_back(s);
55  }
56 
57  // Get the typestring and properties thereof
58  const auto typestr = GetTypestrFromArrayInterface(pyinterface);
59  if (typestr.compare("") == 0)
60  return NULL;
61  const auto dtypesize = GetDatatypeSizeFromTypestr(typestr);
62  if (!CheckEndianessFromTypestr(typestr))
63  return NULL;
64 
65  const auto dtype = typestr.substr(1, typestr.size());
66  std::string cppdtype = GetCppTypeFromNumpyType(dtype);
67  if (cppdtype.compare("") == 0)
68  return NULL;
69 
70  // Get strides
71  if (!PyObject_HasAttrString(obj, "strides")) {
72  PyErr_SetString(PyExc_RuntimeError, "Object not convertible: Object does not have method 'strides'.");
73  return NULL;
74  }
75  auto pystrides = PyObject_GetAttrString(obj, "strides");
76  std::vector<std::size_t> strides;
77  for (Py_ssize_t i = 0; i < PyTuple_Size(pystrides); i++) {
78  strides.push_back(PyInt_AsLong(PyTuple_GetItem(pystrides, i)) / dtypesize);
79  }
80  Py_DECREF(pystrides);
81 
82  // Infer memory layout from strides
83  bool rowMajor = true;
84  if (strides.size() > 1) {
85  if (strides.front() < strides.back()) rowMajor = false;
86  }
87 
88  // Construct an RTensor of the correct data-type
89  const std::string klassname = "TMVA::Experimental::RTensor<" + cppdtype + ",std::vector<" + cppdtype + ">>";
90  std::stringstream code;
91  code << "new " << klassname << "(reinterpret_cast<" << cppdtype << "*>(" << data << "),{";
92  for (auto s: shape) code << s << ",";
93  code << "},{";
94  for (auto s: strides) code << s << ",";
95  code << "},";
96  if (rowMajor) {
97  code << "TMVA::Experimental::MemoryLayout::RowMajor";
98  }
99  else {
100  code << "TMVA::Experimental::MemoryLayout::ColumnMajor";
101  }
102  code << ")";
103  const auto codestr = code.str();
104  auto address = (void*) gInterpreter->Calc(codestr.c_str());
105 
106  // Bind the object to a Python-side proxy
107  auto klass = (Cppyy::TCppType_t)Cppyy::GetScope(klassname);
108  auto pyobj = CPyCppyy::BindCppObject(address, klass);
109 
110  // Give Python the ownership of the underlying C++ object
111  ((CPyCppyy::CPPInstance*)pyobj)->PythonOwns();
112 
113  // Bind pyobject holding adopted memory to the RTensor
114  if (PyObject_SetAttrString(pyobj, "__adopted__", obj)) {
115  PyErr_SetString(PyExc_RuntimeError, "Object not convertible: Failed to set Python object as attribute __adopted__.");
116  return NULL;
117  }
118 
119  // Clean-up and return
120  Py_DECREF(pyinterface);
121  return pyobj;
122 }
CPPInstance.h
PyObject
_object PyObject
Definition: PyMethodBase.h:42
GetTypestrFromArrayInterface
std::string GetTypestrFromArrayInterface(PyObject *obj)
Get type string from Numpy array interface and perform error handling.
Definition: PyzCppHelpers.cxx:110
PyROOTPythonize.h
gInterpreter
#define gInterpreter
Definition: TInterpreter.h:560
CPyCppyy::CPPInstance
Definition: CPPInstance.h:26
TGeant4Unit::s
static constexpr double s
Definition: TGeant4SystemOfUnits.h:162
GetCppTypeFromNumpyType
std::string GetCppTypeFromNumpyType(const std::string &dtype)
Convert Numpy data-type string to the according C++ data-type string.
Definition: PyzCppHelpers.cxx:55
PyROOT::AsRTensor
PyObject * AsRTensor(PyObject *self, PyObject *obj)
Adopt memory of a Python object with array interface using an RTensor.
Definition: RTensorPyz.cxx:28
Cppyy::GetScope
RPY_EXPORTED TCppScope_t GetScope(const std::string &scope_name)
Definition: clingwrapper.cxx:497
CPyCppyy::BindCppObject
PyObject * BindCppObject(Cppyy::TCppObject_t object, Cppyy::TCppType_t klass, const unsigned flags=0)
Definition: ProxyWrappers.cxx:873
CheckEndianessFromTypestr
bool CheckEndianessFromTypestr(const std::string &typestr)
Check whether endianess in type string matches the endianess of ROOT.
Definition: PyzCppHelpers.cxx:142
ProxyWrappers.h
GetDataPointerFromArrayInterface
unsigned long long GetDataPointerFromArrayInterface(PyObject *obj)
Get data pointer from Numpy array interface and perform error handling.
Definition: PyzCppHelpers.cxx:96
GetArrayInterface
PyObject * GetArrayInterface(PyObject *obj)
Get Numpy array interface and perform error handling.
Definition: PyzCppHelpers.cxx:78
CPyCppyy.h
Cppyy::TCppType_t
TCppScope_t TCppType_t
Definition: cpp_cppyy.h:19
GetDatatypeSizeFromTypestr
unsigned int GetDatatypeSizeFromTypestr(const std::string &typestr)
Get size of data type in bytes from Numpy type string.
Definition: PyzCppHelpers.cxx:131
PyzCppHelpers.hxx
TInterpreter.h
int