#ifndef NETGEN_CORE_PYTHON_NGCORE_HPP #define NETGEN_CORE_PYTHON_NGCORE_HPP #include "ngcore_api.hpp" // for operator new #include #include #include #include #include "array.hpp" #include "table.hpp" #include "archive.hpp" #include "flags.hpp" #include "ngcore_api.hpp" #include "profiler.hpp" namespace py = pybind11; //////////////////////////////////////////////////////////////////////////////// // automatic conversion of python list to Array<> namespace pybind11 { namespace detail { template struct ngcore_list_caster { using value_conv = make_caster; bool load(handle src, bool convert) { if (!isinstance(src) || isinstance(src)) return false; auto s = reinterpret_borrow(src); value.SetSize(s.size()); value.SetSize0(); for (auto it : s) { value_conv conv; if (!conv.load(it, convert)) return false; value.Append(cast_op(std::move(conv))); } return true; } public: template static handle cast(T &&src, return_value_policy policy, handle parent) { if (!std::is_lvalue_reference::value) policy = return_value_policy_override::policy(policy); list l(src.Size()); size_t index = 0; for (auto &&value : src) { auto value_ = reinterpret_steal(value_conv::cast(forward_like(value), policy, parent)); if (!value_) return handle(); PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference } return l.release(); } PYBIND11_TYPE_CASTER(Type, _("Array[") + value_conv::name + _("]")); }; template struct type_caster> : ngcore_list_caster, Type> { }; /* template struct type_caster>> { template static handle cast(T &&src, return_value_policy policy, handle parent) { std::cout << "handle called with type src = " << typeid(src).name() << std::endl; return handle(); // what so ever } PYBIND11_TYPE_CASTER(Type, _("Table[") + make_caster::name + _("]")); }; */ } // namespace detail } // namespace pybind11 //////////////////////////////////////////////////////////////////////////////// namespace ngcore { NGCORE_API extern bool ngcore_have_numpy; NGCORE_API extern bool parallel_pickling; // Python class name type traits template struct PyNameTraits { static const std::string & GetName() { static const std::string name = typeid(T).name(); return name; } }; template std::string GetPyName(const char *prefix = 0) { std::string s; if(prefix) s = std::string(prefix); s+= PyNameTraits::GetName(); return s; } template<> struct PyNameTraits { static std::string GetName() { return "I"; } }; template<> struct PyNameTraits { static std::string GetName() { return "U"; } }; template<> struct PyNameTraits { static std::string GetName() { return "F"; } }; template<> struct PyNameTraits { static std::string GetName() { return "D"; } }; template<> struct PyNameTraits { static std::string GetName() { return "S"; } }; template struct PyNameTraits> { static std::string GetName() { return std::string("sp_")+GetPyName(); } }; template Array makeCArray(const py::object& obj) { Array arr; if(py::isinstance(obj)) for(auto& val : py::cast(obj)) arr.Append(py::cast(val)); else if(py::isinstance(obj)) for(auto& val : py::cast(obj)) arr.Append(py::cast(val)); else throw py::type_error("Cannot convert Python object to C Array"); return arr; } namespace detail { template struct HasPyFormat { private: template static auto check(T2*) -> std::enable_if_t>().format()), std::string>, std::true_type>; static auto check(...) -> std::false_type; public: static constexpr bool value = decltype(check((T*) nullptr))::value; }; } // namespace detail template ::index_type> void ExportArray (py::module &m) { using TFlat = FlatArray; using TArray = Array; std::string suffix = GetPyName() + "_" + GetPyName(); std::string fname = std::string("FlatArray_") + suffix; auto flatarray_class = py::class_(m, fname.c_str(), py::buffer_protocol()) .def ("__len__", [] ( TFlat &self ) { return self.Size(); } ) .def ("__getitem__", [](TFlat & self, TIND i) -> T& { static constexpr int base = IndexBASE(); if (i < base || i >= self.Size()+base) throw py::index_error(); return self[i]; }, py::return_value_policy::reference) .def ("__setitem__", [](TFlat & self, TIND i, T val) -> T& { static constexpr int base = IndexBASE(); if (i < base || i >= self.Size()+base) throw py::index_error(); self[i] = val; return self[i]; }, py::return_value_policy::reference) .def ("__setitem__", [](TFlat & self, py::slice slice, T val) { size_t start, stop, step, slicelength; if (!slice.compute(self.Size(), &start, &stop, &step, &slicelength)) throw py::error_already_set(); static constexpr int base = IndexBASE(); if (start < base || start+(slicelength-1)*step >= self.Size()+base) throw py::index_error(); for (size_t i = 0; i < slicelength; i++, start+=step) self[start] = val; }) .def("__iter__", [] ( TFlat & self) { return py::make_iterator (self.begin(),self.end()); }, py::keep_alive<0,1>()) // keep array alive while iterator is used .def("__str__", [](TFlat& self) { return ToString(self); }) ; if constexpr (detail::HasPyFormat::value) { if(ngcore_have_numpy && !py::detail::npy_format_descriptor::dtype().is_none()) { flatarray_class .def_buffer([](TFlat& self) { return py::buffer_info( self.Addr(0), sizeof(T), py::format_descriptor::format(), 1, { self.Size() }, { sizeof(T) * (self.Addr(1) - self.Addr(0)) }); }) .def("NumPy", [](py::object self) { return py::module::import("numpy") .attr("frombuffer")(self, py::detail::npy_format_descriptor::dtype()); }) ; } } std::string aname = std::string("Array_") + suffix; py::class_(m, aname.c_str()) .def(py::init([] (size_t n) { return new TArray(n); }),py::arg("n"), "Makes array of given length") .def(py::init([] (std::vector const & x) { size_t s = x.size(); TArray tmp(s); for (size_t i : Range(tmp)) tmp[TIND(i)] = x[i]; return tmp; }), py::arg("vec"), "Makes array with given list of elements") ; } template void ExportTable (py::module &m) { py::class_, std::shared_ptr>> (m, ("Table_"+GetPyName()).c_str()) .def(py::init([] (py::list blocks) { size_t size = py::len(blocks); Array cnt(size); size_t i = 0; for (auto block : blocks) cnt[i++] = py::len(block); i = 0; Table blocktable(cnt); for (auto block : blocks) { auto row = blocktable[i++]; size_t j = 0; for (auto val : block) row[j++] = val.cast(); } // cout << "blocktable = " << *blocktable << endl; return blocktable; }), py::arg("blocks"), "a list of lists") .def ("__len__", [] (Table &self ) { return self.Size(); } ) .def ("__getitem__", [](Table & self, size_t i) -> FlatArray { if (i >= self.Size()) throw py::index_error(); return self[i]; }) .def("__str__", [](Table & self) { return ToString(self); }) ; } void NGCORE_API SetFlag(Flags &flags, std::string s, py::object value); // Parse python kwargs to flags Flags NGCORE_API CreateFlagsFromKwArgs(const py::kwargs& kwargs, py::object pyclass = py::none(), py::list info = py::list()); // Create python dict from kwargs py::dict NGCORE_API CreateDictFromFlags(const Flags& flags); // *************** Archiving functionality ************** template Archive& Archive :: Shallow(T& val) { static_assert(detail::is_any_pointer, "ShallowArchive must be given pointer type!"); #ifdef NETGEN_PYTHON if(shallow_to_python) { if(is_output) ShallowOutPython(pybind11::cast(val)); else { pybind11::object obj; ShallowInPython(obj); val = pybind11::cast(obj); } } else #endif // NETGEN_PYTHON *this & val; return *this; } template class NGCORE_API_EXPORT PyArchive : public ARCHIVE { private: pybind11::list lst; size_t index = 0; std::map version_needed; protected: using ARCHIVE::stream; using ARCHIVE::version_map; using ARCHIVE::logger; public: PyArchive(const pybind11::object& alst = pybind11::none()) : ARCHIVE(std::make_shared()), lst(alst.is_none() ? pybind11::list() : pybind11::cast(alst)) { ARCHIVE::shallow_to_python = true; if(Input()) { stream = std::make_shared (pybind11::cast(lst[pybind11::len(lst)-1])); *this & version_needed; logger->debug("versions needed for unpickling = {}", version_needed); for(auto& libversion : version_needed) if(libversion.second > GetLibraryVersion(libversion.first)) throw Exception("Error in unpickling data:\nLibrary " + libversion.first + " must be at least " + libversion.second.to_string()); stream = std::make_shared (pybind11::cast(lst[pybind11::len(lst)-2])); *this & version_map; stream = std::make_shared (pybind11::cast(lst[pybind11::len(lst)-3])); } } void NeedsVersion(const std::string& library, const std::string& version) override { if(Output()) { logger->debug("Need version {} of library {}.", version, library); version_needed[library] = version_needed[library] > version ? version_needed[library] : version; } } using ARCHIVE::Output; using ARCHIVE::Input; using ARCHIVE::FlushBuffer; using ARCHIVE::operator&; using ARCHIVE::operator<<; using ARCHIVE::GetVersion; void ShallowOutPython(const pybind11::object& val) override { lst.append(val); } void ShallowInPython(pybind11::object& val) override { val = lst[index++]; } pybind11::list WriteOut() { auto version_runtime = GetLibraryVersions(); FlushBuffer(); lst.append(pybind11::bytes(std::static_pointer_cast(stream)->str())); stream = std::make_shared(); *this & version_runtime; FlushBuffer(); lst.append(pybind11::bytes(std::static_pointer_cast(stream)->str())); stream = std::make_shared(); logger->debug("Writeout version needed = {}", version_needed); *this & version_needed; FlushBuffer(); lst.append(pybind11::bytes(std::static_pointer_cast(stream)->str())); return lst; } }; template auto NGSPickle() { return pybind11::pickle([](T* self) { PyArchive ar; ar.SetParallel(parallel_pickling); ar & self; auto output = pybind11::make_tuple(ar.WriteOut()); return output; }, [](const pybind11::tuple & state) { T* val = nullptr; PyArchive ar(state[0]); ar & val; return val; }); } } // namespace ngcore #endif // NETGEN_CORE_PYTHON_NGCORE_HPP