switched to lazy interval_view to improve performance

This commit is contained in:
Hans Dembinski 2018-03-11 17:08:13 +01:00
parent 8cb08efa7b
commit fe4bd1701d
9 changed files with 120 additions and 138 deletions

View File

@ -7,10 +7,10 @@
#ifndef _BOOST_HISTOGRAM_AXIS_ANY_HPP_
#define _BOOST_HISTOGRAM_AXIS_ANY_HPP_
#include <boost/histogram/axis/interval.hpp>
#include <boost/histogram/axis/iterator.hpp>
#include <boost/histogram/detail/axis_visitor.hpp>
#include <boost/histogram/detail/cat.hpp>
#include <boost/histogram/interval.hpp>
#include <boost/mpl/contains.hpp>
#include <boost/utility/string_view.hpp>
#include <boost/variant.hpp>
@ -54,36 +54,47 @@ struct set_label : public static_visitor<void> {
template <typename A> void operator()(A &a) const { a.label(label); }
};
template <typename T> struct index : public static_visitor<int> {
const T &t;
explicit index(const T &arg) : t(arg) {}
struct index : public static_visitor<int> {
const double x;
explicit index(const double arg) : x(arg) {}
template <typename Axis> int operator()(const Axis &a) const {
return impl(std::is_convertible<T, typename Axis::value_type>(), a);
return impl(std::is_convertible<double, typename Axis::value_type>(), a);
}
template <typename Axis> int impl(std::true_type, const Axis &a) const {
return a.index(t);
return a.index(x);
}
template <typename Axis> int impl(std::false_type, const Axis &) const {
throw std::runtime_error(::boost::histogram::detail::cat(
"fill argument not convertible to axis value type: ",
boost::typeindex::type_id<Axis>().pretty_name(), ", ",
boost::typeindex::type_id<T>().pretty_name()));
"cannot convert value_type ",
boost::typeindex::type_id<typename Axis::value_type>().pretty_name(),
" of ",
boost::typeindex::type_id<Axis>().pretty_name(),
" to double")
);
}
};
struct bin : public static_visitor<axis::interval<double>> {
using double_interval = axis::interval<double>;
const int i;
bin(const int v) : i(v) {}
template <typename A> double_interval operator()(const A &a) const {
return impl(is_convertible<typename A::bin_type, double_interval>(),
std::forward<typename A::bin_type>(a[i]));
struct eval : public static_visitor<std::function<double(int)>> {
template <typename Axis> std::function<double(int)> operator()(const Axis &a) const {
return impl(std::integral_constant<bool,
(std::is_convertible<typename Axis::value_type, double>::value &&
std::is_same<
typename Axis::bin_type,
interval_view<typename Axis::value_type>
>::value)
>(), a);
}
template <typename B> double_interval impl(true_type, B &&b) const {
return b;
template <typename Axis> std::function<double(int)> impl(std::true_type, const Axis &a) const {
return [&a](int i) -> double { return a[i].lower(); } ;
}
template <typename B> double_interval impl(false_type, B &&) const {
throw std::runtime_error("cannot convert bin_type to interval<double>");
template <typename Axis> std::function<double(int)> impl(std::false_type, const Axis &) const {
throw std::runtime_error(::boost::histogram::detail::cat(
"cannot convert bin_type ",
boost::typeindex::type_id<typename Axis::bin_type>().pretty_name(),
" of ",
boost::typeindex::type_id<Axis>().pretty_name(),
" to interval_view<double>")
);
}
};
} // namespace detail
@ -95,7 +106,7 @@ template <typename Axes> class any : public make_variant_over<Axes>::type {
public:
using types = typename base_type::types;
using value_type = double;
using bin_type = interval<double>;
using bin_type = interval_view<double>;
using const_iterator = iterator_over<any>;
using const_reverse_iterator = reverse_iterator_over<any>;
@ -112,15 +123,15 @@ public:
template <typename T, typename = typename std::enable_if<
mpl::contains<types, T>::value>::type>
any &operator=(const T &t) {
// ugly workaround for compiler bug
return reinterpret_cast<any &>(base_type::operator=(t));
base_type::operator=(t);
return *this;
}
template <typename T, typename = typename std::enable_if<
mpl::contains<types, T>::value>::type>
any &operator=(T &&t) {
// ugly workaround for compiler bug
return reinterpret_cast<any &>(base_type::operator=(std::move(t)));
base_type::operator=(std::move(t));
return *this;
}
int size() const { return apply_visitor(detail::size(), *this); }
@ -131,7 +142,7 @@ public:
// note: this only works for axes with compatible value type
int index(const value_type x) const {
return apply_visitor(detail::index<value_type>(x), *this);
return apply_visitor(detail::index(x), *this);
}
string_view label() const {
@ -143,9 +154,10 @@ public:
}
// this only works for axes with compatible bin type
// and will raise an error otherwise
// and will throw a runtime_error otherwise
bin_type operator[](const int i) const {
return apply_visitor(detail::bin(i), *this);
auto eval = apply_visitor(detail::eval(), *this);
return bin_type(i, eval);
}
bool operator==(const any &rhs) const {

View File

@ -9,9 +9,9 @@
#include <algorithm>
#include <boost/bimap.hpp>
#include <boost/histogram/axis/interval.hpp>
#include <boost/histogram/axis/iterator.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/interval.hpp>
#include <boost/math/constants/constants.hpp>
#include <boost/utility/string_view.hpp>
#include <cmath>
@ -36,7 +36,7 @@ namespace axis {
enum class uoflow { off = false, on = true };
/// Base class for all axes, uses CRTP.
/// Base class for all axes, uses CRTP to inject iterator logic.
template <typename Derived> class axis_base {
public:
using const_iterator = iterator_over<Derived>;
@ -102,7 +102,7 @@ private:
template <class Archive> void serialize(Archive &, unsigned);
};
/// Base class for axes with overflow/underflow bins, uses CRTP.
/// Base class for axes with optional under-/overflow bins, uses CRTP.
template <typename Derived> class axis_base_uoflow : public axis_base<Derived> {
using base_type = axis_base<Derived>;
@ -114,7 +114,7 @@ public:
protected:
axis_base_uoflow(unsigned n, string_view label, enum uoflow uo)
: base_type(n, label), shape_(n + 2u * static_cast<unsigned>(uo)) {}
: base_type(n, label), shape_(n + 2 * static_cast<int>(uo)) {}
axis_base_uoflow() = default;
axis_base_uoflow(const axis_base_uoflow &) = default;
@ -202,7 +202,7 @@ class regular : public axis_base_uoflow<regular<RealType, Transform>>,
public:
using value_type = RealType;
using bin_type = interval<value_type>;
using bin_type = interval_view<value_type>;
/** Construct axis with n bins over real range [lower, upper).
*
@ -241,9 +241,9 @@ public:
: -1;
}
/// Returns the starting edge of the bin.
/// Returns the bin interval.
bin_type operator[](int idx) const noexcept {
auto eval = [this](int i) {
return bin_type(idx, [this](int i) {
const auto n = base_type::size();
if (i < 0)
return this->inverse(-std::numeric_limits<value_type>::infinity());
@ -251,8 +251,7 @@ public:
return this->inverse(std::numeric_limits<value_type>::infinity());
const auto z = value_type(i) / n;
return this->inverse((1.0 - z) * min_ + z * (min_ + delta_ * n));
};
return {eval(idx), eval(idx + 1)};
});
}
bool operator==(const regular &o) const noexcept {
@ -260,6 +259,7 @@ public:
min_ == o.min_ && delta_ == o.delta_;
}
/// Access properties of the transform.
const Transform &transform() const noexcept {
return static_cast<const Transform &>(*this);
}
@ -283,7 +283,7 @@ class circular : public axis_base<circular<RealType>> {
public:
using value_type = RealType;
using bin_type = interval<value_type>;
using bin_type = interval_view<value_type>;
/** Constructor for n bins with an optional offset.
*
@ -313,11 +313,10 @@ public:
/// Returns the starting edge of the bin.
bin_type operator[](int idx) const {
auto eval = [this](int i) {
return bin_type(idx, [this](int i) {
const value_type z = value_type(i) / base_type::size();
return z * perimeter_ + phase_;
};
return {eval(idx), eval(idx + 1)};
});
}
bool operator==(const circular &o) const noexcept {
@ -346,7 +345,7 @@ class variable : public axis_base_uoflow<variable<RealType>> {
public:
using value_type = RealType;
using bin_type = interval<value_type>;
using bin_type = interval_view<value_type>;
/** Construct an axis from bin edges.
*
@ -397,7 +396,7 @@ public:
/// Returns the starting edge of the bin.
bin_type operator[](int idx) const {
auto eval = [this](int i) {
return bin_type(idx, [this](int i) {
if (i < 0) {
return -std::numeric_limits<value_type>::infinity();
}
@ -405,8 +404,7 @@ public:
return std::numeric_limits<value_type>::infinity();
}
return x_[i];
};
return {eval(idx), eval(idx + 1)};
});
}
bool operator==(const variable &o) const noexcept {
@ -434,7 +432,7 @@ class integer : public axis_base_uoflow<integer<IntType>> {
public:
using value_type = IntType;
using bin_type = interval<value_type>;
using bin_type = interval_view<value_type>;
/** Construct axis over a semi-open integer interval [lower, upper).
*
@ -465,7 +463,7 @@ public:
/// Returns the integer that is mapped to the bin index.
bin_type operator[](int idx) const {
auto eval = [this](int i) {
return bin_type(idx, [this](int i) {
if (i < 0) {
return -std::numeric_limits<value_type>::max();
}
@ -473,8 +471,7 @@ public:
return std::numeric_limits<value_type>::max();
}
return min_ + i;
};
return {eval(idx), eval(idx + 1)};
});
}
bool operator==(const integer &o) const noexcept {

View File

@ -0,0 +1,46 @@
// 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_AXIS_INTERVAL_HPP_
#define _BOOST_HISTOGRAM_AXIS_INTERVAL_HPP_
#include <functional>
#include <utility>
namespace boost {
namespace histogram {
namespace axis {
template <typename T> class interval_view {
public:
interval_view(int idx, std::function<T(int)> eval) : idx_(idx), eval_(eval) {}
interval_view(const interval_view &) = default;
interval_view &operator=(const interval_view &) = default;
interval_view(interval_view &&) = default;
interval_view &operator=(interval_view &&) = default;
T lower() const noexcept { return eval_(idx_); }
T upper() const noexcept { return eval_(idx_ + 1); }
T width() const noexcept { return upper() - lower(); }
bool operator==(const interval_view &rhs) const noexcept {
return lower() == rhs.lower() && upper() == rhs.upper();
}
bool operator!=(const interval_view &rhs) const noexcept {
return !operator==(rhs);
}
private:
const int idx_;
const std::function<T(int)> eval_;
};
} // namespace axis
} // namespace histogram
} // namespace boost
#endif

View File

@ -10,8 +10,8 @@
#define _BOOST_HISTOGRAM_AXIS_OSTREAM_OPERATORS_HPP_
#include <boost/histogram/axis/axis.hpp>
#include <boost/histogram/axis/interval.hpp>
#include <boost/histogram/detail/utility.hpp>
#include <boost/histogram/interval.hpp>
#include <boost/math/constants/constants.hpp>
#include <ostream>
@ -26,6 +26,12 @@ inline string_view to_string(const transform::sqrt &) { return {"_sqrt", 5}; }
inline string_view to_string(const transform::cos &) { return {"_cos", 4}; }
} // namespace detail
template <typename T>
inline std::ostream &operator<<(std::ostream &os, const interval_view<T> &i) {
os << "[" << i.lower() << ", " << i.upper() << ")";
return os;
}
template <typename RealType, typename Transform>
inline std::ostream &operator<<(std::ostream &os,
const regular<RealType, Transform> &a) {

View File

@ -13,7 +13,7 @@
#include <boost/fusion/include/is_sequence.hpp>
#include <boost/fusion/include/size.hpp>
#include <boost/fusion/support/is_sequence.hpp>
#include <boost/histogram/interval.hpp>
#include <boost/histogram/axis/interval.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/contains.hpp>
#include <boost/variant/get.hpp>

View File

@ -1,46 +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_INTERVAL_HPP_
#define _BOOST_HISTOGRAM_INTERVAL_HPP_
#include <utility>
namespace boost {
namespace histogram {
namespace axis {
template <typename T> class interval {
public:
interval() = default;
interval(const interval &) = default;
interval &operator=(const interval &) = default;
interval(interval &&) = default;
interval &operator=(interval &&) = default;
interval(const T &x, const T &y) : a(x), b(y) {}
interval(T &&x, T &&y) : a(std::move(x)), b(std::move(y)) {}
template <typename U>
interval(const interval<U> &i) : a(i.lower()), b(i.upper()) {}
const T &lower() const noexcept { return a; }
const T &upper() const noexcept { return b; }
bool operator==(const interval &i) const noexcept {
return a == i.a && b == i.b;
}
bool operator!=(const interval &i) const noexcept { return !operator==(i); }
private:
T a, b;
};
} // namespace axis
} // namespace histogram
} // namespace boost
#endif

View File

@ -55,25 +55,14 @@ generic_iterator make_generic_iterator(bp::object self) {
}
template <typename T>
struct axis_interval_to_python
struct axis_interval_view_to_python
{
static PyObject* convert(const bha::interval<T> &i)
static PyObject* convert(const bha::interval_view<T> &i)
{
return bp::incref(bp::make_tuple(i.lower(), i.upper()).ptr());
}
};
template <typename T>
struct pair_int_axis_interval_to_python
{
static PyObject* convert(const std::pair<int, bha::interval<T>> &p)
{
return bp::incref(bp::make_tuple(
p.first, bp::make_tuple(p.second.lower(), p.second.upper())
).ptr());
}
};
bp::object variable_init(bp::tuple args, bp::dict kwargs) {
bp::object self = args[0];
@ -263,23 +252,13 @@ void register_axis_types() {
docstring_options dopt(true, true, false);
to_python_converter<
bha::interval<int>,
axis_interval_to_python<int>
bha::interval_view<int>,
axis_interval_view_to_python<int>
>();
to_python_converter<
bha::interval<double>,
axis_interval_to_python<double>
>();
to_python_converter<
std::pair<int, bha::interval<int>>,
pair_int_axis_interval_to_python<int>
>();
to_python_converter<
std::pair<int, bha::interval<double>>,
pair_int_axis_interval_to_python<double>
bha::interval_view<double>,
axis_interval_view_to_python<double>
>();
class_<generic_iterator>("generic_iterator", init<object>())

View File

@ -23,18 +23,6 @@
#define BOOST_TEST_NOT(expr) BOOST_TEST(!(expr))
#define BOOST_TEST_IS_CLOSE(a, b, eps) BOOST_TEST(std::abs(a - b) < eps)
namespace boost {
namespace histogram {
namespace axis {
template <typename T>
std::ostream &operator<<(std::ostream &os, const interval<T> &i) {
os << "[" << i.lower() << ", " << i.upper() << ")";
return os;
}
} // namespace axis
} // namespace histogram
} // namespace boost
template <typename Axis>
void test_axis_iterator(const Axis &a, int begin, int end) {
auto it = a.begin();
@ -239,6 +227,7 @@ int main() {
test_axis_iterator(axis::integer<>(0, 4, ""), 0, 4);
test_axis_iterator(axis::category<>({A, B, C}, ""), 0, 3);
test_axis_iterator(any_axis_type(axis::regular<>(5, 0, 1)), 0, 5);
BOOST_TEST_THROWS(any_axis_type(axis::category<>({A, B, C}))[0], std::runtime_error);
}
// any_axis_type_copyable

View File

@ -882,8 +882,7 @@ int main() {
{
enum { A, B };
auto c = make_dynamic_histogram(axis::category<>({A, B}));
BOOST_TEST_THROWS(c.axis()[0].lower(), std::runtime_error);
BOOST_TEST_THROWS(c.axis()[0].upper(), std::runtime_error);
BOOST_TEST_THROWS(c.axis()[0], std::runtime_error);
}
// reduce