Skip to content

Commit

Permalink
regular and variable axis are closed intervals if overflow bin is abs…
Browse files Browse the repository at this point in the history
…ent (#344)
  • Loading branch information
HDembinski committed Nov 15, 2021
1 parent b482598 commit bf7712f
Show file tree
Hide file tree
Showing 7 changed files with 235 additions and 112 deletions.
62 changes: 62 additions & 0 deletions benchmark/histogram_filling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,33 @@ using DStore = boost::histogram::adaptive_storage<>;
#endif

using namespace boost::histogram;
namespace op = boost::histogram::axis::option;
using reg = axis::regular<>;
using reg_closed =
axis::regular<double, boost::use_default, boost::use_default, op::none_t>;

class reg_closed_unsafe {
public:
reg_closed_unsafe(axis::index_type n, double start, double stop)
: min_{start}, delta_{stop - start}, size_{n} {}

axis::index_type index(double x) const noexcept {
// Runs in hot loop, please measure impact of changes
auto z = (x - min_) / delta_;
// assume that z < 0 and z > 1 never happens, promised by inclusive()
if (z == 1) return size() - 1;
return static_cast<axis::index_type>(z * size());
}

axis::index_type size() const noexcept { return size_; }

static constexpr bool inclusive() { return true; }

private:
double min_;
double delta_;
axis::index_type size_;
};

template <class Distribution, class Tag, class Storage = SStore>
static void fill_1d(benchmark::State& state) {
Expand All @@ -41,6 +67,22 @@ static void fill_1d(benchmark::State& state) {
state.SetItemsProcessed(state.iterations());
}

template <class Distribution, class Tag, class Storage = SStore>
static void fill_1d_closed(benchmark::State& state) {
auto h = make_s(Tag(), Storage(), reg_closed(100, 0, 1));
auto gen = generator<Distribution>();
for (auto _ : state) benchmark::DoNotOptimize(h(gen()));
state.SetItemsProcessed(state.iterations());
}

template <class Distribution, class Tag, class Storage = SStore>
static void fill_1d_closed_unsafe(benchmark::State& state) {
auto h = make_s(Tag(), Storage(), reg_closed_unsafe(100, 0, 1));
auto gen = generator<Distribution>();
for (auto _ : state) benchmark::DoNotOptimize(h(gen()));
state.SetItemsProcessed(state.iterations());
}

template <class Distribution, class Tag, class Storage = SStore>
static void fill_n_1d(benchmark::State& state) {
auto h = make_s(Tag(), Storage(), reg(100, 0, 1));
Expand All @@ -49,6 +91,22 @@ static void fill_n_1d(benchmark::State& state) {
state.SetItemsProcessed(state.iterations() * gen.size());
}

template <class Distribution, class Tag, class Storage = SStore>
static void fill_n_1d_closed(benchmark::State& state) {
auto h = make_s(Tag(), Storage(), reg_closed(100, 0, 1));
auto gen = generator<Distribution>();
for (auto _ : state) h.fill(gen);
state.SetItemsProcessed(state.iterations() * gen.size());
}

template <class Distribution, class Tag, class Storage = SStore>
static void fill_n_1d_closed_unsafe(benchmark::State& state) {
auto h = make_s(Tag(), Storage(), reg_closed_unsafe(100, 0, 1));
auto gen = generator<Distribution>();
for (auto _ : state) h.fill(gen);
state.SetItemsProcessed(state.iterations() * gen.size());
}

template <class Distribution, class Tag, class Storage = SStore>
static void fill_2d(benchmark::State& state) {
auto h = make_s(Tag(), Storage(), reg(100, 0, 1), reg(100, 0, 1));
Expand Down Expand Up @@ -111,6 +169,8 @@ BENCHMARK_TEMPLATE(fill_1d, uniform, dynamic_tag);
// BENCHMARK_TEMPLATE(fill_1d, uniform, dynamic_tag, DStore);
BENCHMARK_TEMPLATE(fill_1d, normal, dynamic_tag);
// BENCHMARK_TEMPLATE(fill_1d, normal, dynamic_tag, DStore);
BENCHMARK_TEMPLATE(fill_1d_closed, uniform, static_tag);
BENCHMARK_TEMPLATE(fill_1d_closed_unsafe, uniform, static_tag);

BENCHMARK_TEMPLATE(fill_n_1d, uniform, static_tag);
// BENCHMARK_TEMPLATE(fill_n_1d, uniform, static_tag, DStore);
Expand All @@ -120,6 +180,8 @@ BENCHMARK_TEMPLATE(fill_n_1d, uniform, dynamic_tag);
// BENCHMARK_TEMPLATE(fill_n_1d, uniform, dynamic_tag, DStore);
BENCHMARK_TEMPLATE(fill_n_1d, normal, dynamic_tag);
// BENCHMARK_TEMPLATE(fill_n_1d, normal, dynamic_tag, DStore);
BENCHMARK_TEMPLATE(fill_n_1d_closed, uniform, static_tag);
BENCHMARK_TEMPLATE(fill_n_1d_closed_unsafe, uniform, static_tag);

BENCHMARK_TEMPLATE(fill_2d, uniform, static_tag);
// BENCHMARK_TEMPLATE(fill_2d, uniform, static_tag, DStore);
Expand Down
63 changes: 31 additions & 32 deletions include/boost/histogram/axis/category.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,21 @@ namespace boost {
namespace histogram {
namespace axis {

/**
Maps at a set of unique values to bin indices.
The axis maps a set of values to bins, following the order of arguments in the
constructor. The optional overflow bin for this axis counts input values that
are not part of the set. Binning has O(N) complexity, but with a very small
factor. For small N (the typical use case) it beats other kinds of lookup.
@tparam Value input value type, must be equal-comparable.
@tparam MetaData type to store meta data.
@tparam Options see boost::histogram::axis::option.
@tparam Allocator allocator to use for dynamic memory management.
The options `underflow` and `circular` are not allowed. The options `growth`
and `overflow` are mutually exclusive.
*/
/** Maps at a set of unique values to bin indices.
The axis maps a set of values to bins, following the order of arguments in the
constructor. The optional overflow bin for this axis counts input values that
are not part of the set. Binning has O(N) complexity, but with a very small
factor. For small N (the typical use case) it beats other kinds of lookup.
@tparam Value input value type, must be equal-comparable.
@tparam MetaData type to store meta data.
@tparam Options see boost::histogram::axis::option.
@tparam Allocator allocator to use for dynamic memory management.
The options `underflow` and `circular` are not allowed. The options `growth`
and `overflow` are mutually exclusive.
*/
template <class Value, class MetaData, class Options, class Allocator>
class category : public iterator_mixin<category<Value, MetaData, Options, Allocator>>,
public metadata_base_t<MetaData> {
Expand All @@ -66,12 +65,12 @@ class category : public iterator_mixin<category<Value, MetaData, Options, Alloca
explicit category(allocator_type alloc) : vec_(alloc) {}

/** Construct from iterator range of unique values.
*
* @param begin begin of category range of unique values.
* @param end end of category range of unique values.
* @param meta description of the axis (optional).
* @param options see boost::histogram::axis::option (optional).
* @param alloc allocator instance to use (optional).
@param begin begin of category range of unique values.
@param end end of category range of unique values.
@param meta description of the axis (optional).
@param options see boost::histogram::axis::option (optional).
@param alloc allocator instance to use (optional).
*/
template <class It, class = detail::requires_iterator<It>>
category(It begin, It end, metadata_type meta = {}, options_type options = {},
Expand All @@ -91,11 +90,11 @@ class category : public iterator_mixin<category<Value, MetaData, Options, Alloca
: category(begin, end, std::move(meta), {}, std::move(alloc)) {}

/** Construct axis from iterable sequence of unique values.
*
* @param iterable sequence of unique values.
* @param meta description of the axis.
* @param options see boost::histogram::axis::option (optional).
* @param alloc allocator instance to use.
@param iterable sequence of unique values.
@param meta description of the axis.
@param options see boost::histogram::axis::option (optional).
@param alloc allocator instance to use.
*/
template <class C, class = detail::requires_iterable<C>>
category(const C& iterable, metadata_type meta = {}, options_type options = {},
Expand All @@ -110,11 +109,11 @@ class category : public iterator_mixin<category<Value, MetaData, Options, Alloca
std::move(alloc)) {}

/** Construct axis from an initializer list of unique values.
*
* @param list `std::initializer_list` of unique values.
* @param meta description of the axis.
* @param options see boost::histogram::axis::option (optional).
* @param alloc allocator instance to use.
@param list `std::initializer_list` of unique values.
@param meta description of the axis.
@param options see boost::histogram::axis::option (optional).
@param alloc allocator instance to use.
*/
template <class U>
category(std::initializer_list<U> list, metadata_type meta = {},
Expand Down
21 changes: 10 additions & 11 deletions include/boost/histogram/axis/integer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,13 @@ namespace boost {
namespace histogram {
namespace axis {

/**
Axis for an interval of integer values with unit steps.
/** Axis for an interval of integer values with unit steps.
Binning is a O(1) operation. This axis bins faster than a regular axis.
Binning is a O(1) operation. This axis bins faster than a regular axis.
@tparam Value input value type. Must be integer or floating point.
@tparam MetaData type to store meta data.
@tparam Options see boost::histogram::axis::option.
@tparam Value input value type. Must be integer or floating point.
@tparam MetaData type to store meta data.
@tparam Options see boost::histogram::axis::option.
*/
template <class Value, class MetaData, class Options>
class integer : public iterator_mixin<integer<Value, MetaData, Options>>,
Expand Down Expand Up @@ -72,11 +71,11 @@ class integer : public iterator_mixin<integer<Value, MetaData, Options>>,
constexpr integer() = default;

/** Construct over semi-open integer interval [start, stop).
*
* @param start first integer of covered range.
* @param stop one past last integer of covered range.
* @param meta description of the axis (optional).
* @param options see boost::histogram::axis::option (optional).
@param start first integer of covered range.
@param stop one past last integer of covered range.
@param meta description of the axis (optional).
@param options see boost::histogram::axis::option (optional).
*/
integer(value_type start, value_type stop, metadata_type meta = {},
options_type options = {})
Expand Down
93 changes: 51 additions & 42 deletions include/boost/histogram/axis/regular.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,15 +165,22 @@ step_type<T> step(T t) {
return step_type<T>{t};
}

/**
Axis for equidistant intervals on the real line.
/** Axis for equidistant intervals on the real line.
The most common binning strategy. Very fast. Binning is a O(1) operation.
The most common binning strategy. Very fast. Binning is a O(1) operation.
If the axis has an overflow bin (the default), a value on the upper edge of the last
bin is put in the overflow bin. The axis range represents a semi-open interval.
@tparam Value input value type, must be floating point.
@tparam Transform builtin or user-defined transform type.
@tparam MetaData type to store meta data.
@tparam Options see boost::histogram::axis::option.
If the overflow bin is deactivated, then a value on the upper edge of the last bin is
still counted towards the last bin. The axis range represents a closed interval. This
is the desired behavior for random numbers drawn from a bounded interval, which is
usually closed.
@tparam Value input value type, must be floating point.
@tparam Transform builtin or user-defined transform type.
@tparam MetaData type to store meta data.
@tparam Options see boost::histogram::axis::option.
*/
template <class Value, class Transform, class MetaData, class Options>
class regular : public iterator_mixin<regular<Value, Transform, MetaData, Options>>,
Expand Down Expand Up @@ -207,13 +214,13 @@ class regular : public iterator_mixin<regular<Value, Transform, MetaData, Option
constexpr regular() = default;

/** Construct n bins over real transformed range [start, stop).
*
* @param trans transform instance to use.
* @param n number of bins.
* @param start low edge of first bin.
* @param stop high edge of last bin.
* @param meta description of the axis (optional).
* @param options see boost::histogram::axis::option (optional).
@param trans transform instance to use.
@param n number of bins.
@param start low edge of first bin.
@param stop high edge of last bin.
@param meta description of the axis (optional).
@param options see boost::histogram::axis::option (optional).
*/
regular(transform_type trans, unsigned n, value_type start, value_type stop,
metadata_type meta = {}, options_type options = {})
Expand All @@ -232,30 +239,30 @@ class regular : public iterator_mixin<regular<Value, Transform, MetaData, Option
}

/** Construct n bins over real range [start, stop).
*
* @param n number of bins.
* @param start low edge of first bin.
* @param stop high edge of last bin.
* @param meta description of the axis (optional).
* @param options see boost::histogram::axis::option (optional).
@param n number of bins.
@param start low edge of first bin.
@param stop high edge of last bin.
@param meta description of the axis (optional).
@param options see boost::histogram::axis::option (optional).
*/
regular(unsigned n, value_type start, value_type stop, metadata_type meta = {},
options_type options = {})
: regular({}, n, start, stop, std::move(meta), options) {}

/** Construct bins with the given step size over real transformed range
* [start, stop).
*
* @param trans transform instance to use.
* @param step width of a single bin.
* @param start low edge of first bin.
* @param stop upper limit of high edge of last bin (see below).
* @param meta description of the axis (optional).
* @param options see boost::histogram::axis::option (optional).
*
* The axis computes the number of bins as n = abs(stop - start) / step,
* rounded down. This means that stop is an upper limit to the actual value
* (start + n * step).
[start, stop).
@param trans transform instance to use.
@param step width of a single bin.
@param start low edge of first bin.
@param stop upper limit of high edge of last bin (see below).
@param meta description of the axis (optional).
@param options see boost::histogram::axis::option (optional).
The axis computes the number of bins as n = abs(stop - start) / step,
rounded down. This means that stop is an upper limit to the actual value
(start + n * step).
*/
template <class T>
regular(transform_type trans, step_type<T> step, value_type start, value_type stop,
Expand All @@ -267,16 +274,16 @@ class regular : public iterator_mixin<regular<Value, Transform, MetaData, Option
std::move(meta), options) {}

/** Construct bins with the given step size over real range [start, stop).
*
* @param step width of a single bin.
* @param start low edge of first bin.
* @param stop upper limit of high edge of last bin (see below).
* @param meta description of the axis (optional).
* @param options see boost::histogram::axis::option (optional).
*
* The axis computes the number of bins as n = abs(stop - start) / step,
* rounded down. This means that stop is an upper limit to the actual value
* (start + n * step).
@param step width of a single bin.
@param start low edge of first bin.
@param stop upper limit of high edge of last bin (see below).
@param meta description of the axis (optional).
@param options see boost::histogram::axis::option (optional).
The axis computes the number of bins as n = abs(stop - start) / step,
rounded down. This means that stop is an upper limit to the actual value
(start + n * step).
*/
template <class T>
regular(step_type<T> step, value_type start, value_type stop, metadata_type meta = {},
Expand Down Expand Up @@ -311,6 +318,8 @@ class regular : public iterator_mixin<regular<Value, Transform, MetaData, Option
else
return -1;
}
// upper edge of last bin is inclusive if overflow bin is not present
if (!options_type::test(option::overflow) && z == 1) return size() - 1;
}
return size(); // also returned if x is NaN
}
Expand Down
Loading

0 comments on commit bf7712f

Please sign in to comment.