diff --git a/src/python/histogram.cpp b/src/python/histogram.cpp index 97eace76..33ffd869 100644 --- a/src/python/histogram.cpp +++ b/src/python/histogram.cpp @@ -26,9 +26,16 @@ #define BOOST_HISTOGRAM_AXIS_LIMIT 32 #endif + namespace boost { namespace histogram { +#ifdef HAVE_NUMPY +auto array_cast = [](python::handle<>& h) { + return python::downcast(h.get()); +}; +#endif + struct axis_visitor : public static_visitor { template python::object operator()(const T &t) const { return python::object(t); @@ -46,15 +53,13 @@ python::object histogram_axis(const dynamic_histogram<> &self, int i) { } python::object histogram_init(python::tuple args, python::dict kwargs) { - using namespace python; - using python::tuple; - object self = args[0]; - object pyinit = self.attr("__init__"); + python::object self = args[0]; + python::object pyinit = self.attr("__init__"); if (kwargs) { PyErr_SetString(PyExc_RuntimeError, "no keyword arguments allowed"); - throw_error_already_set(); + python::throw_error_already_set(); } const unsigned dim = len(args) - 1; @@ -62,129 +67,117 @@ python::object histogram_init(python::tuple args, python::dict kwargs) { // normal constructor std::vector::axis_type> axes; for (unsigned i = 0; i < dim; ++i) { - object pa = args[i + 1]; - extract> er(pa); + python::object pa = args[i + 1]; + python::extract> er(pa); if (er.check()) { axes.push_back(er()); continue; } - extract> ep(pa); + python::extract> ep(pa); if (ep.check()) { axes.push_back(ep()); continue; } - extract> ev(pa); + python::extract> ev(pa); if (ev.check()) { axes.push_back(ev()); continue; } - extract ei(pa); + python::extract ei(pa); if (ei.check()) { axes.push_back(ei()); continue; } - extract ec(pa); + python::extract ec(pa); if (ec.check()) { axes.push_back(ec()); continue; } std::string msg = "require an axis object, got "; - msg += extract(pa.attr("__class__").attr("__name__"))(); + msg += python::extract(pa.attr("__class__").attr("__name__"))(); PyErr_SetString(PyExc_TypeError, msg.c_str()); - throw_error_already_set(); + python::throw_error_already_set(); } dynamic_histogram<> h(axes.begin(), axes.end()); return pyinit(h); } python::object histogram_fill(python::tuple args, python::dict kwargs) { - using namespace python; + const unsigned nargs = python::len(args); + dynamic_histogram<> &self = python::extract &>(args[0]); - const unsigned nargs = len(args); - dynamic_histogram<> &self = extract &>(args[0]); - - object ow; + python::object ow; if (kwargs) { if (len(kwargs) > 1 || !kwargs.has_key("w")) { PyErr_SetString(PyExc_RuntimeError, "only keyword w allowed"); - throw_error_already_set(); + python::throw_error_already_set(); } ow = kwargs.get("w"); } #ifdef HAVE_NUMPY if (nargs == 2) { - object o = args[1]; + python::object o = args[1]; if (PySequence_Check(o.ptr())) { - PyArrayObject *a = reinterpret_cast( - PyArray_FROM_OTF(o.ptr(), NPY_DOUBLE, NPY_ARRAY_IN_ARRAY)); - if (!a) { - PyErr_SetString(PyExc_ValueError, - "could not convert sequence into array"); - throw_error_already_set(); - } + // exception is thrown automatically if + python::handle<> a(PyArray_FROM_OTF(o.ptr(), NPY_DOUBLE, NPY_ARRAY_IN_ARRAY)); - npy_intp *dims = PyArray_DIMS(a); - switch (PyArray_NDIM(a)) { + npy_intp *dims = PyArray_DIMS(array_cast(a)); + switch (PyArray_NDIM(array_cast(a))) { case 1: if (self.dim() > 1) { PyErr_SetString(PyExc_ValueError, "array has to be two-dimensional"); - throw_error_already_set(); + python::throw_error_already_set(); } break; case 2: if (self.dim() != dims[1]) { PyErr_SetString(PyExc_ValueError, "size of second dimension does not match"); - throw_error_already_set(); + python::throw_error_already_set(); } break; default: PyErr_SetString(PyExc_ValueError, "array has wrong dimension"); - throw_error_already_set(); + python::throw_error_already_set(); } if (!ow.is_none()) { if (PySequence_Check(ow.ptr())) { - PyArrayObject *aw = reinterpret_cast( - PyArray_FROM_OTF(ow.ptr(), NPY_DOUBLE, NPY_ARRAY_IN_ARRAY)); - if (!aw) { - PyErr_SetString(PyExc_ValueError, - "could not convert sequence into array"); - throw_error_already_set(); - } - if (PyArray_NDIM(aw) != 1) { + // exception is thrown automatically if handle below receives null + python::handle<> aw( + PyArray_FROM_OTF(ow.ptr(), NPY_DOUBLE, NPY_ARRAY_IN_ARRAY)); + + if (PyArray_NDIM(array_cast(aw)) != 1) { PyErr_SetString(PyExc_ValueError, "array has to be one-dimensional"); - throw_error_already_set(); + python::throw_error_already_set(); } - if (PyArray_DIMS(aw)[0] != dims[0]) { + if (PyArray_DIMS(array_cast(aw))[0] != dims[0]) { PyErr_SetString(PyExc_ValueError, "sizes do not match"); - throw_error_already_set(); + python::throw_error_already_set(); } for (unsigned i = 0; i < dims[0]; ++i) { - double *v = reinterpret_cast(PyArray_GETPTR1(a, i)); - double *w = reinterpret_cast(PyArray_GETPTR1(aw, i)); + double *v = reinterpret_cast(PyArray_GETPTR1(array_cast(a), i)); + double *w = reinterpret_cast(PyArray_GETPTR1(array_cast(aw), i)); self.wfill(*w, v, v + self.dim()); } - Py_DECREF(aw); } else { PyErr_SetString(PyExc_ValueError, "w is not a sequence"); - throw_error_already_set(); + python::throw_error_already_set(); } } else { for (unsigned i = 0; i < dims[0]; ++i) { - double *v = reinterpret_cast(PyArray_GETPTR1(a, i)); + double *v = reinterpret_cast(PyArray_GETPTR1(array_cast(a), i)); self.fill(v, v + self.dim()); } } - Py_DECREF(a); - return object(); + return python::object(); } } #endif @@ -192,88 +185,85 @@ python::object histogram_fill(python::tuple args, python::dict kwargs) { const unsigned dim = nargs - 1; if (dim != self.dim()) { PyErr_SetString(PyExc_RuntimeError, "wrong number of arguments"); - throw_error_already_set(); + python::throw_error_already_set(); } if (dim > BOOST_HISTOGRAM_AXIS_LIMIT) { std::ostringstream os; os << "too many axes, maximum is " << BOOST_HISTOGRAM_AXIS_LIMIT; PyErr_SetString(PyExc_RuntimeError, os.str().c_str()); - throw_error_already_set(); + python::throw_error_already_set(); } double v[BOOST_HISTOGRAM_AXIS_LIMIT]; for (unsigned i = 0; i < dim; ++i) - v[i] = extract(args[1 + i]); + v[i] = python::extract(args[1 + i]); if (ow.is_none()) { self.fill(v, v + self.dim()); } else { - const double w = extract(ow); + const double w = python::extract(ow); self.wfill(w, v, v + self.dim()); } - return object(); + return python::object(); } python::object histogram_value(python::tuple args, python::dict kwargs) { - using namespace python; - const dynamic_histogram<> &self = - extract &>(args[0]); + const dynamic_histogram<> & self = python::extract &>(args[0]); const unsigned dim = len(args) - 1; if (self.dim() != dim) { PyErr_SetString(PyExc_RuntimeError, "wrong number of arguments"); - throw_error_already_set(); + python::throw_error_already_set(); } if (dim > BOOST_HISTOGRAM_AXIS_LIMIT) { std::ostringstream os; os << "too many axes, maximum is " << BOOST_HISTOGRAM_AXIS_LIMIT; PyErr_SetString(PyExc_RuntimeError, os.str().c_str()); - throw_error_already_set(); + python::throw_error_already_set(); } if (kwargs) { PyErr_SetString(PyExc_RuntimeError, "no keyword arguments allowed"); - throw_error_already_set(); + python::throw_error_already_set(); } int idx[BOOST_HISTOGRAM_AXIS_LIMIT]; for (unsigned i = 0; i < self.dim(); ++i) - idx[i] = extract(args[1 + i]); + idx[i] = python::extract(args[1 + i]); - return object(self.value(idx + 0, idx + self.dim())); + return python::object(self.value(idx + 0, idx + self.dim())); } python::object histogram_variance(python::tuple args, python::dict kwargs) { - using namespace python; const dynamic_histogram<> &self = - extract &>(args[0]); + python::extract &>(args[0]); const unsigned dim = len(args) - 1; if (self.dim() != dim) { PyErr_SetString(PyExc_RuntimeError, "wrong number of arguments"); - throw_error_already_set(); + python::throw_error_already_set(); } if (dim > BOOST_HISTOGRAM_AXIS_LIMIT) { std::ostringstream os; os << "too many axes, maximum is " << BOOST_HISTOGRAM_AXIS_LIMIT; PyErr_SetString(PyExc_RuntimeError, os.str().c_str()); - throw_error_already_set(); + python::throw_error_already_set(); } if (kwargs) { PyErr_SetString(PyExc_RuntimeError, "no keyword arguments allowed"); - throw_error_already_set(); + python::throw_error_already_set(); } int idx[BOOST_HISTOGRAM_AXIS_LIMIT]; for (unsigned i = 0; i < self.dim(); ++i) - idx[i] = extract(args[1 + i]); + idx[i] = python::extract(args[1 + i]); - return object(self.variance(idx + 0, idx + self.dim())); + return python::object(self.variance(idx + 0, idx + self.dim())); } std::string histogram_repr(const dynamic_histogram<> &h) { @@ -282,8 +272,8 @@ std::string histogram_repr(const dynamic_histogram<> &h) { return os.str(); } -struct storage_access { #ifdef HAVE_NUMPY +struct storage_access { using mp_int = adaptive_storage<>::mp_int; using weight = adaptive_storage<>::weight; template @@ -333,14 +323,14 @@ struct storage_access { for (int i = 0; i < dim; ++i) { shapes2[i] = python::extract(shapes[i]); } - PyObject *ptr = PyArray_SimpleNew(dim, shapes2, NPY_UINT8); + python::handle<> a(PyArray_SimpleNew(dim, shapes2, NPY_UINT8)); for (int i = 0; i < dim; ++i) { - PyArray_STRIDES((PyArrayObject *)ptr)[i] = python::extract(strides[i]); + PyArray_STRIDES(array_cast(a))[i] = python::extract(strides[i]); } - auto *buf = static_cast(PyArray_DATA((PyArrayObject *)ptr)); + auto *buf = static_cast(PyArray_DATA(array_cast(a))); std::fill(buf, buf + b.size, uint8_t(0)); - PyArray_CLEARFLAGS((PyArrayObject*)ptr, NPY_ARRAY_WRITEABLE); - return python::object(python::handle<>(ptr)); + PyArray_CLEARFLAGS(array_cast(a), NPY_ARRAY_WRITEABLE); + return python::object(a); } python::object operator()(const array& b) const { // cannot pass cpp_int to numpy; make new @@ -350,16 +340,16 @@ struct storage_access { for (int i = 0; i < dim; ++i) { shapes2[i] = python::extract(shapes[i]); } - PyObject *ptr = PyArray_SimpleNew(dim, shapes2, NPY_DOUBLE); + python::handle<> a(PyArray_SimpleNew(dim, shapes2, NPY_DOUBLE)); for (int i = 0; i < dim; ++i) { - PyArray_STRIDES((PyArrayObject *)ptr)[i] = python::extract(strides[i]); + PyArray_STRIDES(array_cast(a))[i] = python::extract(strides[i]); } - auto *buf = static_cast(PyArray_DATA((PyArrayObject *)ptr)); + auto *buf = static_cast(PyArray_DATA(array_cast(a))); for (std::size_t i = 0; i < b.size; ++i) { buf[i] = static_cast(b[i]); } - PyArray_CLEARFLAGS((PyArrayObject*)ptr, NPY_ARRAY_WRITEABLE); - return python::object(python::handle<>(ptr)); + PyArray_CLEARFLAGS(array_cast(a), NPY_ARRAY_WRITEABLE); + return python::object(a); } }; @@ -393,28 +383,26 @@ struct storage_access { d["data"] = apply_visitor(data_visitor(shapes, strides), b); return d; } -#endif }; +#endif void register_histogram() { - using namespace python; - using python::arg; - docstring_options dopt(true, true, false); + python::docstring_options dopt(true, true, false); - class_, boost::shared_ptr>>( - "histogram", "N-dimensional histogram for real-valued data.", no_init) - .def("__init__", raw_function(histogram_init), + python::class_, boost::shared_ptr>>( + "histogram", "N-dimensional histogram for real-valued data.", python::no_init) + .def("__init__", python::raw_function(histogram_init), ":param axis args: axis objects" "\nPass one or more axis objects to define" "\nthe dimensions of the dynamic_histogram<>.") // shadowed C++ ctors - .def(init &>()) + .def(python::init &>()) #ifdef HAVE_NUMPY .add_property("__array_interface__", &storage_access::array_interface) #endif .def("__len__", &dynamic_histogram<>::dim) .def("__getitem__", histogram_axis) - .def("fill", raw_function(histogram_fill), + .def("fill", python::raw_function(histogram_fill), "Pass a sequence of values with a length n is" "\nequal to the dimensions of the histogram," "\nand optionally a weight w for this fill" @@ -425,16 +413,16 @@ void register_histogram() { "\nthe number of tuples, and optionally" "\nanother a second 1d-array w of shape (n,).") .add_property("sum", &dynamic_histogram<>::sum) - .def("value", raw_function(histogram_value), + .def("value", python::raw_function(histogram_value), ":param int args: indices of the bin" "\n:return: count for the bin") - .def("variance", raw_function(histogram_variance), + .def("variance", python::raw_function(histogram_variance), ":param int args: indices of the bin" "\n:return: variance estimate for the bin") .def("__repr__", histogram_repr, ":returns: string representation of the histogram") - .def(self == self) - .def(self += self) + .def(python::self == python::self) + .def(python::self += python::self) .def_pickle(serialization_suite>()); }