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()); },
[](auto) { return S(); }, 0));
detail::index_mapper im(h.rank());
detail::index_mapper<A> im(h.rank());
auto iter = im.begin();
std::size_t s = 1;
h.for_each_axis([&](const auto& a) {
@ -87,7 +87,7 @@ auto project(const histogram<A, S>& h, C c) {
axes);
r_axes.reserve(std::distance(begin, end));
detail::index_mapper im(h.rank());
detail::index_mapper<A> im(h.rank());
auto iter = im.begin();
std::size_t stride = 1;
h.for_each_axis([&](const auto& a) {

View File

@ -8,7 +8,6 @@
#define BOOST_HISTOGRAM_ALGORITHM_REDUCE_HPP
#include <boost/assert.hpp>
#include <boost/container/static_vector.hpp>
#include <boost/histogram/detail/axes.hpp>
#include <boost/histogram/detail/index_mapper.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>>
histogram<A, S> reduce(const histogram<A, S>& h, const C& c) {
auto options =
boost::container::static_vector<reduce_option_type, axis::limit>(h.rank());
auto options = detail::axes_buffer<A, reduce_option_type>(h.rank());
for (const auto& o : c) {
auto& opt_ref = options[o.iaxis];
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));
detail::index_mapper_reduce im(h.rank());
detail::index_mapper_reduce<A> im(h.rank());
auto im_iter = im.begin();
std::size_t stride[2] = {1, 1};
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);
}
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 axis
} // namespace histogram

View File

@ -20,6 +20,18 @@
#include <type_traits>
#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 histogram {
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); }
}
template <typename T, typename A>
using make_axes_buffer = boost::container::static_vector<
T, mp11::mp_eval_if_c<!(has_fixed_size<A>::value), mp11::mp_size_t<axis::limit>,
std::tuple_size, A>::value>;
template <typename Axes, typename T>
using axes_buffer = boost::container::static_vector<
T, mp11::mp_eval_if_c<!(has_fixed_size<Axes>::value),
mp11::mp_size_t<BOOST_HISTOGRAM_DETAIL_AXES_LIMIT>,
std::tuple_size, Axes>::value>;
template <typename T>
auto make_empty_axes(const T& t) {

View File

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

View File

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

View File

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

View File

@ -18,21 +18,6 @@ namespace boost {
namespace histogram {
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
struct null_type {};

View File

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

View File

@ -7,6 +7,7 @@
#include <boost/core/lightweight_test.hpp>
#include <boost/histogram/axis/integer.hpp>
#include <boost/histogram/axis/ostream_operators.hpp>
#include <boost/histogram/axis/variable.hpp>
#include <boost/histogram/histogram.hpp>
#include <boost/histogram/indexed.hpp>
#include <boost/histogram/literals.hpp>
@ -105,6 +106,22 @@ void run_tests() {
++it;
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() {