regular and variable axis are closed intervals if overflow bin is absent (#344)

This commit is contained in:
Hans Dembinski 2021-09-30 14:59:27 +02:00 committed by Hans Dembinski
parent b482598597
commit bf7712f0d7
7 changed files with 232 additions and 109 deletions

View File

@ -31,7 +31,33 @@ using DStore = boost::histogram::adaptive_storage<>;
#endif #endif
using namespace boost::histogram; using namespace boost::histogram;
namespace op = boost::histogram::axis::option;
using reg = axis::regular<>; 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> template <class Distribution, class Tag, class Storage = SStore>
static void fill_1d(benchmark::State& state) { static void fill_1d(benchmark::State& state) {
@ -41,6 +67,22 @@ static void fill_1d(benchmark::State& state) {
state.SetItemsProcessed(state.iterations()); 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> template <class Distribution, class Tag, class Storage = SStore>
static void fill_n_1d(benchmark::State& state) { static void fill_n_1d(benchmark::State& state) {
auto h = make_s(Tag(), Storage(), reg(100, 0, 1)); auto h = make_s(Tag(), Storage(), reg(100, 0, 1));
@ -49,6 +91,22 @@ static void fill_n_1d(benchmark::State& state) {
state.SetItemsProcessed(state.iterations() * gen.size()); 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> template <class Distribution, class Tag, class Storage = SStore>
static void fill_2d(benchmark::State& state) { static void fill_2d(benchmark::State& state) {
auto h = make_s(Tag(), Storage(), reg(100, 0, 1), reg(100, 0, 1)); auto h = make_s(Tag(), Storage(), reg(100, 0, 1), reg(100, 0, 1));
@ -111,6 +169,8 @@ BENCHMARK_TEMPLATE(fill_1d, uniform, dynamic_tag);
// BENCHMARK_TEMPLATE(fill_1d, uniform, dynamic_tag, DStore); // BENCHMARK_TEMPLATE(fill_1d, uniform, dynamic_tag, DStore);
BENCHMARK_TEMPLATE(fill_1d, normal, dynamic_tag); BENCHMARK_TEMPLATE(fill_1d, normal, dynamic_tag);
// BENCHMARK_TEMPLATE(fill_1d, normal, dynamic_tag, DStore); // 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);
// BENCHMARK_TEMPLATE(fill_n_1d, uniform, static_tag, DStore); // BENCHMARK_TEMPLATE(fill_n_1d, uniform, static_tag, DStore);
@ -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, uniform, dynamic_tag, DStore);
BENCHMARK_TEMPLATE(fill_n_1d, normal, dynamic_tag); BENCHMARK_TEMPLATE(fill_n_1d, normal, dynamic_tag);
// BENCHMARK_TEMPLATE(fill_n_1d, normal, dynamic_tag, DStore); // 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);
// BENCHMARK_TEMPLATE(fill_2d, uniform, static_tag, DStore); // BENCHMARK_TEMPLATE(fill_2d, uniform, static_tag, DStore);

View File

@ -26,22 +26,21 @@ namespace boost {
namespace histogram { namespace histogram {
namespace axis { namespace axis {
/** /** Maps at a set of unique values to bin indices.
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 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 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 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. factor. For small N (the typical use case) it beats other kinds of lookup.
@tparam Value input value type, must be equal-comparable. @tparam Value input value type, must be equal-comparable.
@tparam MetaData type to store meta data. @tparam MetaData type to store meta data.
@tparam Options see boost::histogram::axis::option. @tparam Options see boost::histogram::axis::option.
@tparam Allocator allocator to use for dynamic memory management. @tparam Allocator allocator to use for dynamic memory management.
The options `underflow` and `circular` are not allowed. The options `growth` The options `underflow` and `circular` are not allowed. The options `growth`
and `overflow` are mutually exclusive. and `overflow` are mutually exclusive.
*/ */
template <class Value, class MetaData, class Options, class Allocator> template <class Value, class MetaData, class Options, class Allocator>
class category : public iterator_mixin<category<Value, MetaData, Options, Allocator>>, class category : public iterator_mixin<category<Value, MetaData, Options, Allocator>>,
public metadata_base_t<MetaData> { public metadata_base_t<MetaData> {
@ -66,12 +65,12 @@ public:
explicit category(allocator_type alloc) : vec_(alloc) {} explicit category(allocator_type alloc) : vec_(alloc) {}
/** Construct from iterator range of unique values. /** Construct from iterator range of unique values.
*
* @param begin begin of category range of unique values. @param begin begin of category range of unique values.
* @param end end of category range of unique values. @param end end of category range of unique values.
* @param meta description of the axis (optional). @param meta description of the axis (optional).
* @param options see boost::histogram::axis::option (optional). @param options see boost::histogram::axis::option (optional).
* @param alloc allocator instance to use (optional). @param alloc allocator instance to use (optional).
*/ */
template <class It, class = detail::requires_iterator<It>> template <class It, class = detail::requires_iterator<It>>
category(It begin, It end, metadata_type meta = {}, options_type options = {}, category(It begin, It end, metadata_type meta = {}, options_type options = {},
@ -91,11 +90,11 @@ public:
: category(begin, end, std::move(meta), {}, std::move(alloc)) {} : category(begin, end, std::move(meta), {}, std::move(alloc)) {}
/** Construct axis from iterable sequence of unique values. /** Construct axis from iterable sequence of unique values.
*
* @param iterable sequence of unique values. @param iterable sequence of unique values.
* @param meta description of the axis. @param meta description of the axis.
* @param options see boost::histogram::axis::option (optional). @param options see boost::histogram::axis::option (optional).
* @param alloc allocator instance to use. @param alloc allocator instance to use.
*/ */
template <class C, class = detail::requires_iterable<C>> template <class C, class = detail::requires_iterable<C>>
category(const C& iterable, metadata_type meta = {}, options_type options = {}, category(const C& iterable, metadata_type meta = {}, options_type options = {},
@ -110,11 +109,11 @@ public:
std::move(alloc)) {} std::move(alloc)) {}
/** Construct axis from an initializer list of unique values. /** Construct axis from an initializer list of unique values.
*
* @param list `std::initializer_list` of unique values. @param list `std::initializer_list` of unique values.
* @param meta description of the axis. @param meta description of the axis.
* @param options see boost::histogram::axis::option (optional). @param options see boost::histogram::axis::option (optional).
* @param alloc allocator instance to use. @param alloc allocator instance to use.
*/ */
template <class U> template <class U>
category(std::initializer_list<U> list, metadata_type meta = {}, category(std::initializer_list<U> list, metadata_type meta = {},

View File

@ -29,14 +29,13 @@ namespace boost {
namespace histogram { namespace histogram {
namespace axis { 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 Value input value type. Must be integer or floating point.
@tparam MetaData type to store meta data. @tparam MetaData type to store meta data.
@tparam Options see boost::histogram::axis::option. @tparam Options see boost::histogram::axis::option.
*/ */
template <class Value, class MetaData, class Options> template <class Value, class MetaData, class Options>
class integer : public iterator_mixin<integer<Value, MetaData, Options>>, class integer : public iterator_mixin<integer<Value, MetaData, Options>>,
@ -72,11 +71,11 @@ public:
constexpr integer() = default; constexpr integer() = default;
/** Construct over semi-open integer interval [start, stop). /** Construct over semi-open integer interval [start, stop).
*
* @param start first integer of covered range. @param start first integer of covered range.
* @param stop one past last integer of covered range. @param stop one past last integer of covered range.
* @param meta description of the axis (optional). @param meta description of the axis (optional).
* @param options see boost::histogram::axis::option (optional). @param options see boost::histogram::axis::option (optional).
*/ */
integer(value_type start, value_type stop, metadata_type meta = {}, integer(value_type start, value_type stop, metadata_type meta = {},
options_type options = {}) options_type options = {})

View File

@ -165,15 +165,22 @@ step_type<T> step(T t) {
return step_type<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.
@tparam Value input value type, must be floating point. If the axis has an overflow bin (the default), a value on the upper edge of the last
@tparam Transform builtin or user-defined transform type. bin is put in the overflow bin. The axis range represents a semi-open interval.
@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> template <class Value, class Transform, class MetaData, class Options>
class regular : public iterator_mixin<regular<Value, Transform, MetaData, Options>>, class regular : public iterator_mixin<regular<Value, Transform, MetaData, Options>>,
@ -207,13 +214,13 @@ public:
constexpr regular() = default; constexpr regular() = default;
/** Construct n bins over real transformed range [start, stop). /** Construct n bins over real transformed range [start, stop).
*
* @param trans transform instance to use. @param trans transform instance to use.
* @param n number of bins. @param n number of bins.
* @param start low edge of first bin. @param start low edge of first bin.
* @param stop high edge of last bin. @param stop high edge of last bin.
* @param meta description of the axis (optional). @param meta description of the axis (optional).
* @param options see boost::histogram::axis::option (optional). @param options see boost::histogram::axis::option (optional).
*/ */
regular(transform_type trans, unsigned n, value_type start, value_type stop, regular(transform_type trans, unsigned n, value_type start, value_type stop,
metadata_type meta = {}, options_type options = {}) metadata_type meta = {}, options_type options = {})
@ -232,30 +239,30 @@ public:
} }
/** Construct n bins over real range [start, stop). /** Construct n bins over real range [start, stop).
*
* @param n number of bins. @param n number of bins.
* @param start low edge of first bin. @param start low edge of first bin.
* @param stop high edge of last bin. @param stop high edge of last bin.
* @param meta description of the axis (optional). @param meta description of the axis (optional).
* @param options see boost::histogram::axis::option (optional). @param options see boost::histogram::axis::option (optional).
*/ */
regular(unsigned n, value_type start, value_type stop, metadata_type meta = {}, regular(unsigned n, value_type start, value_type stop, metadata_type meta = {},
options_type options = {}) options_type options = {})
: regular({}, n, start, stop, std::move(meta), options) {} : regular({}, n, start, stop, std::move(meta), options) {}
/** Construct bins with the given step size over real transformed range /** Construct bins with the given step size over real transformed range
* [start, stop). [start, stop).
*
* @param trans transform instance to use. @param trans transform instance to use.
* @param step width of a single bin. @param step width of a single bin.
* @param start low edge of first bin. @param start low edge of first bin.
* @param stop upper limit of high edge of last bin (see below). @param stop upper limit of high edge of last bin (see below).
* @param meta description of the axis (optional). @param meta description of the axis (optional).
* @param options see boost::histogram::axis::option (optional). @param options see boost::histogram::axis::option (optional).
*
* The axis computes the number of bins as n = abs(stop - start) / step, 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 rounded down. This means that stop is an upper limit to the actual value
* (start + n * step). (start + n * step).
*/ */
template <class T> template <class T>
regular(transform_type trans, step_type<T> step, value_type start, value_type stop, regular(transform_type trans, step_type<T> step, value_type start, value_type stop,
@ -267,16 +274,16 @@ public:
std::move(meta), options) {} std::move(meta), options) {}
/** Construct bins with the given step size over real range [start, stop). /** Construct bins with the given step size over real range [start, stop).
*
* @param step width of a single bin. @param step width of a single bin.
* @param start low edge of first bin. @param start low edge of first bin.
* @param stop upper limit of high edge of last bin (see below). @param stop upper limit of high edge of last bin (see below).
* @param meta description of the axis (optional). @param meta description of the axis (optional).
* @param options see boost::histogram::axis::option (optional). @param options see boost::histogram::axis::option (optional).
*
* The axis computes the number of bins as n = abs(stop - start) / step, 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 rounded down. This means that stop is an upper limit to the actual value
* (start + n * step). (start + n * step).
*/ */
template <class T> template <class T>
regular(step_type<T> step, value_type start, value_type stop, metadata_type meta = {}, regular(step_type<T> step, value_type start, value_type stop, metadata_type meta = {},
@ -311,6 +318,8 @@ public:
else else
return -1; 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 return size(); // also returned if x is NaN
} }

View File

@ -34,17 +34,24 @@ namespace boost {
namespace histogram { namespace histogram {
namespace axis { namespace axis {
/** /** Axis for non-equidistant bins on the real line.
Axis for non-equidistant bins on the real line.
Binning is a O(log(N)) operation. If speed matters and the problem domain Binning is a O(log(N)) operation. If speed matters and the problem domain
allows it, prefer a regular axis, possibly with a transform. allows it, prefer a regular axis, possibly with a transform.
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.
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 Value input value type, must be floating point.
@tparam MetaData type to store meta data. @tparam MetaData type to store meta data.
@tparam Options see boost::histogram::axis::option. @tparam Options see boost::histogram::axis::option.
@tparam Allocator allocator to use for dynamic memory management. @tparam Allocator allocator to use for dynamic memory management.
*/ */
template <class Value, class MetaData, class Options, class Allocator> template <class Value, class MetaData, class Options, class Allocator>
class variable : public iterator_mixin<variable<Value, MetaData, Options, Allocator>>, class variable : public iterator_mixin<variable<Value, MetaData, Options, Allocator>>,
public metadata_base_t<MetaData> { public metadata_base_t<MetaData> {
@ -72,12 +79,12 @@ public:
explicit variable(allocator_type alloc) : vec_(alloc) {} explicit variable(allocator_type alloc) : vec_(alloc) {}
/** Construct from iterator range of bin edges. /** Construct from iterator range of bin edges.
*
* @param begin begin of edge sequence. @param begin begin of edge sequence.
* @param end end of edge sequence. @param end end of edge sequence.
* @param meta description of the axis (optional). @param meta description of the axis (optional).
* @param options see boost::histogram::axis::option (optional). @param options see boost::histogram::axis::option (optional).
* @param alloc allocator instance to use (optional). @param alloc allocator instance to use (optional).
*/ */
template <class It, class = detail::requires_iterator<It>> template <class It, class = detail::requires_iterator<It>>
variable(It begin, It end, metadata_type meta = {}, options_type options = {}, variable(It begin, It end, metadata_type meta = {}, options_type options = {},
@ -106,11 +113,11 @@ public:
: variable(begin, end, std::move(meta), {}, std::move(alloc)) {} : variable(begin, end, std::move(meta), {}, std::move(alloc)) {}
/** Construct variable axis from iterable range of bin edges. /** Construct variable axis from iterable range of bin edges.
*
* @param iterable iterable range of bin edges. @param iterable iterable range of bin edges.
* @param meta description of the axis (optional). @param meta description of the axis (optional).
* @param options see boost::histogram::axis::option (optional). @param options see boost::histogram::axis::option (optional).
* @param alloc allocator instance to use (optional). @param alloc allocator instance to use (optional).
*/ */
template <class U, class = detail::requires_iterable<U>> template <class U, class = detail::requires_iterable<U>>
variable(const U& iterable, metadata_type meta = {}, options_type options = {}, variable(const U& iterable, metadata_type meta = {}, options_type options = {},
@ -125,11 +132,11 @@ public:
std::move(alloc)) {} std::move(alloc)) {}
/** Construct variable axis from initializer list of bin edges. /** Construct variable axis from initializer list of bin edges.
*
* @param list `std::initializer_list` of bin edges. @param list `std::initializer_list` of bin edges.
* @param meta description of the axis (optional). @param meta description of the axis (optional).
* @param options see boost::histogram::axis::option (optional). @param options see boost::histogram::axis::option (optional).
* @param alloc allocator instance to use (optional). @param alloc allocator instance to use (optional).
*/ */
template <class U> template <class U>
variable(std::initializer_list<U> list, metadata_type meta = {}, variable(std::initializer_list<U> list, metadata_type meta = {},
@ -159,6 +166,8 @@ public:
const auto b = vec_[size()]; const auto b = vec_[size()];
x -= std::floor((x - a) / (b - a)) * (b - a); x -= std::floor((x - a) / (b - a)) * (b - a);
} }
// upper edge of last bin is inclusive if overflow bin is not present
if (!options_type::test(option::overflow) && x == vec_.back()) return size() - 1;
return static_cast<index_type>(std::upper_bound(vec_.begin(), vec_.end(), x) - return static_cast<index_type>(std::upper_bound(vec_.begin(), vec_.end(), x) -
vec_.begin() - 1); vec_.begin() - 1);
} }

View File

@ -20,6 +20,7 @@ int main() {
using namespace boost::histogram; using namespace boost::histogram;
using def = use_default; using def = use_default;
namespace tr = axis::transform; namespace tr = axis::transform;
namespace op = axis::option;
BOOST_TEST(std::is_nothrow_move_assignable<axis::regular<>>::value); BOOST_TEST(std::is_nothrow_move_assignable<axis::regular<>>::value);
BOOST_TEST(std::is_nothrow_move_constructible<axis::regular<>>::value); BOOST_TEST(std::is_nothrow_move_constructible<axis::regular<>>::value);
@ -202,7 +203,7 @@ int main() {
// with growth // with growth
{ {
using pii_t = std::pair<axis::index_type, axis::index_type>; using pii_t = std::pair<axis::index_type, axis::index_type>;
axis::regular<double, def, def, axis::option::growth_t> a{1, 0, 1}; axis::regular<double, def, def, op::growth_t> a{1, 0, 1};
BOOST_TEST_EQ(a.size(), 1); BOOST_TEST_EQ(a.size(), 1);
BOOST_TEST_EQ(a.update(0), pii_t(0, 0)); BOOST_TEST_EQ(a.update(0), pii_t(0, 0));
BOOST_TEST_EQ(a.size(), 1); BOOST_TEST_EQ(a.size(), 1);
@ -223,11 +224,32 @@ int main() {
BOOST_TEST_EQ(a.update(-std::numeric_limits<double>::infinity()), pii_t(-1, 0)); BOOST_TEST_EQ(a.update(-std::numeric_limits<double>::infinity()), pii_t(-1, 0));
} }
// axis with overflow bin represents open interval
{
axis::regular<double, def, def, op::overflow_t> a{2, 0, 1};
BOOST_TEST_EQ(a.index(0), 0);
BOOST_TEST_EQ(a.index(0.49), 0);
BOOST_TEST_EQ(a.index(0.50), 1);
BOOST_TEST_EQ(a.index(0.99), 1);
BOOST_TEST_EQ(a.index(1), 2); // overflow bin
BOOST_TEST_EQ(a.index(1.1), 2); // overflow bin
}
// axis without overflow bin represents a closed interval
{
axis::regular<double, def, def, op::none_t> a{2, 0, 1};
BOOST_TEST_EQ(a.index(0), 0);
BOOST_TEST_EQ(a.index(0.49), 0);
BOOST_TEST_EQ(a.index(0.50), 1);
BOOST_TEST_EQ(a.index(0.99), 1);
BOOST_TEST_EQ(a.index(1), 1); // last ordinary bin
BOOST_TEST_EQ(a.index(1.1), 2); // out of range
}
// iterators // iterators
{ {
test_axis_iterator(axis::regular<>(5, 0, 1), 0, 5); test_axis_iterator(axis::regular<>(5, 0, 1), 0, 5);
test_axis_iterator(axis::regular<double, def, def, axis::option::none_t>(5, 0, 1), 0, test_axis_iterator(axis::regular<double, def, def, op::none_t>(5, 0, 1), 0, 5);
5);
test_axis_iterator(axis::circular<>(5, 0, 1), 0, 5); test_axis_iterator(axis::circular<>(5, 0, 1), 0, 5);
} }

View File

@ -18,6 +18,7 @@
#include "utility_str.hpp" #include "utility_str.hpp"
using namespace boost::histogram; using namespace boost::histogram;
namespace op = boost::histogram::axis::option;
int main() { int main() {
constexpr auto inf = std::numeric_limits<double>::infinity(); constexpr auto inf = std::numeric_limits<double>::infinity();
@ -104,7 +105,7 @@ int main() {
// axis::variable circular // axis::variable circular
{ {
axis::variable<double, axis::null_type, axis::option::circular_t> a{-1, 1, 2}; axis::variable<double, axis::null_type, op::circular_t> a{-1, 1, 2};
BOOST_TEST_EQ(a.value(-2), -4); BOOST_TEST_EQ(a.value(-2), -4);
BOOST_TEST_EQ(a.value(-1), -2); BOOST_TEST_EQ(a.value(-1), -2);
BOOST_TEST_EQ(a.value(0), -1); BOOST_TEST_EQ(a.value(0), -1);
@ -125,7 +126,7 @@ int main() {
// axis::regular with growth // axis::regular with growth
{ {
using pii_t = std::pair<axis::index_type, axis::index_type>; using pii_t = std::pair<axis::index_type, axis::index_type>;
axis::variable<double, axis::null_type, axis::option::growth_t> a{0, 1}; axis::variable<double, axis::null_type, op::growth_t> a{0, 1};
BOOST_TEST_EQ(a.size(), 1); BOOST_TEST_EQ(a.size(), 1);
BOOST_TEST_EQ(a.update(0), pii_t(0, 0)); BOOST_TEST_EQ(a.update(0), pii_t(0, 0));
BOOST_TEST_EQ(a.size(), 1); BOOST_TEST_EQ(a.size(), 1);
@ -149,11 +150,33 @@ int main() {
BOOST_TEST_EQ(a.update(nan), pii_t(a.size(), 0)); BOOST_TEST_EQ(a.update(nan), pii_t(a.size(), 0));
} }
// axis with overflow bin represents open interval
{
axis::variable<double, boost::use_default, op::overflow_t> a{0.0, 0.5, 1.0};
BOOST_TEST_EQ(a.index(0), 0);
BOOST_TEST_EQ(a.index(0.49), 0);
BOOST_TEST_EQ(a.index(0.50), 1);
BOOST_TEST_EQ(a.index(0.99), 1);
BOOST_TEST_EQ(a.index(1), 2); // overflow bin
BOOST_TEST_EQ(a.index(1.1), 2); // overflow bin
}
// axis without overflow bin represents a closed interval
{
axis::variable<double, boost::use_default, op::none_t> a{0.0, 0.5, 1.0};
BOOST_TEST_EQ(a.index(0), 0);
BOOST_TEST_EQ(a.index(0.49), 0);
BOOST_TEST_EQ(a.index(0.50), 1);
BOOST_TEST_EQ(a.index(0.99), 1);
BOOST_TEST_EQ(a.index(1), 1); // last ordinary bin
BOOST_TEST_EQ(a.index(1.1), 2); // out of range
}
// iterators // iterators
{ {
test_axis_iterator(axis::variable<>{1, 2, 3}, 0, 2); test_axis_iterator(axis::variable<>{1, 2, 3}, 0, 2);
test_axis_iterator( test_axis_iterator(axis::variable<double, axis::null_type, op::circular_t>{1, 2, 3},
axis::variable<double, axis::null_type, axis::option::circular_t>{1, 2, 3}, 0, 2); 0, 2);
} }
// shrink and rebin // shrink and rebin
@ -176,7 +199,7 @@ int main() {
// shrink and rebin with circular option // shrink and rebin with circular option
{ {
using A = axis::variable<double, axis::null_type, axis::option::circular_t>; using A = axis::variable<double, axis::null_type, op::circular_t>;
auto a = A({1, 2, 3, 4, 5}); auto a = A({1, 2, 3, 4, 5});
BOOST_TEST_THROWS(A(a, 1, 4, 1), std::invalid_argument); BOOST_TEST_THROWS(A(a, 1, 4, 1), std::invalid_argument);
BOOST_TEST_THROWS(A(a, 0, 3, 1), std::invalid_argument); BOOST_TEST_THROWS(A(a, 0, 3, 1), std::invalid_argument);