mirror of
https://github.com/boostorg/histogram.git
synced 2025-05-09 23:04:07 +00:00
add crop command for reduce
This commit is contained in:
parent
a872c6e1c3
commit
3e882bce26
@ -27,6 +27,11 @@ namespace detail {
|
||||
struct reduce_command {
|
||||
static constexpr unsigned unset = static_cast<unsigned>(-1);
|
||||
unsigned iaxis;
|
||||
enum class range_t : char {
|
||||
none,
|
||||
indices,
|
||||
values,
|
||||
} range = range_t::none;
|
||||
union {
|
||||
axis::index_type index;
|
||||
double value;
|
||||
@ -36,36 +41,169 @@ struct reduce_command {
|
||||
double value;
|
||||
} end;
|
||||
unsigned merge = 0; // default value indicates unset option
|
||||
enum class state_t : char {
|
||||
rebin,
|
||||
slice,
|
||||
shrink,
|
||||
} state;
|
||||
bool crop = false;
|
||||
// for internal use by the reduce algorithm
|
||||
bool is_ordered;
|
||||
bool use_underflow_bin;
|
||||
bool use_overflow_bin;
|
||||
bool is_ordered = true;
|
||||
bool use_underflow_bin = true;
|
||||
bool use_overflow_bin = true;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
namespace algorithm {
|
||||
|
||||
/** Base type for all @ref reduce commands.
|
||||
/** Base type for all reduce commands.
|
||||
|
||||
Use this type to store commands in a container. The internals of this type are an
|
||||
implementation detail. Casting a derived command to this base is safe and never causes
|
||||
slicing.
|
||||
*/
|
||||
Use this type to store commands in a container. The internals of this type are an
|
||||
implementation detail. Casting a derived command to this base is safe and never causes
|
||||
object slicing.
|
||||
*/
|
||||
using reduce_command = detail::reduce_command;
|
||||
|
||||
using reduce_option [[deprecated("use reduce_command instead")]] =
|
||||
reduce_command; ///< deprecated
|
||||
|
||||
/** Shrink command to be used in reduce().
|
||||
|
||||
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 @ref
|
||||
crop, which always discards the counts.
|
||||
*/
|
||||
struct shrink : reduce_command {
|
||||
|
||||
/** Command is applied to axis with given index.
|
||||
|
||||
@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.
|
||||
*/
|
||||
shrink(unsigned iaxis, double lower, double upper) {
|
||||
if (lower == upper)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("lower != upper required"));
|
||||
reduce_command::iaxis = iaxis;
|
||||
reduce_command::range = reduce_command::range_t::values;
|
||||
reduce_command::begin.value = lower;
|
||||
reduce_command::end.value = upper;
|
||||
reduce_command::merge = 1;
|
||||
reduce_command::crop = false;
|
||||
}
|
||||
|
||||
/** Command is applied to corresponding axis in order of reduce arguments.
|
||||
|
||||
@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.
|
||||
*/
|
||||
shrink(double lower, double upper) : shrink{reduce_command::unset, lower, upper} {}
|
||||
};
|
||||
|
||||
/** Crop command to be used in reduce().
|
||||
|
||||
Works like @ref shrink (see shrink documentation for details), but counts in removed
|
||||
bins are always discarded, whether underflow and overflow bins are present or not.
|
||||
*/
|
||||
struct crop : reduce_command {
|
||||
|
||||
/** Command is applied to axis with given index.
|
||||
|
||||
@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.
|
||||
*/
|
||||
crop(unsigned iaxis, double lower, double upper)
|
||||
: reduce_command{shrink{iaxis, lower, upper}} {
|
||||
reduce_command::crop = true;
|
||||
}
|
||||
|
||||
/** Command is applied to corresponding axis in order of reduce arguments.
|
||||
|
||||
@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.
|
||||
*/
|
||||
crop(double lower, double upper) : crop{reduce_command::unset, lower, upper} {}
|
||||
};
|
||||
|
||||
/** Slice command to be used in reduce().
|
||||
|
||||
Slicing works like @ref shrink or @ref crop, but uses bin indices instead of values.
|
||||
*/
|
||||
struct slice : reduce_command {
|
||||
|
||||
/// Whether to behave like @ref shrink or @ref crop regarding removed bins.
|
||||
enum class mode { shrink, crop };
|
||||
|
||||
/** Command is applied to axis with given index.
|
||||
|
||||
@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 @ref shrink or @ref crop regarding removed bins.
|
||||
*/
|
||||
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::iaxis = iaxis;
|
||||
reduce_command::range = reduce_command::range_t::indices;
|
||||
reduce_command::begin.index = begin;
|
||||
reduce_command::end.index = end;
|
||||
reduce_command::merge = 1;
|
||||
reduce_command::crop = mode == slice::mode::crop;
|
||||
}
|
||||
|
||||
/** Command is applied to corresponding axis in order of reduce arguments.
|
||||
|
||||
@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 @ref shrink or @ref crop regarding removed bins.
|
||||
*/
|
||||
slice(axis::index_type begin, axis::index_type end,
|
||||
slice::mode mode = slice::mode::shrink)
|
||||
: slice{reduce_command::unset, begin, end, mode} {}
|
||||
};
|
||||
|
||||
/** Rebin command to be used in reduce().
|
||||
|
||||
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.
|
||||
*/
|
||||
struct rebin : reduce_command {
|
||||
|
||||
/** Command is applied to axis with given index.
|
||||
|
||||
@param iaxis which axis to operate on.
|
||||
@param merge how many adjacent bins to merge into one.
|
||||
*/
|
||||
rebin(unsigned iaxis, unsigned merge) {
|
||||
if (merge == 0) BOOST_THROW_EXCEPTION(std::invalid_argument("merge > 0 required"));
|
||||
reduce_command::iaxis = iaxis;
|
||||
reduce_command::merge = merge;
|
||||
reduce_command::range = reduce_command::range_t::none;
|
||||
reduce_command::crop = false;
|
||||
}
|
||||
|
||||
/** Command is applied to corresponding axis in order of reduce arguments.
|
||||
|
||||
@param merge how many adjacent bins to merge into one.
|
||||
*/
|
||||
rebin(unsigned merge) : rebin{reduce_command::unset, merge} {}
|
||||
};
|
||||
|
||||
/** Shrink and rebin command to be used in reduce().
|
||||
|
||||
To @ref shrink and @ref rebin in one command (see the respective commands for more
|
||||
details). Equivalent to passing both commands for the same axis to @ref reduce.
|
||||
*/
|
||||
details). Equivalent to passing both commands for the same axis to reduce().
|
||||
*/
|
||||
struct shrink_and_rebin : reduce_command {
|
||||
|
||||
/** Command is applied to axis with given index.
|
||||
@ -76,15 +214,9 @@ struct shrink_and_rebin : reduce_command {
|
||||
whole interval is removed.
|
||||
@param merge how many adjacent bins to merge into one.
|
||||
*/
|
||||
shrink_and_rebin(unsigned iaxis, double lower, double upper, unsigned merge) {
|
||||
if (lower == upper)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("lower != upper required"));
|
||||
if (merge == 0) BOOST_THROW_EXCEPTION(std::invalid_argument("merge > 0 required"));
|
||||
reduce_command::iaxis = iaxis;
|
||||
reduce_command::begin.value = lower;
|
||||
reduce_command::end.value = upper;
|
||||
reduce_command::merge = merge;
|
||||
reduce_command::state = reduce_command::state_t::shrink;
|
||||
shrink_and_rebin(unsigned iaxis, double lower, double upper, unsigned merge)
|
||||
: reduce_command{shrink{iaxis, lower, upper}} {
|
||||
reduce_command::merge = rebin{0, merge}.merge;
|
||||
}
|
||||
|
||||
/** Command is applied to corresponding axis in order of reduce arguments.
|
||||
@ -98,11 +230,42 @@ struct shrink_and_rebin : reduce_command {
|
||||
: shrink_and_rebin(reduce_command::unset, lower, upper, merge) {}
|
||||
};
|
||||
|
||||
/** Crop and rebin command to be used in reduce().
|
||||
|
||||
To @ref crop and @ref rebin in one command (see the respective commands for more
|
||||
details). Equivalent to passing both commands for the same axis to reduce().
|
||||
*/
|
||||
struct crop_and_rebin : reduce_command {
|
||||
|
||||
/** Command is applied to axis with given index.
|
||||
|
||||
@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.
|
||||
*/
|
||||
crop_and_rebin(unsigned iaxis, double lower, double upper, unsigned merge)
|
||||
: reduce_command{algorithm::crop{iaxis, lower, upper}} {
|
||||
reduce_command::merge = rebin{0, merge}.merge;
|
||||
}
|
||||
|
||||
/** Command is applied to corresponding axis in order of reduce arguments.
|
||||
|
||||
@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.
|
||||
*/
|
||||
crop_and_rebin(double lower, double upper, unsigned merge)
|
||||
: crop_and_rebin(reduce_command::unset, lower, upper, merge) {}
|
||||
};
|
||||
|
||||
/** Slice and rebin command to be used in reduce().
|
||||
|
||||
To @ref slice and @ref rebin in one command (see the respective commands for more
|
||||
details). Equivalent to passing both commands for the same axis to @ref reduce.
|
||||
*/
|
||||
details). Equivalent to passing both commands for the same axis to reduce().
|
||||
*/
|
||||
struct slice_and_rebin : reduce_command {
|
||||
|
||||
/** Command is applied to axis with given index.
|
||||
@ -111,18 +274,12 @@ struct slice_and_rebin : reduce_command {
|
||||
@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.
|
||||
*/
|
||||
slice_and_rebin(unsigned iaxis, axis::index_type begin, axis::index_type end,
|
||||
unsigned merge) {
|
||||
if (!(begin < end))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("begin < end required"));
|
||||
if (merge == 0) BOOST_THROW_EXCEPTION(std::invalid_argument("merge > 0 required"));
|
||||
|
||||
reduce_command::iaxis = iaxis;
|
||||
reduce_command::begin.index = begin;
|
||||
reduce_command::end.index = end;
|
||||
reduce_command::merge = merge;
|
||||
reduce_command::state = reduce_command::state_t::slice;
|
||||
unsigned merge, slice::mode mode = slice::mode::shrink)
|
||||
: reduce_command{slice{iaxis, begin, end, mode}} {
|
||||
reduce_command::merge = rebin{0, merge}.merge;
|
||||
}
|
||||
|
||||
/** Command is applied to corresponding axis in order of reduce arguments.
|
||||
@ -130,89 +287,14 @@ struct slice_and_rebin : reduce_command {
|
||||
@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.
|
||||
*/
|
||||
slice_and_rebin(axis::index_type begin, axis::index_type end, unsigned merge)
|
||||
: slice_and_rebin(reduce_command::unset, begin, end, merge) {}
|
||||
slice_and_rebin(axis::index_type begin, axis::index_type end, unsigned merge,
|
||||
slice::mode mode = slice::mode::shrink)
|
||||
: slice_and_rebin(reduce_command::unset, begin, end, merge, mode) {}
|
||||
};
|
||||
|
||||
/** Shrink command to be used in reduce().
|
||||
|
||||
The shrink is inclusive. 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.
|
||||
*/
|
||||
struct shrink : shrink_and_rebin {
|
||||
|
||||
/** Command is applied to axis with given index.
|
||||
|
||||
@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.
|
||||
*/
|
||||
shrink(unsigned iaxis, double lower, double upper)
|
||||
: shrink_and_rebin{iaxis, lower, upper, 1u} {}
|
||||
|
||||
/** Command is applied to corresponding axis in order of reduce arguments.
|
||||
|
||||
@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.
|
||||
*/
|
||||
shrink(double lower, double upper) : shrink{reduce_command::unset, lower, upper} {}
|
||||
};
|
||||
|
||||
/** Slice command to be used in reduce().
|
||||
|
||||
Slicing works like shrinking, but uses bin indices instead of values.
|
||||
*/
|
||||
struct slice : slice_and_rebin {
|
||||
/** Command is applied to axis with given index.
|
||||
|
||||
@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.
|
||||
*/
|
||||
slice(unsigned iaxis, axis::index_type begin, axis::index_type end)
|
||||
: slice_and_rebin{iaxis, begin, end, 1u} {}
|
||||
|
||||
/** Command is applied to corresponding axis in order of reduce arguments.
|
||||
|
||||
@param begin first index that should be kept.
|
||||
@param end one past the last index that should be kept.
|
||||
*/
|
||||
slice(axis::index_type begin, axis::index_type end)
|
||||
: slice{reduce_command::unset, begin, end} {}
|
||||
};
|
||||
|
||||
/** Rebin command to be used in reduce().
|
||||
|
||||
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.
|
||||
*/
|
||||
struct rebin : reduce_command {
|
||||
/** Command is applied to axis with given index.
|
||||
|
||||
@param iaxis which axis to operate on.
|
||||
@param merge how many adjacent bins to merge into one.
|
||||
*/
|
||||
rebin(unsigned iaxis, unsigned merge) {
|
||||
if (merge == 0) BOOST_THROW_EXCEPTION(std::invalid_argument("merge > 0 required"));
|
||||
reduce_command::iaxis = iaxis;
|
||||
reduce_command::merge = merge;
|
||||
reduce_command::state = reduce_command::state_t::rebin;
|
||||
}
|
||||
|
||||
/** Command is applied to corresponding axis in order of reduce arguments.
|
||||
|
||||
@param merge how many adjacent bins to merge into one.
|
||||
*/
|
||||
rebin(unsigned merge) : rebin{reduce_command::unset, merge} {}
|
||||
};
|
||||
|
||||
/** Shrink, slice, and/or rebin axes of a histogram.
|
||||
/** Shrink, crop, slice, and/or rebin axes of a histogram.
|
||||
|
||||
Returns a new reduced histogram and leaves the original histogram untouched.
|
||||
|
||||
@ -227,7 +309,7 @@ struct rebin : reduce_command {
|
||||
@param options iterable sequence of reduce commands: shrink_and_rebin, slice_and_rebin,
|
||||
@ref shrink, @ref slice, or @ref rebin. The element type of the iterable should be
|
||||
<a href="./boost/histogram/algorithm/reduce_command.html">reduce_command</a>.
|
||||
*/
|
||||
*/
|
||||
template <class Histogram, class Iterable, class = detail::requires_iterable<Iterable>>
|
||||
Histogram reduce(const Histogram& hist, const Iterable& options) {
|
||||
using axis::index_type;
|
||||
@ -235,6 +317,8 @@ Histogram reduce(const Histogram& hist, const Iterable& options) {
|
||||
const auto& old_axes = unsafe_access::axes(hist);
|
||||
|
||||
auto opts = detail::make_stack_buffer<reduce_command>(old_axes);
|
||||
|
||||
// check for invalid commands, merge commands, and set iaxis for positional commands
|
||||
unsigned iaxis = 0;
|
||||
for (const reduce_command& o_in : options) {
|
||||
BOOST_ASSERT(o_in.merge > 0);
|
||||
@ -244,23 +328,22 @@ Histogram reduce(const Histogram& hist, const Iterable& options) {
|
||||
if (o_out.merge == 0) {
|
||||
o_out = o_in;
|
||||
} else {
|
||||
// Some option was already set for this axis, see if we can combine requests.
|
||||
// We can combine a rebin and non-rebin request.
|
||||
if (!((o_in.state == reduce_command::state_t::rebin) ^
|
||||
(o_out.state == reduce_command::state_t::rebin)) ||
|
||||
// Some command was already set for this axis, see if we can combine commands.
|
||||
// We can combine a rebin and non-rebin command.
|
||||
if (!((o_in.range == reduce_command::range_t::none) ^
|
||||
(o_out.range == reduce_command::range_t::none)) ||
|
||||
(o_out.merge > 1 && o_in.merge > 1))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument(
|
||||
"multiple non-fuseable reduce requests for axis " +
|
||||
"multiple conflicting reduce commands for axis " +
|
||||
std::to_string(o_in.iaxis == reduce_command::unset ? iaxis : o_in.iaxis)));
|
||||
if (o_in.state != reduce_command::state_t::rebin) {
|
||||
o_out.state = o_in.state;
|
||||
if (o_in.range != reduce_command::range_t::none) {
|
||||
o_out.range = o_in.range;
|
||||
o_out.begin = o_in.begin;
|
||||
o_out.end = o_in.end;
|
||||
} else {
|
||||
o_out.merge = o_in.merge;
|
||||
}
|
||||
}
|
||||
o_out.iaxis = reduce_command::unset; // value not used below
|
||||
++iaxis;
|
||||
}
|
||||
|
||||
@ -284,14 +367,16 @@ Histogram reduce(const Histogram& hist, const Iterable& options) {
|
||||
auto& o = opts[iaxis];
|
||||
o.is_ordered = axis::traits::ordered(a_in);
|
||||
if (o.merge > 0) { // option is set?
|
||||
o.use_underflow_bin = !o.crop && AO::test(axis::option::underflow);
|
||||
o.use_overflow_bin = !o.crop && AO::test(axis::option::overflow);
|
||||
detail::static_if_c<axis::traits::is_reducible<A>::value>(
|
||||
[&o](auto&& a_out, const auto& a_in) {
|
||||
using A = std::decay_t<decltype(a_in)>;
|
||||
if (o.state == reduce_command::state_t::rebin) {
|
||||
if (o.range == reduce_command::range_t::none) {
|
||||
o.begin.index = 0;
|
||||
o.end.index = a_in.size();
|
||||
} else {
|
||||
if (o.state == reduce_command::state_t::shrink) {
|
||||
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);
|
||||
@ -314,17 +399,14 @@ Histogram reduce(const Histogram& hist, const Iterable& options) {
|
||||
" is not reducible"));
|
||||
},
|
||||
axis::get<A>(detail::axis_get(axes, iaxis)), a_in);
|
||||
// will be configurable with crop()
|
||||
} 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);
|
||||
} else {
|
||||
// option was not set for this axis; fill noop values and copy original axis
|
||||
o.merge = 1;
|
||||
o.begin.index = 0;
|
||||
o.end.index = a_in.size();
|
||||
axis::get<A>(detail::axis_get(axes, iaxis)) = a_in;
|
||||
o.use_underflow_bin = AO::test(axis::option::underflow);
|
||||
o.use_overflow_bin = AO::test(axis::option::overflow);
|
||||
}
|
||||
++iaxis;
|
||||
});
|
||||
@ -380,7 +462,7 @@ Histogram reduce(const Histogram& hist, const Iterable& options) {
|
||||
@param opt first reduce command; one of @ref shrink, @ref slice, @ref rebin,
|
||||
shrink_and_rebin, or slice_or_rebin.
|
||||
@param opts more reduce commands.
|
||||
*/
|
||||
*/
|
||||
template <class Histogram, class... Ts>
|
||||
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
|
||||
|
@ -21,6 +21,15 @@
|
||||
using namespace boost::histogram;
|
||||
using namespace boost::histogram::algorithm;
|
||||
|
||||
struct unreducible {
|
||||
axis::index_type index(int) const { return 0; }
|
||||
axis::index_type size() const { return 1; }
|
||||
friend std::ostream& operator<<(std::ostream& os, const unreducible&) {
|
||||
os << "unreducible";
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Tag>
|
||||
void run_tests() {
|
||||
// limitations: shrink does not work with arguments not convertible to double
|
||||
@ -36,28 +45,33 @@ void run_tests() {
|
||||
|
||||
// not allowed: invalid axis index
|
||||
BOOST_TEST_THROWS((void)reduce(h, slice(10, 2, 3)), std::invalid_argument);
|
||||
// not allowed: repeated indices
|
||||
// two slice requests for same axis not allowed
|
||||
BOOST_TEST_THROWS((void)reduce(h, slice(1, 0, 2), slice(1, 1, 3)),
|
||||
std::invalid_argument);
|
||||
// two rebin requests for same axis cannot be fused
|
||||
// two rebin requests for same axis not allowed
|
||||
BOOST_TEST_THROWS((void)reduce(h, rebin(0, 2), rebin(0, 2)), std::invalid_argument);
|
||||
// rebin and slice_and_rebin with merge > 1 requests for same axis cannot be fused
|
||||
BOOST_TEST_THROWS((void)reduce(h, slice_and_rebin(0, 1, 3, 2), rebin(0, 2)),
|
||||
std::invalid_argument);
|
||||
BOOST_TEST_THROWS((void)reduce(h, shrink(1, 0, 2), shrink(1, 0, 2)),
|
||||
BOOST_TEST_THROWS((void)reduce(h, shrink(1, 0, 2), crop(1, 0, 2)),
|
||||
std::invalid_argument);
|
||||
// not allowed: slice with begin >= end
|
||||
BOOST_TEST_THROWS((void)reduce(h, slice(0, 1, 1)), std::invalid_argument);
|
||||
BOOST_TEST_THROWS((void)reduce(h, slice(0, 2, 1)), std::invalid_argument);
|
||||
// not allowed: shrink with lower == upper
|
||||
BOOST_TEST_THROWS((void)reduce(h, shrink(0, 0, 0)), std::invalid_argument);
|
||||
// not allowed: crop with lower == upper
|
||||
BOOST_TEST_THROWS((void)reduce(h, crop(0, 0, 0)), std::invalid_argument);
|
||||
// not allowed: shrink axis to zero size
|
||||
BOOST_TEST_THROWS((void)reduce(h, shrink(0, 10, 11)), std::invalid_argument);
|
||||
// not allowed: rebin with zero merge
|
||||
BOOST_TEST_THROWS((void)reduce(h, rebin(0, 0)), std::invalid_argument);
|
||||
// not allowed: reducing unreducible axis
|
||||
BOOST_TEST_THROWS((void)reduce(make(Tag(), unreducible{}), slice(0, 1)),
|
||||
std::invalid_argument);
|
||||
}
|
||||
|
||||
// shrink behavior when value on edge and not on edge is inclusive:
|
||||
// shrink and crop behavior when value on edge and not on edge is inclusive:
|
||||
// - lower edge of shrink: pick bin which contains edge, lower <= x < upper
|
||||
// - upper edge of shrink: pick bin which contains edge + 1, lower < x <= upper
|
||||
{
|
||||
@ -78,6 +92,17 @@ void run_tests() {
|
||||
BOOST_TEST_EQ(reduce(h, shrink(0, 2.001)).axis(), ID(0, 3));
|
||||
BOOST_TEST_EQ(reduce(h, shrink(0, 2)).axis(), ID(0, 2));
|
||||
BOOST_TEST_EQ(reduce(h, shrink(0, 1.999)).axis(), ID(0, 2));
|
||||
|
||||
BOOST_TEST_EQ(reduce(h, crop(-1, 5)).axis(), ID(0, 3));
|
||||
BOOST_TEST_EQ(reduce(h, crop(0, 3)).axis(), ID(0, 3));
|
||||
BOOST_TEST_EQ(reduce(h, crop(1, 3)).axis(), ID(1, 3));
|
||||
BOOST_TEST_EQ(reduce(h, crop(1.001, 3)).axis(), ID(1, 3));
|
||||
BOOST_TEST_EQ(reduce(h, crop(1.999, 3)).axis(), ID(1, 3));
|
||||
BOOST_TEST_EQ(reduce(h, crop(2, 3)).axis(), ID(2, 3));
|
||||
BOOST_TEST_EQ(reduce(h, crop(0, 2.999)).axis(), ID(0, 3));
|
||||
BOOST_TEST_EQ(reduce(h, crop(0, 2.001)).axis(), ID(0, 3));
|
||||
BOOST_TEST_EQ(reduce(h, crop(0, 2)).axis(), ID(0, 2));
|
||||
BOOST_TEST_EQ(reduce(h, crop(0, 1.999)).axis(), ID(0, 2));
|
||||
}
|
||||
|
||||
{
|
||||
@ -131,21 +156,17 @@ void run_tests() {
|
||||
|
||||
/*
|
||||
matrix layout:
|
||||
x ->
|
||||
x
|
||||
y 1 0 1 0
|
||||
| 1 1 0 0
|
||||
v 0 2 1 3
|
||||
1 1 0 0
|
||||
0 2 1 3
|
||||
*/
|
||||
|
||||
hr = reduce(h, shrink_and_rebin(0, 2, 5, 2), rebin(1, 3));
|
||||
BOOST_TEST_EQ(hr.rank(), 2);
|
||||
BOOST_TEST_EQ(sum(hr), 10);
|
||||
BOOST_TEST_EQ(hr.axis(0).size(), 1);
|
||||
BOOST_TEST_EQ(hr.axis(1).size(), 1);
|
||||
BOOST_TEST_EQ(hr.axis(0).bin(0).lower(), 2);
|
||||
BOOST_TEST_EQ(hr.axis(0).bin(0).upper(), 4);
|
||||
BOOST_TEST_EQ(hr.axis(1).bin(0).lower(), -1);
|
||||
BOOST_TEST_EQ(hr.axis(1).bin(0).upper(), 2);
|
||||
BOOST_TEST_EQ(hr.axis(0), R(1, 2, 4));
|
||||
BOOST_TEST_EQ(hr.axis(1), R(1, -1, 2));
|
||||
BOOST_TEST_EQ(hr.at(-1, 0), 2); // underflow
|
||||
BOOST_TEST_EQ(hr.at(0, 0), 5);
|
||||
BOOST_TEST_EQ(hr.at(1, 0), 3); // overflow
|
||||
@ -163,16 +184,66 @@ void run_tests() {
|
||||
BOOST_TEST_EQ(hr4, hr);
|
||||
}
|
||||
|
||||
// crop
|
||||
{
|
||||
auto h = make_s(Tag(), std::vector<int>(), R(4, 1, 5), R(3, 1, 4));
|
||||
|
||||
/*
|
||||
matrix layout:
|
||||
x
|
||||
y 1 0 1 0
|
||||
1 1 0 0
|
||||
0 2 1 3
|
||||
*/
|
||||
h.at(0, 0) = 1;
|
||||
h.at(0, 1) = 1;
|
||||
h.at(1, 1) = 1;
|
||||
h.at(1, 2) = 2;
|
||||
h.at(2, 0) = 1;
|
||||
h.at(2, 2) = 1;
|
||||
h.at(3, 2) = 3;
|
||||
|
||||
/*
|
||||
crop first and last column in x and y
|
||||
matrix layout after:
|
||||
x
|
||||
y 3 1
|
||||
*/
|
||||
|
||||
auto hr = reduce(h, crop(2, 4), crop_and_rebin(2, 4, 2));
|
||||
BOOST_TEST_EQ(hr.rank(), 2);
|
||||
BOOST_TEST_EQ(sum(hr), 4);
|
||||
BOOST_TEST_EQ(hr.axis(0), R(2, 2, 4));
|
||||
BOOST_TEST_EQ(hr.axis(1), R(1, 2, 4));
|
||||
BOOST_TEST_EQ(hr.at(0, 0), 3);
|
||||
BOOST_TEST_EQ(hr.at(1, 0), 1);
|
||||
|
||||
// slice with crop mode
|
||||
auto hr2 = reduce(h, slice(1, 3, slice::mode::crop),
|
||||
slice_and_rebin(1, 3, 2, slice::mode::crop));
|
||||
BOOST_TEST_EQ(hr, hr2);
|
||||
|
||||
// explicit axis indices
|
||||
auto hr3 = reduce(h, crop_and_rebin(1, 2, 4, 2), crop(0, 2, 4));
|
||||
BOOST_TEST_EQ(hr, hr3);
|
||||
auto hr4 = reduce(h, slice_and_rebin(1, 1, 3, 2, slice::mode::crop),
|
||||
slice(0, 1, 3, slice::mode::crop));
|
||||
BOOST_TEST_EQ(hr, hr4);
|
||||
}
|
||||
|
||||
// mixed axis types
|
||||
{
|
||||
R r(5, 0.0, 5.0);
|
||||
V v{{1., 2., 3.}};
|
||||
CI c{{1, 2, 3}};
|
||||
auto h = make(Tag(), r, v, c);
|
||||
unreducible u;
|
||||
|
||||
auto h = make(Tag(), r, v, c, u);
|
||||
auto hr = algorithm::reduce(h, shrink(0, 2, 4), slice(2, 1, 3));
|
||||
BOOST_TEST_EQ(hr.axis(0), (R{2, 2, 4}));
|
||||
BOOST_TEST_EQ(hr.axis(1), (V{{1., 2., 3.}}));
|
||||
BOOST_TEST_EQ(hr.axis(2), (CI{{2, 3}}));
|
||||
BOOST_TEST_EQ(hr.axis(3), u);
|
||||
BOOST_TEST_THROWS((void)algorithm::reduce(h, rebin(2, 2)), std::invalid_argument);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user