From 8b159f239b42ef23d066275f2dad02bceacf2c20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana=20G=C3=B3mez?= Date: Sat, 4 Nov 2023 11:00:21 +0100 Subject: [PATCH] Refactor singleton to manage cleanup --- src/PyClassifier.cc | 56 +++++--------- src/PyClassifier.h | 2 +- src/PyHelper.hpp | 4 +- src/PyWrap.cc | 185 +++++++++++++++----------------------------- src/PyWrap.h | 25 ++---- src/STree.cc | 6 +- src/STree.h | 1 + src/example.cpp | 3 - src/main.cc | 3 +- 9 files changed, 104 insertions(+), 181 deletions(-) diff --git a/src/PyClassifier.cc b/src/PyClassifier.cc index 872560b..9eaa973 100644 --- a/src/PyClassifier.cc +++ b/src/PyClassifier.cc @@ -1,8 +1,6 @@ #include "PyClassifier.h" #include -#include #include -//#include "tensorflow/python/lib/core/py_func.h" #include namespace pywrap { @@ -13,35 +11,20 @@ namespace pywrap { pyWrap = PyWrap::GetInstance(); pyWrap->importClass(module, className); } - PyClassifier::~PyClassifier() { std::cout << "Cleaning Classifier" << std::endl; pyWrap->clean(module, className); std::cout << "Classifier cleaned" << std::endl; } - PyObject* PyClassifier::toPyObject(torch::Tensor& data_tensor) - { - - // return torch::utils::tensor_to_numpy(data_tensor); - return THPVariable_Wrap(data_tensor); - //auto data_numpy = np::from_data(data_tensor.data_ptr(), np::dtype::get_builtin(), p::make_tuple(m, n), p::make_tuple(sizeof(data_tensor.dtype()) * 2 * n, sizeof(data_tensor.dtype()) * 2), p::object()); - // PyObject* numpyObject = data_numpy.ptr(); - - // return numpyObject; - } - // PyObject* PyClassifier::toPyObjecty(torch::Tensor& data_tensor) - // { - // //return THPVariable_Wrap(tensor); - // auto y_numpy = np::from_data(data_tensor.data_ptr(), np::dtype::get_builtin(), p::make_tuple(m), p::make_tuple(sizeof(data_tensor.dtype()) * 2), p::object()); - // PyObject* numpyObject = y_numpy.ptr(); - - // } std::string PyClassifier::version() { return pyWrap->version(module, className); } - + std::string PyClassifier::graph() + { + return pyWrap->graph(module, className); + } std::string PyClassifier::callMethodString(const std::string& method) { return pyWrap->callMethodString(module, className, method); @@ -53,28 +36,32 @@ namespace pywrap { } PyClassifier& PyClassifier::fit(torch::Tensor& X, torch::Tensor& y, const std::vector& features, const std::string& className, std::map>& states) { - std::cout << "Converting X to PyObject" << std::endl; + std::cout << "PyClassifier:fit:Converting X to PyObject" << std::endl; std::cout << "X.defined() = " << X.defined() << std::endl; - //std::cout << "X.pyobj() = " << X.pyobj() << std::endl; - //PyObject* Xp = torch::utils::tensor_to_numpy(X); - auto XX = X.transpose(0, 1); - int m = XX.size(0); - int n = XX.size(1); - auto data_numpy = np::from_data(XX.data_ptr(), np::dtype::get_builtin(), p::make_tuple(m, n), p::make_tuple(sizeof(XX.dtype()) * 2 * n, sizeof(XX.dtype()) * 2), p::object()); + int m = X.size(0); + int n = X.size(1); + auto data_numpy = np::from_data(X.data_ptr(), np::dtype::get_builtin(), p::make_tuple(m, n), p::make_tuple(sizeof(X.dtype()) * 2 * n, sizeof(X.dtype()) * 2), p::object()); + data_numpy = data_numpy.transpose(); print_array(data_numpy); CPyObject Xp = data_numpy.ptr(); - std::cout << "Converting y to PyObject" << std::endl; - auto y_numpy = np::from_data(y.data_ptr(), np::dtype::get_builtin(), p::make_tuple(m), p::make_tuple(sizeof(y.dtype()) * 2), p::object()); + std::cout << "PyClassifier:fit:Converting y to PyObject" << std::endl; + auto y_numpy = np::from_data(y.data_ptr(), np::dtype::get_builtin(), p::make_tuple(n), p::make_tuple(sizeof(y.dtype()) * 2), p::object()); + print_array(y_numpy); CPyObject yp = y_numpy.ptr(); - std::cout << "Calling fit" << std::endl; + std::cout << "PyClassifier:fit:Calling fit" << std::endl; pyWrap->fit(module, this->className, Xp, yp); return *this; } torch::Tensor PyClassifier::predict(torch::Tensor& X) { - CPyObject Xp = toPyObject(X); + int m = X.size(0); + int n = X.size(1); + auto data_numpy = np::from_data(X.data_ptr(), np::dtype::get_builtin(), p::make_tuple(m, n), p::make_tuple(sizeof(X.dtype()) * 2 * n, sizeof(X.dtype()) * 2), p::object()); + data_numpy = data_numpy.transpose(); + print_array(data_numpy); + CPyObject Xp = data_numpy.ptr(); auto PyResult = pyWrap->predict(module, className, Xp); - auto result = THPVariable_Unpack(PyResult); + auto result = torch::tensor({ 1,2,3 }); return result; } double PyClassifier::score(torch::Tensor& X, torch::Tensor& y) @@ -95,5 +82,4 @@ namespace pywrap { auto result = pyWrap->score(module, className, Xp, yp); return result; } - -} /* namespace PyWrap */ \ No newline at end of file +} /* namespace pywrap */ \ No newline at end of file diff --git a/src/PyClassifier.h b/src/PyClassifier.h index 7e9acab..af02faf 100644 --- a/src/PyClassifier.h +++ b/src/PyClassifier.h @@ -15,9 +15,9 @@ namespace pywrap { torch::Tensor predict(torch::Tensor& X); double score(torch::Tensor& X, torch::Tensor& y); std::string version(); + std::string graph(); std::string callMethodString(const std::string& method); private: - PyObject* toPyObject(torch::Tensor& tensor); PyWrap* pyWrap; std::string module; std::string className; diff --git a/src/PyHelper.hpp b/src/PyHelper.hpp index 8b8062c..2404f6f 100644 --- a/src/PyHelper.hpp +++ b/src/PyHelper.hpp @@ -3,7 +3,7 @@ #pragma once // Code taken and adapted from // https ://www.codeproject.com/Articles/820116/Embedding-Python-program-in-a-C-Cplusplus-code - +#include #include #include @@ -14,12 +14,14 @@ namespace pywrap { public: CPyInstance() { + std::cout << "PyHelper:Initializing Python interpreter" << std::endl; Py_Initialize(); np::initialize(); } ~CPyInstance() { + std::cout << "PyHelper:Finalizing Python interpreter" << std::endl; Py_Finalize(); } }; diff --git a/src/PyWrap.cc b/src/PyWrap.cc index 099f915..2bc7215 100644 --- a/src/PyWrap.cc +++ b/src/PyWrap.cc @@ -10,6 +10,7 @@ namespace pywrap { namespace np = boost::python::numpy; PyWrap* PyWrap::wrapper = nullptr; std::mutex PyWrap::mutex; + CPyInstance* PyWrap::pyInstance = nullptr; PyWrap* PyWrap::GetInstance() { @@ -17,27 +18,23 @@ namespace pywrap { if (wrapper == nullptr) { std::cout << "Creando instancia" << std::endl; wrapper = new PyWrap(); + pyInstance = new CPyInstance(); std::cout << "Instancia creada" << std::endl; } return wrapper; } - - PyWrap::PyWrap() + void PyWrap::RemoveInstance() { - PyStatus status = initPython(); - if (PyStatus_Exception(status)) { - throw std::runtime_error("Error initializing Python"); + std::lock_guard lock(mutex); + if (wrapper != nullptr) { + std::cout << "Liberando instancia" << std::endl; + delete pyInstance; + pyInstance = nullptr; + delete wrapper; + wrapper = nullptr; + std::cout << "Instancia liberada" << std::endl; } - np::initialize(); } - - PyWrap::~PyWrap() - { - std::cout << "Destruyendo PyWrap" << std::endl; - Py_Finalize(); - std::cout << "PyWrap destruido" << std::endl; - } - void PyWrap::importClass(const std::string& moduleName, const std::string& className) { std::cout << "Importando clase" << std::endl; @@ -61,7 +58,6 @@ namespace pywrap { moduleClassMap[{moduleName, className}] = { module, classObject, instance }; std::cout << "Clase importada" << std::endl; } - void PyWrap::clean(const std::string& moduleName, const std::string& className) { std::cout << "Limpiando" << std::endl; @@ -71,14 +67,20 @@ namespace pywrap { } std::cout << "--> Limpiando" << std::endl; moduleClassMap.erase(result); + if (PyErr_Occurred()) { + PyErr_Print(); + errorAbort("Error cleaning module " + moduleName + " and class " + className); + } + if (moduleClassMap.empty()) { + RemoveInstance(); + } std::cout << "Limpieza terminada" << std::endl; } - - void PyWrap::errorAbort(const std::string& message) { std::cout << message << std::endl; PyErr_Print(); + RemoveInstance(); exit(1); } PyObject* PyWrap::getClass(const std::string& moduleName, const std::string& className) @@ -93,28 +95,45 @@ namespace pywrap { std::string PyWrap::callMethodString(const std::string& moduleName, const std::string& className, const std::string& method) { std::cout << "Llamando método " << method << std::endl; - CPyObject instance = getClass(moduleName, className); - CPyObject result; - if (!(result = PyObject_CallMethod(instance, method.c_str(), NULL))) - errorAbort("Couldn't call method " + method); - + PyObject* instance = getClass(moduleName, className); + PyObject* result; + try { + if (!(result = PyObject_CallMethod(instance, method.c_str(), NULL))) + errorAbort("Couldn't call method " + method); + } + catch (const std::exception& e) { + std::cerr << e.what() << '\n'; + RemoveInstance(); + exit(1); + } std::string value = PyUnicode_AsUTF8(result); std::cout << "Result: " << value << std::endl; + Py_DECREF(result); return value; } std::string PyWrap::version(const std::string& moduleName, const std::string& className) { return callMethodString(moduleName, className, "version"); } - + std::string PyWrap::graph(const std::string& moduleName, const std::string& className) + { + return callMethodString(moduleName, className, "graph"); + } void PyWrap::fit(const std::string& moduleName, const std::string& className, CPyObject& X, CPyObject& y) { std::cout << "Llamando método fit" << std::endl; CPyObject instance = getClass(moduleName, className); CPyObject result; std::string method = "fit"; - if (!(result = PyObject_CallMethodObjArgs(instance, PyUnicode_FromString(method.c_str()), X, y, NULL))) - errorAbort("Couldn't call method fit"); + try { + if (!(result = PyObject_CallMethodObjArgs(instance, PyUnicode_FromString(method.c_str()), X, y, NULL))) + errorAbort("Couldn't call method fit"); + } + catch (const std::exception& e) { + std::cerr << e.what() << '\n'; + RemoveInstance(); + exit(1); + } } CPyObject PyWrap::predict(const std::string& moduleName, const std::string& className, CPyObject& X) { @@ -122,9 +141,16 @@ namespace pywrap { CPyObject instance = getClass(moduleName, className); CPyObject result; std::string method = "predict"; - if (!(result = PyObject_CallMethodObjArgs(instance, PyUnicode_FromString(method.c_str()), X, NULL))) - errorAbort("Couldn't call method predict"); - return result; // The caller has to decref the result + try { + if (!(result = PyObject_CallMethodObjArgs(instance, PyUnicode_FromString(method.c_str()), X, NULL))) + errorAbort("Couldn't call method predict"); + } + catch (const std::exception& e) { + std::cerr << e.what() << '\n'; + RemoveInstance(); + exit(1); + } + return result; } double PyWrap::score(const std::string& moduleName, const std::string& className, CPyObject& X, CPyObject& y) { @@ -132,100 +158,15 @@ namespace pywrap { CPyObject instance = getClass(moduleName, className); CPyObject result; std::string method = "score"; - if (!(result = PyObject_CallMethodObjArgs(instance, PyUnicode_FromString(method.c_str()), X, y, NULL))) - errorAbort("Couldn't call method score"); + try { + if (!(result = PyObject_CallMethodObjArgs(instance, PyUnicode_FromString(method.c_str()), X, y, NULL))) + errorAbort("Couldn't call method score"); + } + catch (const std::exception& e) { + std::cerr << e.what() << '\n'; + RemoveInstance(); + exit(1); + } return PyFloat_AsDouble(result); } - // void PyWrap::doCommand2() -// { -// PyObject* list = Py_BuildValue("[s]", "Stree"); -// // PyObject* module = PyImport_ImportModuleEx("stree", NULL, NULL, list); -// PyObject* module = PyImport_ImportModule("stree"); -// if (PyErr_Occurred()) { -// PyErr_Print(); -// cout << "Fails to obtain the module.\n"; -// return; -// } -// cout << "Antes de empezar" << endl; -// if (module != nullptr) { -// cout << "Lo consiguió!!!" << endl; -// // dict is a borrowed reference. -// auto pdict = PyModule_GetDict(module); -// if (pdict == nullptr) { -// cout << "Fails to get the dictionary.\n"; -// return; -// } -// Py_DECREF(module); -// PyObject* pKeys = PyDict_Keys(pdict); -// PyObject* pValues = PyDict_Values(pdict); -// map my_map; -// cout << "size: " << PyDict_Size(pdict) << endl; -// char* cstr_key = new char[100]; -// char* cstr_value = new char[500]; -// for (Py_ssize_t i = 0; i < PyDict_Size(pdict); ++i) { -// PyArg_Parse(PyList_GetItem(pKeys, i), "s", &cstr_key); -// PyArg_Parse(PyList_GetItem(pValues, i), "s", &cstr_value); -// //cout << cstr<< " "<< cstr2 < #include #include "PyHelper.hpp" +#pragma once namespace pywrap { /* - Singleton class to handle Python interpreter. + Singleton class to handle Python/numpy interpreter. */ class PyWrap { public: PyWrap(PyWrap& other) = delete; static PyWrap* GetInstance(); + static void RemoveInstance(); void operator=(const PyWrap&) = delete; - ~PyWrap(); - // template T returnMethod(PyObject* result); - // template std::string returnMethod(PyObject* result); - // template int returnMethod(PyObject* result); - // template bool returnMethod(PyObject* result); - // template torch::Tensor returnMethod(PyObject* result) - // { - // // PyObject* THPVariable_Wrap(at::Tensor t); - // // at::Tensor& THPVariable_Unpack(PyObject * obj); - // return THPVariable_Unpack(result); - // }; - // PyObject* callMethodArgs(const std::string& moduleName, const std::string& className, const std::string& method, PyObject* args); + ~PyWrap() = default; void fit(const std::string& moduleName, const std::string& className, CPyObject& X, CPyObject& y); CPyObject predict(const std::string& moduleName, const std::string& className, CPyObject& X); std::string callMethodString(const std::string& moduleName, const std::string& className, const std::string& method); std::string version(const std::string& moduleName, const std::string& className); + std::string graph(const std::string& moduleName, const std::string& className); double score(const std::string& moduleName, const std::string& className, CPyObject& X, CPyObject& y); void clean(const std::string& moduleName, const std::string& className); void importClass(const std::string& moduleName, const std::string& className); - // void doCommand2(); private: - PyWrap(); + PyWrap() = default; PyObject* getClass(const std::string& moduleName, const std::string& className); void errorAbort(const std::string& message); - PyStatus initPython(); + static CPyInstance* pyInstance; static PyWrap* wrapper; static std::mutex mutex; std::map, std::tuple> moduleClassMap; }; -} /* namespace python */ +} /* namespace pywrap */ #endif /* PYWRAP_H */ \ No newline at end of file diff --git a/src/STree.cc b/src/STree.cc index 45d766d..af247b9 100644 --- a/src/STree.cc +++ b/src/STree.cc @@ -2,5 +2,9 @@ namespace pywrap { - + std::string STree::graph() + { + // return callMethodString("graph"); + return PyClassifier::graph(); + } } /* namespace pywrap */ \ No newline at end of file diff --git a/src/STree.h b/src/STree.h index b442a61..f2e1c37 100644 --- a/src/STree.h +++ b/src/STree.h @@ -7,6 +7,7 @@ namespace pywrap { public: STree() : PyClassifier("stree", "Stree") {}; ~STree() = default; + std::string graph(); }; } /* namespace pywrap */ diff --git a/src/example.cpp b/src/example.cpp index 2928540..278f040 100644 --- a/src/example.cpp +++ b/src/example.cpp @@ -13,13 +13,11 @@ namespace pywrap { PyErr_Print(); exit(1); } - void print_array(np::ndarray& array) { std::cout << "Array: " << std::endl; std::cout << p::extract(p::str(array)) << std::endl; } - np::ndarray to_numpy_matrix(torch::Tensor& input_data, np::dtype numpy_dtype) { p::tuple shape = p::make_tuple(input_data.size(0), input_data.size(1)); @@ -135,7 +133,6 @@ int main(int argc, char** argv) CPyObject result; if (!(result = PyObject_CallMethod(instance, method.c_str(), NULL))) errorAbort("Couldn't call method " + method); - std::string value = PyUnicode_AsUTF8(result); cout << "Version: " << value << endl; cout << "Calling fit" << endl; diff --git a/src/main.cc b/src/main.cc index 1920650..946e491 100644 --- a/src/main.cc +++ b/src/main.cc @@ -46,7 +46,8 @@ int main(int argc, char* argv[]) auto stree = pywrap::STree(); stree.version(); auto svc = pywrap::SVC(); - svc.version(); + //svc.version(); + cout << "Graph: " << stree.graph() << endl; stree.version(); cout << string(80, '-') << endl; cout << "X: " << X.sizes() << endl;