added density method to indexed proxy, hide details of axes_buffer

This commit is contained in:
Hans Dembinski 2018-11-28 14:58:47 +01:00
parent 31c762b359
commit a4fe842b4a
10 changed files with 99 additions and 51 deletions

View File

@ -39,7 +39,7 @@ auto project(const histogram<A, S>& h, mp11::mp_size_t<I> n, Ns... ns) {
[&h](auto) { return S(unsafe_access::storage(h).get_allocator()); }, [&h](auto) { return S(unsafe_access::storage(h).get_allocator()); },
[](auto) { return S(); }, 0)); [](auto) { return S(); }, 0));
detail::index_mapper im(h.rank()); detail::index_mapper<A> im(h.rank());
auto iter = im.begin(); auto iter = im.begin();
std::size_t s = 1; std::size_t s = 1;
h.for_each_axis([&](const auto& a) { h.for_each_axis([&](const auto& a) {
@ -87,7 +87,7 @@ auto project(const histogram<A, S>& h, C c) {
axes); axes);
r_axes.reserve(std::distance(begin, end)); r_axes.reserve(std::distance(begin, end));
detail::index_mapper im(h.rank()); detail::index_mapper<A> im(h.rank());
auto iter = im.begin(); auto iter = im.begin();
std::size_t stride = 1; std::size_t stride = 1;
h.for_each_axis([&](const auto& a) { h.for_each_axis([&](const auto& a) {

View File

@ -8,7 +8,6 @@
#define BOOST_HISTOGRAM_ALGORITHM_REDUCE_HPP #define BOOST_HISTOGRAM_ALGORITHM_REDUCE_HPP
#include <boost/assert.hpp> #include <boost/assert.hpp>
#include <boost/container/static_vector.hpp>
#include <boost/histogram/detail/axes.hpp> #include <boost/histogram/detail/axes.hpp>
#include <boost/histogram/detail/index_mapper.hpp> #include <boost/histogram/detail/index_mapper.hpp>
#include <boost/histogram/detail/meta.hpp> #include <boost/histogram/detail/meta.hpp>
@ -66,8 +65,7 @@ reduce_option_type rebin(unsigned merge) { return rebin(0, merge); }
template <typename A, typename S, typename C, typename = detail::requires_iterable<C>> template <typename A, typename S, typename C, typename = detail::requires_iterable<C>>
histogram<A, S> reduce(const histogram<A, S>& h, const C& c) { histogram<A, S> reduce(const histogram<A, S>& h, const C& c) {
auto options = auto options = detail::axes_buffer<A, reduce_option_type>(h.rank());
boost::container::static_vector<reduce_option_type, axis::limit>(h.rank());
for (const auto& o : c) { for (const auto& o : c) {
auto& opt_ref = options[o.iaxis]; auto& opt_ref = options[o.iaxis];
if (opt_ref) throw std::invalid_argument("indices must be unique"); if (opt_ref) throw std::invalid_argument("indices must be unique");
@ -78,7 +76,7 @@ histogram<A, S> reduce(const histogram<A, S>& h, const C& c) {
auto r_axes = detail::make_empty_axes(unsafe_access::axes(h)); auto r_axes = detail::make_empty_axes(unsafe_access::axes(h));
detail::index_mapper_reduce im(h.rank()); detail::index_mapper_reduce<A> im(h.rank());
auto im_iter = im.begin(); auto im_iter = im.begin();
std::size_t stride[2] = {1, 1}; std::size_t stride[2] = {1, 1};
unsigned iaxis = 0; unsigned iaxis = 0;

View File

@ -61,6 +61,19 @@ unsigned extend(const T& t) noexcept {
return t.size() + (opt & option_type::underflow) + (opt & option_type::overflow); return t.size() + (opt & option_type::underflow) + (opt & option_type::overflow);
} }
template <typename T>
double width(const T& t, unsigned idx) {
return detail::static_if<detail::has_method_value<detail::unqual<T>, double>>(
[&](const auto& a) {
using Arg = detail::unqual<detail::arg_type<detail::unqual<decltype(a)>>>;
return detail::static_if<std::is_integral<Arg>>(
[&](const auto&) -> double { return 1; },
[&](const auto& a) -> double { return a.value(idx + 1) - a.value(idx); }, a);
},
[](const auto&) -> double { throw std::runtime_error("axis has no value method"); },
t);
}
} // namespace traits } // namespace traits
} // namespace axis } // namespace axis
} // namespace histogram } // namespace histogram

View File

@ -20,6 +20,18 @@
#include <type_traits> #include <type_traits>
#include <vector> #include <vector>
/* Most of the histogram code is generic and works for any number of axes. Buffers with a
* fixed maximum capacity are used in some places, which have a size equal to the rank of
* a histogram. The buffers are statically allocated to improve performance, which means
* that they need a preset maximum capacity. 32 seems like a safe upper limit for the rank
* (you can nevertheless increase it here if necessary): the simplest non-trivial axis has
* 2 bins; even if counters are used which need only a byte of storage per bin, this still
* corresponds to 4 GB of storage.
*/
#ifndef BOOST_HISTOGRAM_DETAIL_AXES_LIMIT
#define BOOST_HISTOGRAM_DETAIL_AXES_LIMIT 32
#endif
namespace boost { namespace boost {
namespace histogram { namespace histogram {
namespace detail { namespace detail {
@ -151,10 +163,11 @@ void for_each_axis(const T& axes, F&& f) {
for (const auto& x : axes) { axis::visit(std::forward<F>(f), x); } for (const auto& x : axes) { axis::visit(std::forward<F>(f), x); }
} }
template <typename T, typename A> template <typename Axes, typename T>
using make_axes_buffer = boost::container::static_vector< using axes_buffer = boost::container::static_vector<
T, mp11::mp_eval_if_c<!(has_fixed_size<A>::value), mp11::mp_size_t<axis::limit>, T, mp11::mp_eval_if_c<!(has_fixed_size<Axes>::value),
std::tuple_size, A>::value>; mp11::mp_size_t<BOOST_HISTOGRAM_DETAIL_AXES_LIMIT>,
std::tuple_size, Axes>::value>;
template <typename T> template <typename T>
auto make_empty_axes(const T& t) { auto make_empty_axes(const T& t) {

View File

@ -16,16 +16,14 @@ namespace boost {
namespace histogram { namespace histogram {
namespace detail { namespace detail {
struct index_mapper_item { template <typename A>
std::size_t stride[2]; struct index_mapper {
}; struct item {
std::size_t stride[2];
};
using buffer_type = axes_buffer<A, item>;
class index_mapper index_mapper(unsigned dim) : buffer(dim) {}
: public boost::container::static_vector<index_mapper_item, axis::limit> {
public:
std::size_t total = 1;
index_mapper(unsigned dim) : static_vector(dim) {}
template <typename T, typename U> template <typename T, typename U>
void operator()(T& dst, const U& src) { void operator()(T& dst, const U& src) {
@ -42,19 +40,26 @@ public:
dst.add(j, src[i]); dst.add(j, src[i]);
} }
} }
};
struct index_mapper_reduce_item { decltype(auto) begin() { return buffer.begin(); }
std::size_t stride[2]; decltype(auto) end() { return buffer.end(); }
int underflow[2], overflow[2], begin, end, merge;
};
class index_mapper_reduce decltype(auto) operator[](unsigned i) { return buffer[i]; }
: public boost::container::static_vector<index_mapper_reduce_item, axis::limit> {
public: buffer_type buffer;
std::size_t total = 1; std::size_t total = 1;
};
index_mapper_reduce(unsigned dim) : static_vector(dim) {} template <typename A>
class index_mapper_reduce {
public:
struct item {
std::size_t stride[2];
int underflow[2], overflow[2], begin, end, merge;
};
using buffer_type = axes_buffer<A, item>;
index_mapper_reduce(unsigned dim) : buffer(dim) {}
template <typename T, typename U> template <typename T, typename U>
void operator()(T& dst, const U& src) { void operator()(T& dst, const U& src) {
@ -80,6 +85,14 @@ public:
if (!drop) dst.add(j, src[i]); if (!drop) dst.add(j, src[i]);
} }
} }
decltype(auto) begin() { return buffer.begin(); }
decltype(auto) end() { return buffer.end(); }
decltype(auto) operator[](unsigned i) { return buffer[i]; }
buffer_type buffer;
std::size_t total = 1;
}; };
} // namespace detail } // namespace detail
} // namespace histogram } // namespace histogram

View File

@ -10,7 +10,6 @@
#include <boost/container/flat_set.hpp> #include <boost/container/flat_set.hpp>
#include <boost/container/static_vector.hpp> #include <boost/container/static_vector.hpp>
#include <boost/histogram/detail/meta.hpp> #include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/histogram_fwd.hpp> // for axis::limit
namespace boost { namespace boost {
namespace histogram { namespace histogram {
@ -18,7 +17,7 @@ namespace detail {
template <class Iterator> template <class Iterator>
bool is_set(Iterator begin, Iterator end) { bool is_set(Iterator begin, Iterator end) {
using T = iterator_value_type<Iterator>; using T = iterator_value_type<Iterator>;
using C = boost::container::static_vector<T, axis::limit>; using C = boost::container::static_vector<T, BOOST_HISTOGRAM_DETAIL_AXES_LIMIT>;
boost::container::flat_set<T, std::less<T>, C> s(begin, end); boost::container::flat_set<T, std::less<T>, C> s(begin, end);
return static_cast<std::size_t>(std::distance(begin, end)) == s.size(); return static_cast<std::size_t>(std::distance(begin, end)) == s.size();
} }

View File

@ -30,8 +30,9 @@ public:
using axes_type = Axes; using axes_type = Axes;
using storage_type = Storage; using storage_type = Storage;
using value_type = typename storage_type::value_type; using value_type = typename storage_type::value_type;
using iterator = typename storage_type::const_iterator; // for boost::range_iterator // typedefs for boost::range_iterator
using const_iterator = typename storage_type::const_iterator; using iterator = decltype(std::declval<storage_type&>().begin());
using const_iterator = decltype(std::declval<const storage_type&>().begin());
histogram() = default; histogram() = default;
histogram(const histogram& rhs) = default; histogram(const histogram& rhs) = default;

View File

@ -18,21 +18,6 @@ namespace boost {
namespace histogram { namespace histogram {
namespace axis { namespace axis {
/* Most of the histogram code is generic and works for any number of axes. Buffers with a
* fixed maximum capacity are used in some places, which have a size equal to the rank of
* a histogram. The buffers are statically allocated to improve performance, which means
* that they need a preset maximum capacity. 32 seems like a safe upper limit for the rank
* (you can nevertheless increase it here if necessary): the simplest non-trivial axis has
* 2 bins; even if counters are used which need only a byte of storage per bin, this still
* corresponds to 4 GB of storage.
*/
BOOST_ATTRIBUTE_UNUSED static constexpr unsigned limit =
#ifdef BOOST_HISTOGRAM_AXES_LIMIT
BOOST_HISTOGRAM_AXES_LIMIT;
#else
32;
#endif
/// empty metadata type /// empty metadata type
struct null_type {}; struct null_type {};

View File

@ -7,13 +7,13 @@
#ifndef BOOST_HISTOGRAM_INDEXED_HPP #ifndef BOOST_HISTOGRAM_INDEXED_HPP
#define BOOST_HISTOGRAM_INDEXED_HPP #define BOOST_HISTOGRAM_INDEXED_HPP
#include <boost/container/static_vector.hpp>
#include <boost/histogram/axis/traits.hpp> #include <boost/histogram/axis/traits.hpp>
#include <boost/histogram/detail/axes.hpp> #include <boost/histogram/detail/axes.hpp>
#include <boost/histogram/detail/meta.hpp> #include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/histogram_fwd.hpp> #include <boost/histogram/histogram_fwd.hpp>
#include <boost/histogram/unsafe_access.hpp> #include <boost/histogram/unsafe_access.hpp>
#include <boost/iterator/iterator_facade.hpp> #include <boost/iterator/iterator_facade.hpp>
#include <boost/mp11.hpp>
#include <utility> #include <utility>
namespace boost { namespace boost {
@ -29,8 +29,8 @@ class indexed_range {
std::size_t stride; std::size_t stride;
int underflow; int underflow;
}; };
using strides_type = detail::make_axes_buffer<stride_t, axes_type>; using strides_type = detail::axes_buffer<axes_type, stride_t>;
using index_type = detail::make_axes_buffer<int, axes_type>; using index_type = detail::axes_buffer<axes_type, int>;
using value_type = decltype(std::declval<storage_type&>()[0]); using value_type = decltype(std::declval<storage_type&>()[0]);
public: public:
@ -42,11 +42,20 @@ public:
decltype(auto) bin(mp11::mp_size_t<N>) const { decltype(auto) bin(mp11::mp_size_t<N>) const {
return detail::axis_get<N>(axes_)[(*this)[N]]; return detail::axis_get<N>(axes_)[(*this)[N]];
} }
decltype(auto) bin(unsigned d) const { decltype(auto) bin(unsigned d) const {
return detail::axis_get(axes_, d)[(*this)[d]]; return detail::axis_get(axes_, d)[(*this)[d]];
} }
value_type value; double density() const {
double x = 1;
auto it = this->begin();
detail::for_each_axis(axes_,
[&](const auto& a) { x *= axis::traits::width(a, *it++); });
return value / x;
}
const value_type value;
protected: protected:
index_value(const axes_type& a, value_type v) index_value(const axes_type& a, value_type v)

View File

@ -7,6 +7,7 @@
#include <boost/core/lightweight_test.hpp> #include <boost/core/lightweight_test.hpp>
#include <boost/histogram/axis/integer.hpp> #include <boost/histogram/axis/integer.hpp>
#include <boost/histogram/axis/ostream_operators.hpp> #include <boost/histogram/axis/ostream_operators.hpp>
#include <boost/histogram/axis/variable.hpp>
#include <boost/histogram/histogram.hpp> #include <boost/histogram/histogram.hpp>
#include <boost/histogram/indexed.hpp> #include <boost/histogram/indexed.hpp>
#include <boost/histogram/literals.hpp> #include <boost/histogram/literals.hpp>
@ -105,6 +106,22 @@ void run_tests() {
++it; ++it;
BOOST_TEST(it == ind.end()); BOOST_TEST(it == ind.end());
} }
{
auto ax = axis::variable<>({0.0, 0.1, 0.3, 0.6});
auto ay = axis::integer<int>(0, 2);
auto az = ax;
auto h = make_s(Tag(), std::vector<int>(), ax, ay, az);
// fill uniformly
for (auto x : indexed(h)) {
h(x.bin(0).center(), x.bin(1).value(), x.bin(2).center());
}
for (auto x : indexed(h)) {
BOOST_TEST_EQ(x.density(), x.value / (x.bin(0).width() * x.bin(2).width()));
}
}
} }
int main() { int main() {