update boost

This commit is contained in:
2023-11-24 12:56:13 -06:00
parent cfc99971af
commit 19d727037a
9260 changed files with 849256 additions and 299957 deletions

View File

@@ -0,0 +1,36 @@
// Copyright 2019 Henry Schreiner
//
// 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_ALGORITHM_EMPTY_HPP
#define BOOST_HISTOGRAM_ALGORITHM_EMPTY_HPP
#include <boost/histogram/fwd.hpp>
#include <boost/histogram/indexed.hpp>
namespace boost {
namespace histogram {
namespace algorithm {
/** Check to see if all histogram cells are empty. Use coverage to include or
exclude the underflow/overflow bins.
This algorithm has O(N) complexity, where N is the number of cells.
Returns true if all cells are empty, and false otherwise.
*/
template <class A, class S>
auto empty(const histogram<A, S>& h, coverage cov) {
using value_type = typename histogram<A, S>::value_type;
const value_type default_value = value_type();
for (auto&& ind : indexed(h, cov)) {
if (*ind != default_value) { return false; }
}
return true;
}
} // namespace algorithm
} // namespace histogram
} // namespace boost
#endif

View File

@@ -8,16 +8,20 @@
#define BOOST_HISTOGRAM_ALGORITHM_PROJECT_HPP
#include <algorithm>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/axis/variant.hpp>
#include <boost/histogram/detail/detect.hpp>
#include <boost/histogram/detail/make_default.hpp>
#include <boost/histogram/detail/static_if.hpp>
#include <boost/histogram/histogram.hpp>
#include <boost/histogram/indexed.hpp>
#include <boost/histogram/unsafe_access.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/list.hpp>
#include <boost/mp11/set.hpp>
#include <boost/mp11/utility.hpp>
#include <boost/throw_exception.hpp>
#include <stdexcept>
#include <type_traits>
#include <vector>
namespace boost {
namespace histogram {
@@ -41,8 +45,7 @@ auto project(const histogram<A, S>& h, std::integral_constant<unsigned, N>, Ns..
return std::make_tuple(std::get<N>(old_axes), std::get<Ns::value>(old_axes)...);
},
[&](const auto& old_axes) {
return detail::remove_cvref_t<decltype(old_axes)>(
{old_axes[N], old_axes[Ns::value]...});
return std::decay_t<decltype(old_axes)>({old_axes[N], old_axes[Ns::value]...});
},
old_axes);
@@ -50,7 +53,7 @@ auto project(const histogram<A, S>& h, std::integral_constant<unsigned, N>, Ns..
using A2 = decltype(axes);
auto result = histogram<A2, S>(std::move(axes), detail::make_default(old_storage));
auto idx = detail::make_stack_buffer<int>(unsafe_access::axes(result));
for (auto x : indexed(h, coverage::all)) {
for (auto&& x : indexed(h, coverage::all)) {
auto i = idx.begin();
mp11::mp_for_each<LN>([&i, &x](auto J) { *i++ = x.index(J); });
result.at(idx) += *x;
@@ -66,23 +69,26 @@ auto project(const histogram<A, S>& h, std::integral_constant<unsigned, N>, Ns..
*/
template <class A, class S, class Iterable, class = detail::requires_iterable<Iterable>>
auto project(const histogram<A, S>& h, const Iterable& c) {
static_assert(detail::is_sequence_of_any_axis<A>::value,
"this version of project requires histogram with non-static axes");
using namespace boost::mp11;
const auto& old_axes = unsafe_access::axes(h);
auto axes = detail::make_default(old_axes);
// axes is always std::vector<...>, even if A is tuple
auto axes = detail::make_empty_dynamic_axes(old_axes);
axes.reserve(c.size());
auto seen = detail::make_stack_buffer<bool>(old_axes, false);
auto seen = detail::make_stack_buffer(old_axes, false);
for (auto d : c) {
if (seen[d]) BOOST_THROW_EXCEPTION(std::invalid_argument("indices must be unique"));
if (static_cast<unsigned>(d) >= h.rank())
BOOST_THROW_EXCEPTION(std::invalid_argument("invalid axis index"));
if (seen[d]) BOOST_THROW_EXCEPTION(std::invalid_argument("indices are not unique"));
seen[d] = true;
axes.emplace_back(old_axes[d]);
axes.emplace_back(detail::axis_get(old_axes, d));
}
const auto& old_storage = unsafe_access::storage(h);
auto result = histogram<A, S>(std::move(axes), detail::make_default(old_storage));
auto result =
histogram<decltype(axes), S>(std::move(axes), detail::make_default(old_storage));
auto idx = detail::make_stack_buffer<int>(unsafe_access::axes(result));
for (auto x : indexed(h, coverage::all)) {
for (auto&& x : indexed(h, coverage::all)) {
auto i = idx.begin();
for (auto d : c) *i++ = x.index(d);
result.at(idx) += *x;

View File

@@ -1,4 +1,4 @@
// Copyright 2018 Hans Dembinski
// Copyright 2018-2019 Hans Dembinski
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
@@ -7,212 +7,460 @@
#ifndef BOOST_HISTOGRAM_ALGORITHM_REDUCE_HPP
#define BOOST_HISTOGRAM_ALGORITHM_REDUCE_HPP
#include <boost/assert.hpp>
#include <boost/histogram/axis/traits.hpp>
#include <boost/histogram/detail/axes.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/detail/make_default.hpp>
#include <boost/histogram/detail/reduce_command.hpp>
#include <boost/histogram/detail/static_if.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/histogram/indexed.hpp>
#include <boost/histogram/unsafe_access.hpp>
#include <boost/throw_exception.hpp>
#include <cassert>
#include <cmath>
#include <limits>
#include <initializer_list>
#include <stdexcept>
#include <type_traits>
#include <string>
namespace boost {
namespace histogram {
namespace algorithm {
/**
Option type returned by the helper functions shrink_and_rebin(), shrink(), rebin().
*/
struct reduce_option {
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
unsigned iaxis = 0;
double lower, upper;
unsigned merge = 0;
/** Holder for a reduce command.
reduce_option() noexcept = default;
Use this type to store reduce commands in a container. The internals of this type are an
implementation detail.
*/
using reduce_command = detail::reduce_command;
reduce_option(unsigned i, double l, double u, unsigned m)
: iaxis(i), lower(l), upper(u), merge(m) {
if (lower == upper)
BOOST_THROW_EXCEPTION(std::invalid_argument("lower != upper required"));
if (merge == 0) BOOST_THROW_EXCEPTION(std::invalid_argument("merge > 0 required"));
}
#endif
};
/** Shrink command to be used in `reduce`.
/**
Shrink and rebin option.
Command is applied to axis with given index.
Shrinking is based on an inclusive value interval. The bin which contains the first
value starts the range of bins to keep. The bin which contains the second value is the
last included in that range. When the second value is exactly equal to a lower bin edge,
then the previous bin is the last in the range.
The counts in removed bins are added to the corresponding underflow and overflow bins,
if they are present. If they are not present, the counts are discarded. Also see
`crop`, which always discards the counts.
@param iaxis which axis to operate on.
@param lower bin which contains lower is first to be kept.
@param upper bin which contains upper is last to be kept, except if upper is equal to
the lower edge.
*/
inline reduce_command shrink(unsigned iaxis, double lower, double upper) {
if (lower == upper)
BOOST_THROW_EXCEPTION(std::invalid_argument("lower != upper required"));
reduce_command r;
r.iaxis = iaxis;
r.range = reduce_command::range_t::values;
r.begin.value = lower;
r.end.value = upper;
r.merge = 1;
r.crop = false;
return r;
}
/** Shrink command to be used in `reduce`.
Command is applied to corresponding axis in order of reduce arguments.
Shrinking is based on an inclusive value interval. The bin which contains the first
value starts the range of bins to keep. The bin which contains the second value is the
last included in that range. When the second value is exactly equal to a lower bin edge,
then the previous bin is the last in the range.
The counts in removed bins are added to the corresponding underflow and overflow bins,
if they are present. If they are not present, the counts are discarded. Also see
`crop`, which always discards the counts.
@param lower bin which contains lower is first to be kept.
@param upper bin which contains upper is last to be kept, except if upper is equal to
the lower edge.
*/
inline reduce_command shrink(double lower, double upper) {
return shrink(reduce_command::unset, lower, upper);
}
/** Crop command to be used in `reduce`.
Command is applied to axis with given index.
Works like `shrink` (see shrink documentation for details), but counts in removed
bins are always discarded, whether underflow and overflow bins are present or not.
@param iaxis which axis to operate on.
@param lower bin which contains lower is first to be kept.
@param upper bin which contains upper is last to be kept, except if upper is equal to
the lower edge.
*/
inline reduce_command crop(unsigned iaxis, double lower, double upper) {
reduce_command r = shrink(iaxis, lower, upper);
r.crop = true;
return r;
}
/** Crop command to be used in `reduce`.
Command is applied to corresponding axis in order of reduce arguments.
Works like `shrink` (see shrink documentation for details), but counts in removed bins
are discarded, whether underflow and overflow bins are present or not. If the cropped
range goes beyond the axis range, then the content of the underflow
or overflow bin which overlaps with the range is kept.
If the counts in an existing underflow or overflow bin are discared by the crop, the
corresponding memory cells are not physically removed. Only their contents are set to
zero. This technical limitation may be lifted in the future, then crop may completely
remove the cropped memory cells.
@param lower bin which contains lower is first to be kept.
@param upper bin which contains upper is last to be kept, except if upper is equal to
the lower edge.
*/
inline reduce_command crop(double lower, double upper) {
return crop(reduce_command::unset, lower, upper);
}
/// Whether to behave like `shrink` or `crop` regarding removed bins.
enum class slice_mode { shrink, crop };
/** Slice command to be used in `reduce`.
Command is applied to axis with given index.
Slicing works like `shrink` or `crop`, but uses bin indices instead of values.
@param iaxis which axis to operate on.
@param begin first index that should be kept.
@param end one past the last index that should be kept.
@param mode whether to behave like `shrink` or `crop` regarding removed bins.
*/
inline reduce_command slice(unsigned iaxis, axis::index_type begin, axis::index_type end,
slice_mode mode = slice_mode::shrink) {
if (!(begin < end))
BOOST_THROW_EXCEPTION(std::invalid_argument("begin < end required"));
reduce_command r;
r.iaxis = iaxis;
r.range = reduce_command::range_t::indices;
r.begin.index = begin;
r.end.index = end;
r.merge = 1;
r.crop = mode == slice_mode::crop;
return r;
}
/** Slice command to be used in `reduce`.
Command is applied to corresponding axis in order of reduce arguments.
Slicing works like `shrink` or `crop`, but uses bin indices instead of values.
@param begin first index that should be kept.
@param end one past the last index that should be kept.
@param mode whether to behave like `shrink` or `crop` regarding removed bins.
*/
inline reduce_command slice(axis::index_type begin, axis::index_type end,
slice_mode mode = slice_mode::shrink) {
return slice(reduce_command::unset, begin, end, mode);
}
/** Rebin command to be used in `reduce`.
Command is applied to axis with given index.
The command merges N adjacent bins into one. This makes the axis coarser and the bins
wider. The original number of bins is divided by N. If there is a rest to this devision,
the axis is implicitly shrunk at the upper end by that rest.
@param iaxis which axis to operate on.
@param merge how many adjacent bins to merge into one.
*/
inline reduce_command rebin(unsigned iaxis, unsigned merge) {
if (merge == 0) BOOST_THROW_EXCEPTION(std::invalid_argument("merge > 0 required"));
reduce_command r;
r.iaxis = iaxis;
r.merge = merge;
r.range = reduce_command::range_t::none;
r.crop = false;
return r;
}
/** Rebin command to be used in `reduce`.
Command is applied to corresponding axis in order of reduce arguments.
The command merges N adjacent bins into one. This makes the axis coarser and the bins
wider. The original number of bins is divided by N. If there is a rest to this devision,
the axis is implicitly shrunk at the upper end by that rest.
@param merge how many adjacent bins to merge into one.
*/
inline reduce_command rebin(unsigned merge) {
return rebin(reduce_command::unset, merge);
}
/** Shrink and rebin command to be used in `reduce`.
Command is applied to corresponding axis in order of reduce arguments.
To shrink(unsigned, double, double) and rebin(unsigned, unsigned) in one command (see
the respective commands for more details). Equivalent to passing both commands for the
same axis to `reduce`.
@param iaxis which axis to operate on.
@param lower lowest bound that should be kept.
@param upper highest bound that should be kept. If upper is inside bin interval, the
whole interval is removed.
@param merge how many adjacent bins to merge into one.
*/
inline auto shrink_and_rebin(unsigned iaxis, double lower, double upper, unsigned merge) {
return reduce_option{iaxis, lower, upper, merge};
*/
inline reduce_command shrink_and_rebin(unsigned iaxis, double lower, double upper,
unsigned merge) {
reduce_command r = shrink(iaxis, lower, upper);
r.merge = rebin(merge).merge;
return r;
}
/**
Shrink option.
/** Shrink and rebin command to be used in `reduce`.
Command is applied to corresponding axis in order of reduce arguments.
To `shrink` and `rebin` in one command (see the respective commands for more
details). Equivalent to passing both commands for the same axis to `reduce`.
@param lower lowest bound that should be kept.
@param upper highest bound that should be kept. If upper is inside bin interval, the
whole interval is removed.
@param merge how many adjacent bins to merge into one.
*/
inline reduce_command shrink_and_rebin(double lower, double upper, unsigned merge) {
return shrink_and_rebin(reduce_command::unset, lower, upper, merge);
}
/** Crop and rebin command to be used in `reduce`.
Command is applied to axis with given index.
To `crop` and `rebin` in one command (see the respective commands for more
details). Equivalent to passing both commands for the same axis to `reduce`.
@param iaxis which axis to operate on.
@param lower lowest bound that should be kept.
@param upper highest bound that should be kept. If upper is inside bin interval, the
whole interval is removed.
*/
inline auto shrink(unsigned iaxis, double lower, double upper) {
return reduce_option{iaxis, lower, upper, 1};
@param upper highest bound that should be kept. If upper is inside bin interval,
the whole interval is removed.
@param merge how many adjacent bins to merge into one.
*/
inline reduce_command crop_and_rebin(unsigned iaxis, double lower, double upper,
unsigned merge) {
reduce_command r = crop(iaxis, lower, upper);
r.merge = rebin(merge).merge;
return r;
}
/**
Rebin option.
/** Crop and rebin command to be used in `reduce`.
Command is applied to corresponding axis in order of reduce arguments.
To `crop` and `rebin` in one command (see the respective commands for more
details). Equivalent to passing both commands for the same axis to `reduce`.
@param lower lowest bound that should be kept.
@param upper highest bound that should be kept. If upper is inside bin interval,
the whole interval is removed.
@param merge how many adjacent bins to merge into one.
*/
inline reduce_command crop_and_rebin(double lower, double upper, unsigned merge) {
return crop_and_rebin(reduce_command::unset, lower, upper, merge);
}
/** Slice and rebin command to be used in `reduce`.
Command is applied to axis with given index.
To `slice` and `rebin` in one command (see the respective commands for more
details). Equivalent to passing both commands for the same axis to `reduce`.
@param iaxis which axis to operate on.
@param begin first index that should be kept.
@param end one past the last index that should be kept.
@param merge how many adjacent bins to merge into one.
*/
inline auto rebin(unsigned iaxis, unsigned merge) {
return reduce_option{iaxis, std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::quiet_NaN(), merge};
@param mode slice mode, see slice_mode.
*/
inline reduce_command slice_and_rebin(unsigned iaxis, axis::index_type begin,
axis::index_type end, unsigned merge,
slice_mode mode = slice_mode::shrink) {
reduce_command r = slice(iaxis, begin, end, mode);
r.merge = rebin(merge).merge;
return r;
}
/**
Convenience overload for single axis.
/** Slice and rebin command to be used in `reduce`.
@param lower lowest bound that should be kept.
@param upper highest bound that should be kept. If upper is inside bin interval, the
whole interval is removed.
Command is applied to corresponding axis in order of reduce arguments.
To `slice` and `rebin` in one command (see the respective commands for more
details). Equivalent to passing both commands for the same axis to `reduce`.
@param begin first index that should be kept.
@param end one past the last index that should be kept.
@param merge how many adjacent bins to merge into one.
@param mode slice mode, see slice_mode.
*/
inline auto shrink_and_rebin(double lower, double upper, unsigned merge) {
return shrink_and_rebin(0, lower, upper, merge);
inline reduce_command slice_and_rebin(axis::index_type begin, axis::index_type end,
unsigned merge,
slice_mode mode = slice_mode::shrink) {
return slice_and_rebin(reduce_command::unset, begin, end, merge, mode);
}
/**
Convenience overload for single axis.
/** Shrink, crop, slice, and/or rebin axes of a histogram.
@param lower lowest bound that should be kept.
@param upper highest bound that should be kept. If upper is inside bin interval, the
whole interval is removed.
*/
inline auto shrink(double lower, double upper) { return shrink(0, lower, upper); }
Returns a new reduced histogram and leaves the original histogram untouched.
/**
Convenience overload for single axis.
The commands `rebin` and `shrink` or `slice` for the same axis are
automatically combined, this is not an error. Passing a `shrink` and a `slice`
command for the same axis or two `rebin` commands triggers an `invalid_argument`
exception. Trying to reducing a non-reducible axis triggers an `invalid_argument`
exception. Histograms with non-reducible axes can still be reduced along the
other axes that are reducible.
@param merge how many adjacent bins to merge into one.
*/
inline auto rebin(unsigned merge) { return rebin(0, merge); }
/**
Shrink and/or rebin axes of a histogram.
Returns the reduced copy of the histogram.
An overload allows one to pass reduce_command as positional arguments.
@param hist original histogram.
@param options iterable sequence of reduce_options, generated by shrink_and_rebin(),
shrink(), and rebin().
*/
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
@param options iterable sequence of reduce commands: `shrink`, `slice`, `rebin`,
`shrink_and_rebin`, or `slice_and_rebin`. The element type of the iterable should be
`reduce_command`.
*/
template <class Histogram, class Iterable, class = detail::requires_iterable<Iterable>>
#else
template <class Histogram, class Iterable>
#endif
decltype(auto) reduce(const Histogram& hist, const Iterable& options) {
Histogram reduce(const Histogram& hist, const Iterable& options) {
using axis::index_type;
const auto& old_axes = unsafe_access::axes(hist);
auto opts = detail::make_stack_buffer(old_axes, reduce_command{});
detail::normalize_reduce_commands(opts, options);
struct option_item : reduce_option {
int begin, end;
};
auto axes =
detail::axes_transform(old_axes, [&opts](std::size_t iaxis, const auto& a_in) {
using A = std::decay_t<decltype(a_in)>;
using AO = axis::traits::get_options<A>;
auto& o = opts[iaxis];
o.is_ordered = axis::traits::ordered(a_in);
if (o.merge > 0) { // option is set?
o.use_underflow_bin = AO::test(axis::option::underflow);
o.use_overflow_bin = AO::test(axis::option::overflow);
return detail::static_if_c<axis::traits::is_reducible<A>::value>(
[&o](const auto& a_in) {
if (o.range == reduce_command::range_t::none) {
// no range restriction, pure rebin
o.begin.index = 0;
o.end.index = a_in.size();
} else {
// range striction, convert values to indices as needed
if (o.range == reduce_command::range_t::values) {
const auto end_value = o.end.value;
o.begin.index = axis::traits::index(a_in, o.begin.value);
o.end.index = axis::traits::index(a_in, o.end.value);
// end = index + 1, unless end_value equal to upper bin edge
if (axis::traits::value_as<double>(a_in, o.end.index) != end_value)
++o.end.index;
}
auto opts = detail::make_stack_buffer<option_item>(old_axes);
for (const auto& o : options) {
auto& oi = opts[o.iaxis];
if (oi.merge > 0) // did we already set the option for this axis?
BOOST_THROW_EXCEPTION(std::invalid_argument("indices must be unique"));
oi.lower = o.lower;
oi.upper = o.upper;
oi.merge = o.merge;
}
// crop flow bins if index range does not include them
if (o.crop) {
o.use_underflow_bin &= o.begin.index < 0;
o.use_overflow_bin &= o.end.index > a_in.size();
}
// make new axes container with default-constructed axis instances
auto axes = detail::make_default(old_axes);
detail::static_if<detail::is_tuple<decltype(axes)>>(
[](auto&, const auto&) {},
[](auto& axes, const auto& old_axes) {
axes.reserve(old_axes.size());
for (const auto& a : old_axes) axes.emplace_back(detail::make_default(a));
},
axes, old_axes);
// now limit [begin, end] to [0, size()]
if (o.begin.index < 0) o.begin.index = 0;
if (o.end.index > a_in.size()) o.end.index = a_in.size();
}
// shorten the index range to a multiple of o.merge;
// example [1, 4] with merge = 2 is reduced to [1, 3]
o.end.index -=
(o.end.index - o.begin.index) % static_cast<index_type>(o.merge);
using A = std::decay_t<decltype(a_in)>;
return A(a_in, o.begin.index, o.end.index, o.merge);
},
[iaxis](const auto& a_in) {
return BOOST_THROW_EXCEPTION(std::invalid_argument(
"axis " + std::to_string(iaxis) + " is not reducible")),
a_in;
},
a_in);
} else {
// command was not set for this axis; fill noop values and copy original axis
o.use_underflow_bin = AO::test(axis::option::underflow);
o.use_overflow_bin = AO::test(axis::option::overflow);
o.merge = 1;
o.begin.index = 0;
o.end.index = a_in.size();
return a_in;
}
});
// override default-constructed axis instances with modified instances
unsigned iaxis = 0;
hist.for_each_axis([&](const auto& a) {
using T = detail::remove_cvref_t<decltype(a)>;
auto result =
Histogram(std::move(axes), detail::make_default(unsafe_access::storage(hist)));
auto& o = opts[iaxis];
o.begin = 0;
o.end = a.size();
if (o.merge > 0) { // option is set?
if (o.lower < o.upper) {
while (o.begin != o.end && a.value(o.begin) < o.lower) ++o.begin;
while (o.end != o.begin && a.value(o.end - 1) >= o.upper) --o.end;
} else if (o.lower > o.upper) {
// for inverted axis::regular
while (o.begin != o.end && a.value(o.begin) > o.lower) ++o.begin;
while (o.end != o.begin && a.value(o.end - 1) <= o.upper) --o.end;
}
o.end -= (o.end - o.begin) % o.merge;
auto a2 = T(a, o.begin, o.end, o.merge);
axis::get<T>(detail::axis_get(axes, iaxis)) = a2;
} else {
o.merge = 1;
axis::get<T>(detail::axis_get(axes, iaxis)) = a;
}
++iaxis;
});
auto storage = detail::make_default(unsafe_access::storage(hist));
auto result = Histogram(std::move(axes), std::move(storage));
auto idx = detail::make_stack_buffer<int>(unsafe_access::axes(result));
for (auto x : indexed(hist, coverage::all)) {
auto idx = detail::make_stack_buffer<index_type>(unsafe_access::axes(result));
for (auto&& x : indexed(hist, coverage::all)) {
auto i = idx.begin();
auto o = opts.begin();
bool skip = false;
for (auto j : x.indices()) {
*i = (j - o->begin);
if (*i <= -1)
*i = (j - o->begin.index);
if (o->is_ordered && *i <= -1) {
*i = -1;
else {
*i /= o->merge;
const int end = (o->end - o->begin) / o->merge;
if (*i > end) *i = end;
if (!o->use_underflow_bin) skip = true;
} else {
if (*i >= 0)
*i /= static_cast<index_type>(o->merge);
else
*i = o->end.index;
const auto reduced_axis_end =
(o->end.index - o->begin.index) / static_cast<index_type>(o->merge);
if (*i >= reduced_axis_end) {
*i = reduced_axis_end;
if (!o->use_overflow_bin) skip = true;
}
}
++i;
++o;
}
result.at(idx) += *x;
if (!skip) result.at(idx) += *x;
}
return result;
}
/**
Shrink and/or rebin axes of a histogram.
/** Shrink, slice, and/or rebin axes of a histogram.
Returns the modified copy.
Returns a new reduced histogram and leaves the original histogram untouched.
The commands `rebin` and `shrink` or `slice` for the same axis are
automatically combined, this is not an error. Passing a `shrink` and a `slice`
command for the same axis or two `rebin` commands triggers an invalid_argument
exception. It is safe to reduce histograms with some axis that are not reducible along
the other axes. Trying to reducing a non-reducible axis triggers an invalid_argument
exception.
An overload allows one to pass an iterable of reduce_command.
@param hist original histogram.
@param opt reduce option generated by shrink_and_rebin(), shrink(), and rebin().
@param opts more reduce_options.
*/
@param opt first reduce command; one of `shrink`, `slice`, `rebin`,
`shrink_and_rebin`, or `slice_or_rebin`.
@param opts more reduce commands.
*/
template <class Histogram, class... Ts>
decltype(auto) reduce(const Histogram& hist, const reduce_option& opt, Ts&&... opts) {
Histogram reduce(const Histogram& hist, const reduce_command& opt, const Ts&... opts) {
// this must be in one line, because any of the ts could be a temporary
return reduce(hist, std::initializer_list<reduce_option>{opt, opts...});
return reduce(hist, std::initializer_list<reduce_command>{opt, opts...});
}
} // namespace algorithm

View File

@@ -9,31 +9,58 @@
#include <boost/histogram/accumulators/sum.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/histogram/indexed.hpp>
#include <boost/mp11/utility.hpp>
#include <numeric>
#include <type_traits>
namespace boost {
namespace histogram {
namespace algorithm {
/** Compute the sum over all histogram cells, including underflow/overflow bins.
If the value type of the histogram is an integral or floating point type,
boost::accumulators::sum<double> is used to compute the sum, else the original value
type is used. Compilation fails, if the value type does not support operator+=.
/** Compute the sum over all histogram cells (underflow/overflow included by default).
Return type is double if the value type of the histogram is integral or floating point,
and the original value type otherwise.
*/
The implementation favors accuracy and protection against overflow over speed. If the
value type of the histogram is an integral or floating point type,
accumulators::sum<double> is used to compute the sum, else the original value type is
used. Compilation fails, if the value type does not support operator+=. The return type
is double if the value type of the histogram is integral or floating point, and the
original value type otherwise.
If you need a different trade-off, you can write your own loop or use `std::accumulate`:
```
// iterate over all bins
auto sum_all = std::accumulate(hist.begin(), hist.end(), 0.0);
// skip underflow/overflow bins
double sum = 0;
for (auto&& x : indexed(hist))
sum += *x; // dereference accessor
// or:
// auto ind = boost::histogram::indexed(hist);
// auto sum = std::accumulate(ind.begin(), ind.end(), 0.0);
```
@returns accumulator type or double
@param hist Const reference to the histogram.
@param cov Iterate over all or only inner bins (optional, default: all).
*/
template <class A, class S>
auto sum(const histogram<A, S>& h) {
auto sum(const histogram<A, S>& hist, const coverage cov = coverage::all) {
using T = typename histogram<A, S>::value_type;
using Sum = mp11::mp_if<std::is_arithmetic<T>, accumulators::sum<double>, T>;
Sum sum;
for (auto x : h) sum += x;
// T is arithmetic, compute sum accurately with high dynamic range
using sum_type = mp11::mp_if<std::is_arithmetic<T>, accumulators::sum<double>, T>;
sum_type sum;
if (cov == coverage::all)
for (auto&& x : hist) sum += x;
else
// sum += x also works if sum_type::operator+=(const sum_type&) exists
for (auto&& x : indexed(hist)) sum += *x;
using R = mp11::mp_if<std::is_arithmetic<T>, double, T>;
return static_cast<R>(sum);
}
} // namespace algorithm
} // namespace histogram
} // namespace boost