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] self Always null, since this is a module function.
24/// \param[in] obj PyObject with array interface
25///
26/// This function returns an RTensor which adopts the memory of the given
27/// PyObject. The RTensor takes the data pointer and the shape from the array
28/// interface dictionary.
30{
31 if (!obj) {
32 PyErr_SetString(PyExc_RuntimeError, "Object not convertible: Invalid Python object.");
33 return NULL;
34 }
35
36 // Get array interface of object
37 auto pyinterface = GetArrayInterface(obj);
38 if (pyinterface == NULL)
39 return NULL;
40
41 // Get the data-pointer
42 const auto data = GetDataPointerFromArrayInterface(pyinterface);
43 if (data == 0)
44 return NULL;
45
46 // Get the size of the contiguous memory
47 auto pyshape = PyDict_GetItemString(pyinterface, "shape");
48 if (!pyshape) {
49 PyErr_SetString(PyExc_RuntimeError, "Object not convertible: __array_interface__['shape'] does not exist.");
50 return NULL;
51 }
52 std::vector<std::size_t> shape;
53 for (Py_ssize_t i = 0; i < PyTuple_Size(pyshape); i++) {
54 const auto s = PyLong_AsLong(PyTuple_GetItem(pyshape, i));
55 shape.push_back(s);
56 }
57
58 // Get the typestring and properties thereof
59 const auto typestr = GetTypestrFromArrayInterface(pyinterface);
60 if (typestr.compare("") == 0)
61 return NULL;
62 const auto dtypesize = GetDatatypeSizeFromTypestr(typestr);
63 if (!CheckEndianessFromTypestr(typestr))
64 return NULL;
65
66 const auto dtype = typestr.substr(1, typestr.size());
67 std::string cppdtype = GetCppTypeFromNumpyType(dtype);
68 if (cppdtype.compare("") == 0)
69 return NULL;
70
71 // Get strides
72 if (!PyObject_HasAttrString(obj, "strides")) {
73 PyErr_SetString(PyExc_RuntimeError, "Object not convertible: Object does not have method 'strides'.");
74 return NULL;
75 }
76 auto pystrides = PyObject_GetAttrString(obj, "strides");
77 std::vector<std::size_t> strides;
78 for (Py_ssize_t i = 0; i < PyTuple_Size(pystrides); i++) {
79 strides.push_back(PyInt_AsLong(PyTuple_GetItem(pystrides, i)) / dtypesize);
80 }
81 Py_DECREF(pystrides);
82
83 // Infer memory layout from strides
84 bool rowMajor = true;
85 if (strides.size() > 1) {
86 if (strides.front() < strides.back()) rowMajor = false;
87 }
88
89 // Construct an RTensor of the correct data-type
90 const std::string klassname = "TMVA::Experimental::RTensor<" + cppdtype + ",std::vector<" + cppdtype + ">>";
91 std::stringstream code;
92 code << "new " << klassname << "(reinterpret_cast<" << cppdtype << "*>(" << std::hex << std::showbase << data << "),{";
93 for (auto s: shape) code << s << ",";
94 code << "},{";
95 for (auto s: strides) code << s << ",";
96 code << "},";
97 if (rowMajor) {
98 code << "TMVA::Experimental::MemoryLayout::RowMajor";
99 }
100 else {
101 code << "TMVA::Experimental::MemoryLayout::ColumnMajor";
102 }
103 code << ")";
104 const auto codestr = code.str();
105 auto address = (void*) gInterpreter->Calc(codestr.c_str());
106
107 // Bind the object to a Python-side proxy
108 auto klass = (Cppyy::TCppType_t)Cppyy::GetScope(klassname);
109 auto pyobj = CPyCppyy::BindCppObject(address, klass);
110
111 // Give Python the ownership of the underlying C++ object
112 ((CPyCppyy::CPPInstance*)pyobj)->PythonOwns();
113
114 // Bind pyobject holding adopted memory to the RTensor
115 if (PyObject_SetAttrString(pyobj, "__adopted__", obj)) {
116 PyErr_SetString(PyExc_RuntimeError, "Object not convertible: Failed to set Python object as attribute __adopted__.");
117 return NULL;
118 }
119
120 // Clean-up and return
121 Py_DECREF(pyinterface);
122 return pyobj;
123}
_object PyObject
Definition: PyMethodBase.h:43
unsigned long long GetDataPointerFromArrayInterface(PyObject *obj)
Get data pointer from Numpy array interface and perform error handling.
unsigned int GetDatatypeSizeFromTypestr(const std::string &typestr)
Get size of data type in bytes from Numpy type string.
std::string GetCppTypeFromNumpyType(const std::string &dtype)
Convert Numpy data-type string to the according C++ data-type string.
std::string GetTypestrFromArrayInterface(PyObject *obj)
Get type string from Numpy array interface and perform error handling.
bool CheckEndianessFromTypestr(const std::string &typestr)
Check whether endianess in type string matches the endianess of ROOT.
PyObject * GetArrayInterface(PyObject *obj)
Get Numpy array interface and perform error handling.
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
#define gInterpreter
Definition: TInterpreter.h:564
PyObject * BindCppObject(Cppyy::TCppObject_t object, Cppyy::TCppType_t klass, const unsigned flags=0)
TCppScope_t TCppType_t
Definition: cpp_cppyy.h:19
RPY_EXPORTED TCppScope_t GetScope(const std::string &scope_name)
PyObject * AsRTensor(PyObject *self, PyObject *obj)
Adopt memory of a Python object with array interface using an RTensor.
Definition: RTensorPyz.cxx:29
static constexpr double s