use indexed in reduce and project, making index_mapper obsolete

This commit is contained in:
Hans Dembinski
2018-12-16 20:32:38 +01:00
parent f8b6b28975
commit 7d0bd00adb
8 changed files with 64 additions and 193 deletions

View File

@@ -9,8 +9,9 @@
#include <algorithm>
#include <boost/histogram/detail/axes.hpp>
#include <boost/histogram/detail/index_mapper.hpp>
#include <boost/histogram/histogram_fwd.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/histogram.hpp>
#include <boost/histogram/indexed.hpp>
#include <boost/histogram/unsafe_access.hpp>
#include <boost/mp11.hpp>
#include <boost/throw_exception.hpp>
@@ -29,38 +30,22 @@ namespace algorithm {
histogram is summed over the removed axes.
*/
template <typename A, typename S, unsigned N, typename... Ns>
auto project(const histogram<A, S>& h, std::integral_constant<unsigned, N> n, Ns... ns) {
auto project(const histogram<A, S>& hist, std::integral_constant<unsigned, N> n,
Ns... ns) {
using LN = mp11::mp_list<decltype(n), Ns...>;
static_assert(mp11::mp_is_set<LN>::value, "indices must be unique");
const auto& axes = unsafe_access::axes(h);
auto r_axes = detail::make_sub_axes(axes, n, ns...);
auto r_h = histogram<decltype(r_axes), S>(
std::move(r_axes),
detail::static_if<detail::has_allocator<S>>(
[&h](auto) { return S(unsafe_access::storage(h).get_allocator()); },
[](auto) { return S(); }, 0));
auto axes = detail::make_sub_axes(unsafe_access::axes(hist), n, ns...);
auto result = histogram<decltype(axes), S>(
std::move(axes), detail::make_default(unsafe_access::storage(hist)));
detail::index_mapper<A> im(h.rank());
auto iter = im.begin();
std::size_t s = 1;
h.for_each_axis([&](const auto& a) {
const auto n = axis::traits::extend(a);
im.total *= n;
iter->stride[0] = s;
s *= n;
iter->stride[1] = 0;
++iter;
});
s = 1;
mp11::mp_for_each<LN>([&](auto J) {
im[J].stride[1] = s;
s *= axis::traits::extend(detail::axis_get<J>(axes));
});
im(unsafe_access::storage(r_h), unsafe_access::storage(h));
return r_h;
detail::axes_buffer<typename decltype(result)::axes_type, int> idx(result.rank());
for (auto x : indexed(hist, true)) {
auto i = idx.begin();
mp11::mp_for_each<LN>([&i, &x](auto I) { *i++ = x[I]; });
unsafe_access::add_value(result, idx, *x);
}
return result;
}
/**
@@ -69,59 +54,32 @@ auto project(const histogram<A, S>& h, std::integral_constant<unsigned, N> n, Ns
This version accepts a source histogram and an iterable range containing the remaining
indices.
*/
template <typename A, typename S, typename C, typename = detail::requires_iterable<C>>
auto project(const histogram<A, S>& h, const C& c) {
template <typename A, typename S, typename Iterable,
typename = detail::requires_iterable<Iterable>>
auto project(const histogram<A, S>& hist, const Iterable& c) {
static_assert(detail::is_axis_vector<A>::value,
"dynamic version of project requires a histogram with dynamic axis");
using H = histogram<A, S>;
const auto& axes = unsafe_access::axes(h);
auto r_axes = detail::static_if<detail::has_allocator<A>>(
[](const auto& axes) {
using T = detail::unqual<decltype(axes)>;
return T(axes.get_allocator());
},
[](const auto& axes) {
using T = detail::unqual<decltype(axes)>;
return T();
},
axes);
using std::begin;
using std::end;
r_axes.reserve(std::distance(begin(c), end(c)));
detail::index_mapper<A> im(h.rank());
auto iter = im.begin();
std::size_t stride = 1;
h.for_each_axis([&](const auto& a) {
const auto n = axis::traits::extend(a);
im.total *= n;
iter->stride[0] = stride;
stride *= n;
iter->stride[1] = 0;
++iter;
});
stride = 1;
for (auto idx : c) {
r_axes.emplace_back(axes[idx]);
auto& stride_ref = im[idx].stride[1];
if (stride_ref)
BOOST_THROW_EXCEPTION(std::invalid_argument("indices must be unique"));
else
stride_ref = stride;
stride *= axis::traits::extend(axes[idx]);
const auto& hist_axes = unsafe_access::axes(hist);
auto axes = detail::make_default(hist_axes);
axes.reserve(c.size());
detail::axes_buffer<A, bool> seen(hist_axes.size(), false);
for (auto d : c) {
if (seen[d]) BOOST_THROW_EXCEPTION(std::invalid_argument("indices must be unique"));
seen[d] = true;
axes.emplace_back(hist_axes[d]);
}
auto r_h = H(std::move(r_axes),
detail::static_if<detail::has_allocator<S>>(
[&h](auto) { return S(unsafe_access::storage(h).get_allocator()); },
[](auto) { return S(); }, 0));
auto result = histogram<A, S>(std::move(axes),
detail::make_default(unsafe_access::storage(hist)));
im(unsafe_access::storage(r_h), unsafe_access::storage(h));
return r_h;
detail::axes_buffer<A, int> idx(result.rank());
for (auto x : indexed(hist, true)) {
auto i = idx.begin();
for (auto d : c) *i++ = x[d];
unsafe_access::add_value(result, idx, *x);
}
return result;
}
} // namespace algorithm

View File

@@ -9,7 +9,6 @@
#include <boost/assert.hpp>
#include <boost/histogram/detail/axes.hpp>
#include <boost/histogram/detail/index_mapper.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/histogram_fwd.hpp>
#include <boost/histogram/indexed.hpp>
@@ -112,12 +111,8 @@ histogram<A, S> reduce(const histogram<A, S>& hist, const C& options) {
++iaxis;
});
const auto& storage = unsafe_access::storage(hist);
using storage_type = detail::unqual<decltype(storage)>;
auto result = histogram<A, S>(
std::move(axes), detail::static_if<detail::has_allocator<storage_type>>(
[](auto& x) { return storage_type(x.get_allocator()); },
[](auto&) { return storage_type(); }, storage));
auto result = histogram<A, S>(std::move(axes),
detail::make_default(unsafe_access::storage(hist)));
detail::axes_buffer<A, int> idx(hist.rank());
for (auto x : indexed(hist, true)) {

View File

@@ -172,9 +172,9 @@ using axes_buffer = boost::container::static_vector<
std::tuple_size, Axes>::value>;
template <typename T>
auto make_empty_axes(const T& t) {
auto r = T();
static_if<is_vector_like<T>>([&](auto) { r.reserve(t.size()); }, [](auto) {}, 0);
auto make_empty_axes(const std::vector<T>& t) {
auto r = std::vector<T>(t.get_allocator());
r.reserve(t.size());
for_each_axis(t, [&r](const auto& a) {
using U = unqual<decltype(a)>;
r.emplace_back(U());

View File

@@ -1,101 +0,0 @@
// Copyright 2015-2017 Hans Dembinski
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_HISTOGRAM_DETAIL_INDEX_MAPPER_HPP
#define BOOST_HISTOGRAM_DETAIL_INDEX_MAPPER_HPP
#include <boost/container/static_vector.hpp>
#include <boost/histogram/histogram_fwd.hpp>
#include <cstddef>
#include <functional>
namespace boost {
namespace histogram {
namespace detail {
template <typename A>
struct index_mapper {
struct item {
std::size_t stride[2];
};
using buffer_type = axes_buffer<A, item>;
index_mapper(unsigned dim) : buffer(dim) {}
template <typename T, typename U>
void operator()(T& dst, const U& src) {
for (std::size_t i = 0; i < total; ++i) {
std::size_t j = 0;
auto imod = i;
for (auto it = end(); it != begin(); --it) {
const auto& d = *(it - 1);
// compiler usually optimizes div & mod into one div
const auto k = imod / d.stride[0];
imod %= d.stride[0];
j += k * d.stride[1];
}
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;
};
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) {
for (std::size_t i = 0; i < total; ++i) {
std::size_t j = 0;
auto imod = i;
bool drop = false;
for (auto it = end(); it != begin(); --it) {
const auto& d = *(it - 1);
// compiler usually optimizes div & mod into one div
int k = imod / d.stride[0];
imod %= d.stride[0];
if (k < d.begin || k == d.underflow[0]) {
k = d.underflow[1];
} else if (k >= d.end || k == d.overflow[0]) {
k = d.overflow[1];
} else {
k = (k - d.begin) / d.merge;
}
drop |= k < 0;
j += k * d.stride[1];
}
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
} // namespace boost
#endif

View File

@@ -284,6 +284,13 @@ struct requires_axis_or_axis_variant {};
template <typename T, typename = mp11::mp_if<is_axis_vector<unqual<T>>, void>>
struct requires_axis_vector {};
template <typename T>
T make_default(const T& t) {
using U = unqual<T>;
return static_if<has_allocator<U>>([](const auto& t) { return U(t.get_allocator()); },
[](const auto&) { return U(); }, t);
}
} // namespace detail
} // namespace histogram
} // namespace boost

View File

@@ -117,14 +117,14 @@ public:
, cache_(hist_.rank()) {
auto c = cache_.begin();
std::size_t stride = 1;
const auto extra = include_extra_bins_;
h.for_each_axis([&](const auto& a) {
h.for_each_axis([&, this](const auto& a) {
const auto opt = axis::traits::options(a);
const auto shift = opt & axis::option_type::underflow;
c->extend = axis::traits::extend(a);
c->begin = extra ? -shift : 0;
c->end = c->extend - shift - (extra ? 0 : (opt & axis::option_type::overflow));
c->begin = include_extra_bins_ ? -shift : 0;
c->end = c->extend - shift -
(include_extra_bins_ ? 0 : (opt & axis::option_type::overflow));
c->idx = c->begin;
begin_ += (c->begin + shift) * stride;

View File

@@ -27,7 +27,6 @@ alias run-tests :
[ run histogram_mixed_test.cpp ]
[ run histogram_serialization_test.cpp /boost/serialization//boost_serialization/<link>static ]
[ run histogram_test.cpp ]
[ run index_mapper_test.cpp ]
[ run meta_test.cpp ]
[ run storage_adaptor_test.cpp ]
[ run storage_adaptor_serialization_test.cpp /boost/serialization//boost_serialization/<link>static ]

View File

@@ -24,6 +24,7 @@
#include <unordered_map>
#include <utility>
#include <vector>
#include "utility_allocator.hpp"
#include "utility_meta.hpp"
namespace bh = boost::histogram;
@@ -435,5 +436,17 @@ int main() {
BOOST_TEST(has_fixed_size<D>::value);
}
// make_default
{
struct A {};
auto a = make_default(A());
BOOST_TEST_TRAIT_TRUE((std::is_same<decltype(a), A>));
bh::tracing_allocator_db db;
using B = std::vector<int, bh::tracing_allocator<int>>;
B b = make_default(B(bh::tracing_allocator<int>(db)));
b.resize(100);
BOOST_TEST_EQ(db[&BOOST_CORE_TYPEID(int)].first, 100);
}
return boost::report_errors();
}