Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
PyMethodBase.cxx
Go to the documentation of this file.
1// @(#)root/tmva/pymva $Id$
2// Authors: Omar Zapata, Lorenzo Moneta, Sergei Gleyzer 2015, Stefan Wunsch 2017
3
4/**********************************************************************************
5 * Project: TMVA - a Root-integrated toolkit for multivariate data analysis *
6 * Package: TMVA *
7 * Class : PyMethodBase *
8 * *
9 * Description: *
10 * Virtual base class for all MVA method based on python *
11 * *
12 **********************************************************************************/
13
14#include <Python.h> // Needs to be included first to avoid redefinition of _POSIX_C_SOURCE
15#include <TMVA/PyMethodBase.h>
16
17#include "TMVA/DataSet.h"
18#include "TMVA/DataSetInfo.h"
19#include "TMVA/MsgLogger.h"
20#include "TMVA/Results.h"
21#include "TMVA/Timer.h"
22#include "TMVA/Tools.h"
23
24#include "TSystem.h"
25
26#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
27#include <numpy/arrayobject.h>
28
29using namespace TMVA;
30
31namespace TMVA {
32namespace Internal {
33class PyGILRAII {
34 PyGILState_STATE m_GILState;
35
36public:
39};
40} // namespace Internal
41
42/// get current Python executable used by ROOT
44 TString python_version = gSystem->GetFromPipe("root-config --python-version");
45 if (python_version.IsNull()) {
46 TMVA::gTools().Log() << kFATAL << "Can't find a valid Python version used to build ROOT" << Endl;
47 return nullptr;
48 }
49#ifdef _MSC_VER
50 // on Windows there is a space before the version and the executable is python.exe
51 // for both versions of Python
52 python_version.ReplaceAll(" ", "");
53 if (python_version[0] == '2' || python_version[0] == '3')
54 return "python";
55#endif
56 if (python_version[0] == '2')
57 return "python";
58 else if (python_version[0] == '3')
59 return "python3";
60
61 TMVA::gTools().Log() << kFATAL << "Invalid Python version used to build ROOT : " << python_version << Endl;
62 return nullptr;
63}
64
65} // namespace TMVA
66
67
68// NOTE: Introduce here nothing that breaks if multiple instances
69// of the same method share these objects, e.g., the local namespace.
73
77
80
81///////////////////////////////////////////////////////////////////////////////
82
84 const TString &theOption)
85 : MethodBase(jobName, methodType, methodTitle, dsi, theOption),
86 fClassifier(NULL)
87{
89
90 if (!PyIsInitialized()) {
92 }
93
94 // Set up private local namespace for each method instance
96 if (!fLocalNS) {
97 Log() << kFATAL << "Can't init local namespace" << Endl;
98 }
99}
100
101///////////////////////////////////////////////////////////////////////////////
102
106 fClassifier(NULL)
107{
109
110 if (!PyIsInitialized()) {
111 PyInitialize();
112 }
113
114 // Set up private local namespace for each method instance
116 if (!fLocalNS) {
117 Log() << kFATAL << "Can't init local namespace" << Endl;
118 }
119}
120
121///////////////////////////////////////////////////////////////////////////////
122/// Warn that PyMVA is deprecated and scheduled for removal in ROOT 6.44.
123
125{
126 Log() << kWARNING
127 << "PyMVA is deprecated and will be removed in ROOT 6.44. Please use the underlying Python "
128 "machine-learning packages directly, or export your model to ONNX and evaluate it with SOFIE "
129 "(see the RSofieReader class)."
130 << Endl;
131}
132
133///////////////////////////////////////////////////////////////////////////////
134
136{
137 // should we delete here fLocalNS ?
138 //PyFinalize();
140}
141
142///////////////////////////////////////////////////////////////////////////////
143/// Evaluate Python code
144///
145/// \param[in] code Python code as string
146/// \return Python object from evaluation of code line
147///
148/// Take a Python code as input and evaluate it in the local namespace. Then,
149/// return the result as Python object.
150
159
160///////////////////////////////////////////////////////////////////////////////
161/// Initialize Python interpreter
162///
163/// NOTE: We introduce a shared global namespace `fGlobalNS`, but using
164/// a private local namespace `fLocalNS`. This prohibits the interference
165/// of instances of the same method with the same factory, e.g., by overriding
166/// variables in the same local namespace.
167
169{
171
173 if (!pyIsInitialized) {
175 }
176
178 if (!pyIsInitialized) {
180 }
181
182 // note fMain is a borrowed reference
183 fMain = PyImport_AddModule("__main__");
184 if (!fMain) {
185 Log << kFATAL << "Can't import __main__" << Endl;
186 Log << Endl;
187 }
189
191 if (!fGlobalNS) {
192 Log << kFATAL << "Can't init global namespace" << Endl;
193 Log << Endl;
194 }
196
197 #if PY_MAJOR_VERSION < 3
198 //preparing objects for eval
199 PyObject *bName = PyUnicode_FromString("__builtin__");
200 // Import the file as a Python module.
201 // returns a new reference
203 if (!fModuleBuiltin) {
204 Log << kFATAL << "Can't import __builtin__" << Endl;
205 Log << Endl;
206 }
207 #else
208 //preparing objects for eval
209 PyObject *bName = PyUnicode_FromString("builtins");
210 // Import the file as a Python module.
212 if (!fModuleBuiltin) {
213 Log << kFATAL << "Can't import builtins" << Endl;
214 Log << Endl;
215 }
216 #endif
217
218 // note mDict is a borrowed reference
222 // fEval and fOpen are borrowed referencers and we need to keep them alive
223 if (fEval) Py_INCREF(fEval);
224 if (fOpen) Py_INCREF(fOpen);
225
226 // bName is a new reference (from PyUnicode_FromString)
228
229 //preparing objects for pickle
231 // Import the file as a Python module.
232 // return object is a new reference !
234 if (!fModulePickle) {
235 Log << kFATAL << "Can't import pickle" << Endl;
236 Log << Endl;
237 }
239 // note the following return objects are borrowed references
244
246}
247
248///////////////////////////////////////////////////////////////////////////////
249// Finalize Python interpreter
250
252{
253 if (fEval) Py_DECREF(fEval);
254 if (fOpen) Py_DECREF(fOpen);
258 if(fMain) Py_DECREF(fMain);//objects fGlobalNS and fLocalNS will be free here
260 Py_Finalize();
261}
262
263///////////////////////////////////////////////////////////////////////////////
264/// Check Python interpreter initialization status
265///
266/// \return Boolean whether interpreter is initialized
267
269{
270 if (!Py_IsInitialized()) return kFALSE;
271 if (!fEval) return kFALSE;
272 if (!fModuleBuiltin) return kFALSE;
273 if (!fPickleDumps) return kFALSE;
274 if (!fPickleLoads) return kFALSE;
275 return kTRUE;
276}
277
278///////////////////////////////////////////////////////////////////////////////
279/// Serialize Python object
280///
281/// \param[in] path Path where object is written to file
282/// \param[in] obj Python object
283///
284/// The input Python object is serialized and written to a file. The Python
285/// module `pickle` is used to do so.
286
301
302///////////////////////////////////////////////////////////////////////////////
303/// Unserialize Python object
304///
305/// \param[in] path Path to serialized Python object
306/// \param[in] obj Python object where the unserialized Python object is loaded
307/// \return Error code
308
310{
311 // Load file
312 PyObject *file_arg = Py_BuildValue("(ss)", path.Data(),"rb");
314 if(!file) return 1;
315
316 // Load object from file using pickle
317 PyObject *model_arg = Py_BuildValue("(O)", file);
319 if(!obj) return 2;
320
322 Py_DECREF(file);
324
325 return 0;
326}
327
328///////////////////////////////////////////////////////////////////////////////
329/// Execute Python code from string
330///
331/// \param[in] code Python code as string
332/// \param[in] errorMessage Error message which shall be shown if the execution fails
333/// \param[in] start Start symbol
334///
335/// Helper function to run python code from string in local namespace with
336/// error handling
337/// `start` defines the start symbol defined in PyRun_String (Py_eval_input,
338/// Py_single_input, Py_file_input)
339
341 //std::cout << "Run: >> " << code << std::endl;
342 fPyReturn = PyRun_String(code, start, fGlobalNS, fLocalNS);
343 if (!fPyReturn) {
344 Log() << kWARNING << "Failed to run python code: " << code << Endl;
345 Log() << kWARNING << "Python error message:" << Endl;
346 PyErr_Print();
347 Log() << kFATAL << errorMessage << Endl;
348 }
349}
350
351///////////////////////////////////////////////////////////////////////////////
352/// Execute Python code from string
353///
354/// \param[in] code Python code as string
355/// \param[in] globalNS Global Namespace for Python Session
356/// \param[in] localNS Local Namespace for Python Session
357///
358/// Overloaded static Helper function to run python code
359/// from string and throw runtime error if the Python session
360/// is unable to execute the code
361
364 if (!fPyReturn) {
365 std::cout<<"\nPython error message:\n";
366 PyErr_Print();
367 throw std::runtime_error("\nFailed to run python code: "+code);
368 }
369}
370
371///////////////////////////////////////////////////////////////////////////////
372/// Returns `const char*` from Python string in PyObject
373///
374/// \param[in] string Python String object
375/// \return String representation in `const char*`
376
382
383//////////////////////////////////////////////////////////////////////////////////
384/// \brief Utility function which retrieves and returns the values of the Tuple
385/// object as a vector of size_t
386///
387/// \param[in] tupleObject Python Tuple object
388/// \return vector of tuple members
389
391 std::vector<size_t>tupleVec;
394 if (itemObj == Py_None)
395 tupleVec.push_back(0); // case shape is for example (None,2,3)
396 else
397 tupleVec.push_back((size_t)PyLong_AsLong(itemObj));
398 }
399 return tupleVec;
400}
401
402//////////////////////////////////////////////////////////////////////////////////
403/// \brief Utility function which retrieves and returns the values of the List
404/// object as a vector of size_t
405///
406/// \param[in] listObject Python List object
407/// \return vector of list members
408
410 std::vector<size_t>listVec;
413 }
414 return listVec;
415}
416
417//////////////////////////////////////////////////////////////////////////////////
418/// \brief Utility function which checks if a given key is present in a Python
419/// dictionary object and returns the associated value or throws runtime
420/// error.
421///
422/// \param[in] dict Python Dict object
423/// \param[in] key the key to search for in the dict
424/// \return Associated value PyObject
426{
428}
#define PyBytes_AsString
Definition CPyCppyy.h:64
_object PyObject
constexpr Bool_t kFALSE
Definition RtypesCore.h:109
constexpr Bool_t kTRUE
Definition RtypesCore.h:108
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
R__EXTERN TSystem * gSystem
Definition TSystem.h:582
MsgLogger & Log() const
Class that contains all the data information.
Definition DataSetInfo.h:62
Virtual base Class for all MVA method.
Definition MethodBase.h:82
ostringstream derivative to redirect and format output
Definition MsgLogger.h:57
static std::vector< size_t > GetDataFromTuple(PyObject *tupleObject)
Utility function which retrieves and returns the values of the Tuple object as a vector of size_t.
static int PyIsInitialized()
Check Python interpreter initialization status.
static std::vector< size_t > GetDataFromList(PyObject *listObject)
Utility function which retrieves and returns the values of the List object as a vector of size_t.
static PyObject * fOpen
static PyObject * fPickleDumps
PyObject * Eval(TString code)
Evaluate Python code.
static PyObject * fMain
static void PyInitialize()
Initialize Python interpreter.
static void Serialize(TString file, PyObject *classifier)
Serialize Python object.
static void PyFinalize()
void PrintDeprecationWarning()
Warn that PyMVA is deprecated and scheduled for removal in ROOT 6.44.
static Int_t UnSerialize(TString file, PyObject **obj)
Unserialize Python object.
static const char * PyStringAsString(PyObject *string)
Returns const char* from Python string in PyObject.
static PyObject * fPickleLoads
static PyObject * fGlobalNS
static PyObject * fModulePickle
static PyObject * fModuleBuiltin
PyMethodBase(const TString &jobName, Types::EMVA methodType, const TString &methodTitle, DataSetInfo &dsi, const TString &theOption="")
static PyObject * fEval
static PyObject * GetValueFromDict(PyObject *dict, const char *key)
Utility function which checks if a given key is present in a Python dictionary object and returns the...
void PyRunString(TString code, TString errorMessage="Failed to run python code", int start=256)
Execute Python code from string.
MsgLogger & Log() const
Definition Tools.h:228
Basic string class.
Definition TString.h:137
const char * Data() const
Definition TString.h:385
virtual TString GetFromPipe(const char *command, Int_t *ret=nullptr, Bool_t redirectStderr=kFALSE)
Execute command and return output in TString.
Definition TSystem.cxx:688
create variable transformations
TString Python_Executable()
Function to find current Python executable used by ROOT If "Python3" is installed,...
Tools & gTools()
MsgLogger & Endl(MsgLogger &ml)
Definition MsgLogger.h:148