first working accumulator support

This commit is contained in:
Hans Dembinski
2018-11-08 23:32:14 +01:00
parent 8a4dc1d67b
commit 2a1995bb07
8 changed files with 126 additions and 87 deletions

View File

@@ -15,7 +15,6 @@
#include <boost/histogram/detail/buffer.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/histogram_fwd.hpp>
#include <boost/histogram/weight.hpp>
#include <boost/mp11.hpp>
#if defined BOOST_CLANG
#pragma clang diagnostic push

View File

@@ -34,8 +34,7 @@ struct get_polymorphic_bin_data
template <typename A>
std::tuple<double, double, double> operator()(const A& a) const {
return detail::static_if<
detail::has_value_method_with_return_type_convertible_to_x<A, double>>(
return detail::static_if<detail::has_method_value<A, double>>(
[this](const auto& a) {
using Arg = detail::unqual<detail::arg_type<detail::unqual<decltype(a)>>>;
const auto x = a.value(idx);
@@ -193,8 +192,7 @@ public:
return visit(
[idx](const auto& a) -> double {
using A = detail::unqual<decltype(a)>;
return detail::static_if<
detail::has_value_method_with_return_type_convertible_to_x<A, double>>(
return detail::static_if<detail::has_method_value<A, double>>(
[idx](const auto& a) -> double {
return static_cast<double>(a.value(idx));
},

View File

@@ -319,13 +319,38 @@ optional_index args_to_index(const T& axes, const U& args) {
return idx;
}
template <typename... Us>
constexpr int weight_index() {
const int n = sizeof...(Us) - 1;
using L = mp11::mp_list<Us...>;
if (is_weight<mp11::mp_at_c<L, 0>>::value) return 0;
if (is_weight<mp11::mp_at_c<L, n>>::value) return n;
return -1;
template <typename U>
constexpr std::pair<int, int> weight_sample_indices() {
if (is_weight<U>::value) return std::make_pair(0, -1);
if (is_sample<U>::value) return std::make_pair(-1, 0);
return std::make_pair(-1, -1);
}
template <typename U0, typename U1, typename... Us>
constexpr std::pair<int, int> weight_sample_indices() {
using L = mp11::mp_list<U0, U1, Us...>;
const int n = sizeof...(Us) + 1;
if (is_weight<mp11::mp_at_c<L, 0>>::value) {
if (is_sample<mp11::mp_at_c<L, 1>>::value) return std::make_pair(0, 1);
if (is_sample<mp11::mp_at_c<L, n>>::value) return std::make_pair(0, n);
return std::make_pair(0, -1);
}
if (is_sample<mp11::mp_at_c<L, 0>>::value) {
if (is_weight<mp11::mp_at_c<L, 1>>::value) return std::make_pair(1, 0);
if (is_weight<mp11::mp_at_c<L, n>>::value) return std::make_pair(n, 0);
return std::make_pair(-1, 0);
}
if (is_weight<mp11::mp_at_c<L, n>>::value) {
// 0, n already covered
if (is_sample<mp11::mp_at_c<L, (n - 1)>>::value) return std::make_pair(n, n - 1);
return std::make_pair(n, -1);
}
if (is_sample<mp11::mp_at_c<L, n>>::value) {
// n, 0 already covered
if (is_weight<mp11::mp_at_c<L, (n - 1)>>::value) return std::make_pair(n - 1, n);
return std::make_pair(-1, n);
}
return std::make_pair(-1, -1);
}
template <typename S, typename T>
@@ -343,22 +368,28 @@ void fill_storage_impl(mp11::mp_int<Iw>, mp11::mp_int<-1>, S& storage, std::size
template <int Is, typename S, typename T>
void fill_storage_impl(mp11::mp_int<-1>, mp11::mp_int<Is>, S& storage, std::size_t i,
const T& args) {
storage(i, std::get<Is>(args).value);
mp11::tuple_apply([&](auto&&... sargs) { storage(i, sargs...); },
std::get<Is>(args).value);
}
template <int Iw, int Is, typename S, typename T>
void fill_storage_impl(mp11::mp_int<Iw>, mp11::mp_int<Is>, S& storage, std::size_t i,
void fill_storage_impl(mp11::mp_int<Iw>, mp11::mp_int<Is>, S&, std::size_t i,
const T& args) {
storage(i, std::get<Iw>(args), std::get<Is>(args).value);
mp11::tuple_apply([&](auto&&... sargs) { storage(i, std::get<Iw>(args), sargs...); },
std::get<Is>(args).value);
}
template <typename S, typename T, typename... Us>
void fill_impl(S& storage, const T& axes, const std::tuple<Us...>& args) {
constexpr int Iw = weight_index<Us...>();
constexpr unsigned N = Iw >= 0 ? sizeof...(Us) - 1 : sizeof...(Us);
optional_index idx = args_to_index<(Iw == 0 ? 1 : 0), N>(axes, args);
constexpr std::pair<int, int> iws = weight_sample_indices<Us...>();
constexpr unsigned n = sizeof...(Us) - (iws.first > -1) - (iws.second > -1);
constexpr unsigned offset = (iws.first == 0 || iws.second == 0)
? (iws.first == 1 || iws.second == 1 ? 2 : 1)
: 0;
optional_index idx = args_to_index<offset, n>(axes, args);
if (idx) {
fill_storage_impl(mp11::mp_int<Iw>(), mp11::mp_int<-1>(), storage, *idx, args);
fill_storage_impl(mp11::mp_int<iws.first>(), mp11::mp_int<iws.second>(), storage,
*idx, args);
}
}

View File

@@ -121,14 +121,6 @@ decltype(auto) sub_tuple(const T& t) {
template <typename T> \
using name = typename name##_impl<T>::type
BOOST_HISTOGRAM_MAKE_SFINAE(has_variance_support,
(std::declval<T&>().value(), std::declval<T&>().variance()));
BOOST_HISTOGRAM_MAKE_SFINAE(has_method_value, (std::declval<T&>().value(0)));
// TODO try casting to more specific pmf with correct return type
BOOST_HISTOGRAM_MAKE_SFINAE(has_method_options, (std::declval<T&>().options()));
BOOST_HISTOGRAM_MAKE_SFINAE(has_method_metadata, (std::declval<T&>().metadata()));
// resize has two overloads, trying to get pmf in this case always fails
@@ -138,6 +130,28 @@ BOOST_HISTOGRAM_MAKE_SFINAE(has_method_size, &T::size);
BOOST_HISTOGRAM_MAKE_SFINAE(has_method_clear, &T::clear);
template <typename T, typename X>
struct has_method_value_impl {
template <typename U, typename V = decltype(std::declval<U&>().value(0))>
static typename std::is_convertible<V, X>::type Test(void*);
template <typename U>
static std::false_type Test(...);
using type = decltype(Test<T>(nullptr));
};
template <typename T, typename X>
using has_method_value = typename has_method_value_impl<T, X>::type;
template <typename T>
struct has_method_options_impl {
template <typename U, typename V = decltype(std::declval<const U&>().options())>
static typename std::is_same<V, axis::option_type>::type Test(void*);
template <typename U>
static std::false_type Test(...);
using type = decltype(Test<T>(nullptr));
};
template <typename T>
using has_method_options = typename has_method_options_impl<T>::type;
BOOST_HISTOGRAM_MAKE_SFINAE(has_allocator, &T::get_allocator);
BOOST_HISTOGRAM_MAKE_SFINAE(is_indexable, (std::declval<T&>()[0]));
@@ -209,18 +223,6 @@ struct is_sample_impl<sample_type<T>> : std::true_type {};
template <typename T>
using is_sample = is_sample_impl<unqual<T>>;
template <typename T, typename X>
struct has_value_method_with_return_type_convertible_to_x_impl {
template <typename U, typename V = decltype(std::declval<U&>().value(0))>
static typename std::is_convertible<V, X>::type Test(void*);
template <typename U>
static std::false_type Test(...);
using type = decltype(Test<T>(nullptr));
};
template <typename T, typename X>
using has_value_method_with_return_type_convertible_to_x =
typename has_value_method_with_return_type_convertible_to_x_impl<T, X>::type;
namespace {
struct bool_mask_impl {
std::vector<bool>& b;

View File

@@ -7,6 +7,7 @@
#ifndef BOOST_HISTOGRAM_SAMPLE_HPP
#define BOOST_HISTOGRAM_SAMPLE_HPP
#include <tuple>
#include <utility>
namespace boost {
@@ -18,10 +19,10 @@ struct sample_type {
T value;
};
/// Helper function to mark argument as sample
template <typename T>
sample_type<T> sample(T&& t) {
return {std::forward<T>(t)};
/// Helper function to mark arguments as sample
template <typename... Ts>
sample_type<std::tuple<Ts...>> sample(Ts&&... ts) {
return {std::make_tuple(std::forward<Ts>(ts)...)};
}
} // namespace histogram

View File

@@ -20,8 +20,8 @@ struct weight_type {
/// Helper function to mark argument as weight
template <typename T>
weight_type<T> weight(T&& t) {
return {std::forward<T>(t)};
auto weight(T&& t) {
return weight_type<T>{std::forward<T>(t)};
}
} // namespace histogram

View File

@@ -13,7 +13,9 @@
#include <boost/histogram/histogram.hpp>
#include <boost/histogram/literals.hpp>
#include <boost/histogram/ostream_operators.hpp>
#include <boost/histogram/sample.hpp>
#include <boost/histogram/storage_adaptor.hpp>
#include <boost/histogram/weight.hpp>
#include <sstream>
#include <stdexcept>
#include <tuple>
@@ -288,19 +290,16 @@ void run_tests() {
// d1 mean
{
auto h = make_s(Tag(), std::vector<accumulators::mean<>>(), axis::integer<>(0, 2));
// TODO
// h(0, 1);
// h(0, 2);
// h(0, 3);
// h(1, 2);
// h(1, 3);
h(0, sample(1));
h(0, sample(2));
h(0, sample(3));
h(sample(2), 1);
h(sample(3), 1);
// BOOST_TEST_EQ(h[0].sum(), 3);
// BOOST_TEST_EQ(h[0].value(), 2);
// BOOST_TEST_IS_CLOSE(h[0].variance(), 0.666, 1e-2);
// BOOST_TEST_EQ(h[1].sum(), 2);
// BOOST_TEST_EQ(h[1].value(), 2.5);
// BOOST_TEST_IS_CLOSE(h[1].variance(), 0.25, 1e-2);
BOOST_TEST_EQ(h[0].sum(), 3);
BOOST_TEST_EQ(h[0].value(), 2);
BOOST_TEST_EQ(h[1].sum(), 2);
BOOST_TEST_EQ(h[1].value(), 2.5);
}
// d2

View File

@@ -13,6 +13,8 @@
#include <boost/histogram/axis/variant.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/literals.hpp>
#include <boost/histogram/sample.hpp>
#include <boost/histogram/weight.hpp>
#include <boost/mp11.hpp>
#include <deque>
#include <iterator>
@@ -47,51 +49,36 @@ int main() {
BOOST_TEST_EQ(decltype(j213)::value, 213);
}
// has_variance_support
{
struct A {};
struct B {
void value() {}
};
struct C {
void variance() {}
};
struct D {
void value() {}
void variance() {}
};
BOOST_TEST_TRAIT_FALSE((has_variance_support<A>));
BOOST_TEST_TRAIT_FALSE((has_variance_support<B>));
BOOST_TEST_TRAIT_FALSE((has_variance_support<C>));
BOOST_TEST_TRAIT_TRUE((has_variance_support<D>));
}
// has_method_value
{
struct A {};
struct B {
void value(int) {}
A value(int) const { return {}; }
};
struct C {
char value(int) const { return 0; }
};
BOOST_TEST_TRAIT_FALSE((has_method_value<A>));
BOOST_TEST_TRAIT_TRUE((has_method_value<B>));
BOOST_TEST_TRAIT_FALSE((has_method_value<A, double>));
BOOST_TEST_TRAIT_TRUE((has_method_value<B, A>));
BOOST_TEST_TRAIT_FALSE((has_method_value<B, char>));
BOOST_TEST_TRAIT_TRUE((has_method_value<C, char>));
BOOST_TEST_TRAIT_FALSE((has_method_value<C, A>));
}
// has_method_options
{
struct A {};
struct B {
bh::axis::option_type options() { return bh::axis::option_type(); }
void options() {}
};
struct C {
bh::axis::option_type options() const { return {}; }
};
auto foo = static_cast<std::function<bh::axis::option_type(B&)>>(&B::options);
BOOST_TEST_TRAIT_FALSE((has_method_options<A>));
BOOST_TEST_TRAIT_TRUE((has_method_options<B>));
BOOST_TEST_TRAIT_FALSE((has_method_options<B>));
BOOST_TEST_TRAIT_TRUE((has_method_options<C>));
}
// has_method_metadata
@@ -436,5 +423,27 @@ int main() {
BOOST_TEST_TRAIT_TRUE((is_axis_vector<decltype(std::move(v))>));
}
// is_weight
{
struct A {};
using B = int;
using C = decltype(bh::weight(1));
BOOST_TEST_TRAIT_FALSE((is_weight<A>));
BOOST_TEST_TRAIT_FALSE((is_weight<B>));
BOOST_TEST_TRAIT_TRUE((is_weight<C>));
}
// is_sample
{
struct A {};
using B = int;
using C = decltype(bh::sample(1));
using D = decltype(bh::sample(1, 2.0));
BOOST_TEST_TRAIT_FALSE((is_sample<A>));
BOOST_TEST_TRAIT_FALSE((is_sample<B>));
BOOST_TEST_TRAIT_TRUE((is_sample<C>));
BOOST_TEST_TRAIT_TRUE((is_sample<D>));
}
return boost::report_errors();
}