From a1cecfb6989bc1a6e2e917f1cfd9cc7f81cfccef Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Mon, 6 Aug 2018 22:58:29 +0200 Subject: [PATCH] wip --- .../histogram/axis/ostream_operators.hpp | 2 +- include/boost/histogram/axis/types.hpp | 4 +- include/boost/histogram/detail/axes.hpp | 93 +++++++++++++++---- include/boost/histogram/detail/utility.hpp | 12 --- include/boost/histogram/histogram.hpp | 15 +-- src/python/axis.cpp | 2 +- src/python/histogram.cpp | 8 ++ 7 files changed, 96 insertions(+), 40 deletions(-) diff --git a/include/boost/histogram/axis/ostream_operators.hpp b/include/boost/histogram/axis/ostream_operators.hpp index 472eec0b..678ec2c7 100644 --- a/include/boost/histogram/axis/ostream_operators.hpp +++ b/include/boost/histogram/axis/ostream_operators.hpp @@ -80,7 +80,7 @@ template std::ostream& operator<<(std::ostream& os, const circular& a) { os << "circular(" << a.size(); if (a.phase() != 0.0) { os << ", phase=" << a.phase(); } - if (a.perimeter() != circular::two_pi) { + if (a.perimeter() != circular::two_pi()) { os << ", perimeter=" << a.perimeter(); } if (!a.label().empty()) { diff --git a/include/boost/histogram/axis/types.hpp b/include/boost/histogram/axis/types.hpp index 4302d10b..2935fc8e 100644 --- a/include/boost/histogram/axis/types.hpp +++ b/include/boost/histogram/axis/types.hpp @@ -214,7 +214,7 @@ public: using bin_type = interval_view; // two_pi can be found in boost/math, but it is defined here to reduce deps - static constexpr value_type two_pi = 6.283185307179586; + static value_type two_pi() { return 6.283185307179586; } /** Constructor for n bins with an optional offset. * @@ -224,7 +224,7 @@ public: * \param label description of the axis. */ explicit circular(unsigned n, value_type phase = 0.0, - value_type perimeter = two_pi, string_view label = {}, + value_type perimeter = two_pi(), string_view label = {}, const allocator_type& a = allocator_type()) : base_type(n, uoflow_type::off, label, a), phase_(phase), diff --git a/include/boost/histogram/detail/axes.hpp b/include/boost/histogram/detail/axes.hpp index 20fd42df..fa971941 100644 --- a/include/boost/histogram/detail/axes.hpp +++ b/include/boost/histogram/detail/axes.hpp @@ -8,8 +8,10 @@ #define _BOOST_HISTOGRAM_DETAIL_AXES_HPP_ #include +#include #include #include +#include #include #include #include @@ -323,33 +325,88 @@ sub_axes, Ns...> make_sub_axes( struct optional_index { std::size_t idx = 0; - std::size_t shape = 0; - operator bool() const { return shape > 0; } + std::size_t stride = 0; + operator bool() const { return stride > 0; } std::size_t operator*() const { return idx; } }; -template -optional_index args_to_index(const static_axes&, const Us&...) { - auto index = optional_index(); - return index; +// the following is highly optimized code that runs in a hot loop; +// please measure the performance impact of changes +inline void linearize(optional_index& out, const int axis_size, + const int axis_shape, int j) noexcept { + BOOST_ASSERT_MSG(out.stride == 0 || (-1 <= j && j <= axis_size), + "index must be in bounds for this algorithm"); + j += (j < 0) * (axis_size + 2); // wrap around if j < 0 + out.idx += j * out.stride; + out.stride *= (j < axis_shape) * axis_shape; // set to 0, if j is invalid } -template -optional_index args_to_index(const dynamic_axes&, const Us&...) { - auto index = optional_index(); - return index; +template +void indices_to_index(optional_index&, const Axes&) noexcept {} + +template +void indices_to_index(optional_index& idx, const Axes& axes, const int j, + const Us... us) { + const auto& a = ::boost::histogram::detail::get(axes); + const auto a_size = a.size(); + const auto a_shape = a.shape(); + idx.stride *= (-1 <= j && j <= a_size); // set to 0, if j is invalid + linearize(idx, a_size, a_shape, j); + indices_to_index<(D + 1)>(idx, axes, us...); } -template -optional_index indices_to_index(const static_axes&, const Us&...) { - auto index = optional_index(); - return index; +template +void args_to_index(optional_index&, const static_axes&) noexcept {} + +template +void args_to_index(optional_index& idx, const static_axes& axes, + const U& u, const Us&... us) { + const auto a_size = std::get(axes).size(); + const auto a_shape = std::get(axes).shape(); + const int j = std::get(axes).index(u); + linearize(idx, a_size, a_shape, j); + args_to_index<(D + 1)>(idx, axes, us...); } -template -optional_index indices_to_index(const dynamic_axes&, const Us&...) { - auto index = optional_index(); - return index; +namespace { +template +struct args_to_index_visitor : public boost::static_visitor { + optional_index& idx; + const T& val; + args_to_index_visitor(optional_index& i, const T& v) : idx(i), val(v) {} + template + void operator()(const Axis& a) const { + impl(std::is_convertible(), a); + } + + template + void impl(std::true_type, const Axis& a) const { + const auto a_size = a.size(); + const auto a_shape = a.shape(); + const auto j = a.index(static_cast(val)); + linearize(idx, a_size, a_shape, j); + } + + template + void impl(std::false_type, const Axis&) const { + throw std::invalid_argument(detail::cat( + "axis ", boost::typeindex::type_id().pretty_name(), + ": argument ", boost::typeindex::type_id().pretty_name(), + " not convertible to value_type ", + boost::typeindex::type_id() + .pretty_name())); + } +}; +} + +template +void args_to_index(optional_index&, const dynamic_axes&) {} + +template +void args_to_index(optional_index& idx, const dynamic_axes& axes, + const U& u, const Us&... us) { + boost::apply_visitor(args_to_index_visitor>(idx, u), axes[D]); + args_to_index<(D + 1)>(idx, axes, us...); } } // namespace detail diff --git a/include/boost/histogram/detail/utility.hpp b/include/boost/histogram/detail/utility.hpp index 1c2084ac..7fd244a3 100644 --- a/include/boost/histogram/detail/utility.hpp +++ b/include/boost/histogram/detail/utility.hpp @@ -15,18 +15,6 @@ namespace boost { namespace histogram { namespace detail { -// the following is highly optimized code that runs in a hot loop; -// please measure the performance impact of changes -inline void lin(std::size_t& out, std::size_t& stride, const int axis_size, - const int axis_shape, int j) noexcept { - BOOST_ASSERT_MSG(stride == 0 || (-1 <= j && j <= axis_size), - "index must be in bounds for this algorithm"); - j += (j < 0) * (axis_size + 2); // wrap around if j < 0 - out += j * stride; - stride *= - (j < axis_shape) * axis_shape; // stride == 0 indicates out-of-range -} - template typename std::enable_if<(is_castable_to_int::value), int>::type indirect_int_cast(T&& t) noexcept { diff --git a/include/boost/histogram/histogram.hpp b/include/boost/histogram/histogram.hpp index c1d93e07..6564f0fb 100644 --- a/include/boost/histogram/histogram.hpp +++ b/include/boost/histogram/histogram.hpp @@ -157,7 +157,8 @@ public: void operator()(const Ts&... ts) { // case with one argument needs special treatment, specialized below detail::dimension_check(axes_, mp11::mp_size_t()); - auto index = detail::args_to_index(axes_, ts...); + auto index = detail::optional_index(); + detail::args_to_index<0>(index, axes_, ts...); if (index) storage_.increase(*index); } @@ -171,7 +172,8 @@ public: void operator()(detail::weight_type&& w, const Ts&... ts) { // case with one argument is ambiguous, is specialized below detail::dimension_check(axes_, mp11::mp_size_t()); - auto index = detail::args_to_index(axes_, ts...); + auto index = detail::optional_index(); + detail::args_to_index<0>(index, axes_, ts...); if (index) storage_.add(*index, w); } @@ -185,15 +187,16 @@ public: const_reference at(const Ts&... ts) const { // case with one argument is ambiguous, is specialized below detail::dimension_check(axes_, mp11::mp_size_t()); - auto index = detail::indices_to_index(axes_, static_cast(ts)...); + auto index = detail::optional_index(); + detail::indices_to_index<0>(index, axes_, static_cast(ts)...); BOOST_ASSERT_MSG(index, "indices out of bounds"); return storage_[*index]; } template - const_reference at(const T&) const { - // check whether we need to unpack argument - return storage_[0]; + const_reference at(const T& t) const { + // check whether we need to unpack argument; + return storage_[at_impl(detail::classify_container(), t)]; } /// Access bin counter at index diff --git a/src/python/axis.cpp b/src/python/axis.cpp index 140958c9..2e0a906e 100644 --- a/src/python/axis.cpp +++ b/src/python/axis.cpp @@ -319,7 +319,7 @@ void register_axis_types() { no_init) .def(init( (arg("self"), arg("bin"), arg("phase") = 0.0, - arg("perimeter") = bha::circular<>::two_pi, arg("label") = ""))) + arg("perimeter") = bha::circular<>::two_pi(), arg("label") = ""))) .def(axis_suite>()); class_>( diff --git a/src/python/histogram.cpp b/src/python/histogram.cpp index 4a36b6cb..014c3d49 100644 --- a/src/python/histogram.cpp +++ b/src/python/histogram.cpp @@ -31,12 +31,17 @@ namespace bh = boost::histogram; namespace bp = boost::python; namespace mp11 = boost::mp11; +using pyhistogram = bh::histogram<>; + class pyhistogram : public bh::histogram<> { using base_type = bh::histogram<>; public: using base_type::base_type; using iterator = bh::histogram<>::const_iterator; + + // pyhistogram(const base_type& h) : base_type(h) {} + // pyhistogram(base_type&& h) : base_type(std::move(h)) {} }; #ifdef HAVE_NUMPY @@ -443,4 +448,7 @@ void register_histogram() { .def(bp::self + double()) .def(double() + bp::self) .def("__repr__", element_repr); + + // bp::implicitly_convertible>(); + // bp::implicitly_convertible, pyhistogram>(); }