Bug-fix for one-sided cropping and clarify how cropping works (#302)

This commit is contained in:
Hans Dembinski 2020-12-07 15:12:20 +01:00 committed by GitHub
parent 3ff9fbb976
commit 5bd5bfb978
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 77 additions and 14 deletions

View File

@ -111,7 +111,14 @@ inline reduce_command crop(unsigned iaxis, double lower, double upper) {
Command is applied to corresponding axis in order of reduce arguments. Command is applied to corresponding axis in order of reduce arguments.
Works like `shrink` (see shrink documentation for details), but counts in removed bins Works like `shrink` (see shrink documentation for details), but counts in removed bins
are discarded, whether underflow and overflow bins are present or not. 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 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 @param upper bin which contains upper is last to be kept, except if upper is equal to
@ -323,6 +330,8 @@ inline reduce_command slice_and_rebin(axis::index_type begin, axis::index_type e
exception. Histograms with non-reducible axes can still be reduced along the exception. Histograms with non-reducible axes can still be reduced along the
other axes that are reducible. other axes that are reducible.
An overload allows one to pass reduce_command as positional arguments.
@param hist original histogram. @param hist original histogram.
@param options iterable sequence of reduce commands: `shrink`, `slice`, `rebin`, @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 `shrink_and_rebin`, or `slice_and_rebin`. The element type of the iterable should be
@ -343,14 +352,16 @@ Histogram reduce(const Histogram& hist, const Iterable& options) {
auto& o = opts[iaxis]; auto& o = opts[iaxis];
o.is_ordered = axis::traits::ordered(a_in); o.is_ordered = axis::traits::ordered(a_in);
if (o.merge > 0) { // option is set? if (o.merge > 0) { // option is set?
o.use_underflow_bin = !o.crop && AO::test(axis::option::underflow); o.use_underflow_bin = AO::test(axis::option::underflow);
o.use_overflow_bin = !o.crop && AO::test(axis::option::overflow); o.use_overflow_bin = AO::test(axis::option::overflow);
return detail::static_if_c<axis::traits::is_reducible<A>::value>( return detail::static_if_c<axis::traits::is_reducible<A>::value>(
[&o](const auto& a_in) { [&o](const auto& a_in) {
if (o.range == reduce_command::range_t::none) { if (o.range == reduce_command::range_t::none) {
// no range restriction, pure rebin
o.begin.index = 0; o.begin.index = 0;
o.end.index = a_in.size(); o.end.index = a_in.size();
} else { } else {
// range striction, convert values to indices as needed
if (o.range == reduce_command::range_t::values) { if (o.range == reduce_command::range_t::values) {
const auto end_value = o.end.value; const auto end_value = o.end.value;
o.begin.index = axis::traits::index(a_in, o.begin.value); o.begin.index = axis::traits::index(a_in, o.begin.value);
@ -359,7 +370,14 @@ Histogram reduce(const Histogram& hist, const Iterable& options) {
if (axis::traits::value_as<double>(a_in, o.end.index) != end_value) if (axis::traits::value_as<double>(a_in, o.end.index) != end_value)
++o.end.index; ++o.end.index;
} }
// limit [begin, end] to [0, size()]
// 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();
}
// now limit [begin, end] to [0, size()]
if (o.begin.index < 0) o.begin.index = 0; if (o.begin.index < 0) o.begin.index = 0;
if (o.end.index > a_in.size()) o.end.index = a_in.size(); if (o.end.index > a_in.size()) o.end.index = a_in.size();
} }
@ -435,6 +453,8 @@ Histogram reduce(const Histogram& hist, const Iterable& options) {
the other axes. Trying to reducing a non-reducible axis triggers an invalid_argument the other axes. Trying to reducing a non-reducible axis triggers an invalid_argument
exception. exception.
An overload allows one to pass an iterable of reduce_command.
@param hist original histogram. @param hist original histogram.
@param opt first reduce command; one of `shrink`, `slice`, `rebin`, @param opt first reduce command; one of `shrink`, `slice`, `rebin`,
`shrink_and_rebin`, or `slice_or_rebin`. `shrink_and_rebin`, or `slice_or_rebin`.

View File

@ -105,15 +105,16 @@ void run_tests() {
BOOST_TEST_EQ(reduce(h, crop(0, 1.999)).axis(), ID(0, 2)); BOOST_TEST_EQ(reduce(h, crop(0, 1.999)).axis(), ID(0, 2));
} }
// shrink and rebin
{ {
auto h = make_s(Tag(), std::vector<int>(), R(4, 1, 5, "1"), R(3, -1, 2, "2")); auto h = make_s(Tag(), std::vector<int>(), R(4, 1, 5, "1"), R(3, -1, 2, "2"));
/* /*
matrix layout: matrix layout:
x x ->
y 1 0 1 0 y 1 0 1 0
1 1 0 0 | 1 1 0 0
0 2 1 3 v 0 2 1 3
*/ */
h.at(0, 0) = 1; h.at(0, 0) = 1;
h.at(0, 1) = 1; h.at(0, 1) = 1;
@ -122,11 +123,13 @@ void run_tests() {
h.at(2, 0) = 1; h.at(2, 0) = 1;
h.at(2, 2) = 1; h.at(2, 2) = 1;
h.at(3, 2) = 3; h.at(3, 2) = 3;
h.at(-1, -1) = 1; // underflow
h.at(4, 3) = 1; // overflow
// should do nothing, index order does not matter // should do nothing, index order does not matter
auto hr = reduce(h, shrink(1, -1, 2), rebin(0, 1)); auto hr = reduce(h, shrink(1, -1, 2), rebin(0, 1));
BOOST_TEST_EQ(hr.rank(), 2); BOOST_TEST_EQ(hr.rank(), 2);
BOOST_TEST_EQ(sum(hr), 10); BOOST_TEST_EQ(sum(hr), 12);
BOOST_TEST_EQ(hr.axis(0), R(4, 1, 5, "1")); BOOST_TEST_EQ(hr.axis(0), R(4, 1, 5, "1"));
BOOST_TEST_EQ(hr.axis(1), R(3, -1, 2, "2")); BOOST_TEST_EQ(hr.axis(1), R(3, -1, 2, "2"));
BOOST_TEST_EQ(hr, h); BOOST_TEST_EQ(hr, h);
@ -138,7 +141,7 @@ void run_tests() {
// shrinking along first axis // shrinking along first axis
hr = reduce(h, shrink(0, 2, 4)); hr = reduce(h, shrink(0, 2, 4));
BOOST_TEST_EQ(hr.rank(), 2); BOOST_TEST_EQ(hr.rank(), 2);
BOOST_TEST_EQ(sum(hr), 10); BOOST_TEST_EQ(sum(hr), 12);
BOOST_TEST_EQ(hr.axis(0), R(2, 2, 4, "1")); BOOST_TEST_EQ(hr.axis(0), R(2, 2, 4, "1"));
BOOST_TEST_EQ(hr.axis(1), R(3, -1, 2, "2")); BOOST_TEST_EQ(hr.axis(1), R(3, -1, 2, "2"));
BOOST_TEST_EQ(hr.at(-1, 0), 1); // underflow BOOST_TEST_EQ(hr.at(-1, 0), 1); // underflow
@ -164,7 +167,7 @@ void run_tests() {
hr = reduce(h, shrink_and_rebin(0, 2, 5, 2), rebin(1, 3)); hr = reduce(h, shrink_and_rebin(0, 2, 5, 2), rebin(1, 3));
BOOST_TEST_EQ(hr.rank(), 2); BOOST_TEST_EQ(hr.rank(), 2);
BOOST_TEST_EQ(sum(hr), 10); BOOST_TEST_EQ(sum(hr), 12);
BOOST_TEST_EQ(hr.axis(0), R(1, 2, 4, "1")); BOOST_TEST_EQ(hr.axis(0), R(1, 2, 4, "1"));
BOOST_TEST_EQ(hr.axis(1), R(1, -1, 2, "2")); BOOST_TEST_EQ(hr.axis(1), R(1, -1, 2, "2"));
BOOST_TEST_EQ(hr.at(-1, 0), 2); // underflow BOOST_TEST_EQ(hr.at(-1, 0), 2); // underflow
@ -184,16 +187,16 @@ void run_tests() {
BOOST_TEST_EQ(hr4, hr); BOOST_TEST_EQ(hr4, hr);
} }
// crop // crop and rebin
{ {
auto h = make_s(Tag(), std::vector<int>(), R(4, 1, 5), R(3, 1, 4)); auto h = make_s(Tag(), std::vector<int>(), R(4, 1, 5), R(3, 1, 4));
/* /*
matrix layout: matrix layout:
x x ->
y 1 0 1 0 y 1 0 1 0
1 1 0 0 | 1 1 0 0
0 2 1 3 v 0 2 1 3
*/ */
h.at(0, 0) = 1; h.at(0, 0) = 1;
h.at(0, 1) = 1; h.at(0, 1) = 1;
@ -202,6 +205,9 @@ void run_tests() {
h.at(2, 0) = 1; h.at(2, 0) = 1;
h.at(2, 2) = 1; h.at(2, 2) = 1;
h.at(3, 2) = 3; h.at(3, 2) = 3;
h.at(-1, -1) = 1; // underflow
h.at(4, 3) = 1; // overflow
/* /*
crop first and last column in x and y crop first and last column in x and y
@ -231,6 +237,43 @@ void run_tests() {
BOOST_TEST_EQ(hr, hr4); BOOST_TEST_EQ(hr, hr4);
} }
// one-sided crop
{
auto h = make_s(Tag(), std::vector<int>(), ID(1, 4));
std::fill(h.begin(), h.end(), 1);
// underflow: 1
// index 0, x 1: 1
// index 1, x 2: 1
// index 2, x 3: 1
// overflow: 1
BOOST_TEST_EQ(sum(h), 5);
BOOST_TEST_EQ(h.size(), 5);
// keep underflow
auto hr1 = reduce(h, crop(0, 3));
BOOST_TEST_EQ(hr1, reduce(h, slice(-1, 2, slice_mode::crop)));
BOOST_TEST_EQ(sum(hr1), 3);
BOOST_TEST_EQ(hr1.size(), 4); // flow bins are not physically removed, only zeroed
// remove underflow
auto hr2 = reduce(h, crop(1, 3));
BOOST_TEST_EQ(hr2, reduce(h, slice(0, 2, slice_mode::crop)));
BOOST_TEST_EQ(sum(hr2), 2);
BOOST_TEST_EQ(hr2.size(), 4); // flow bins are not physically removed, only zeroed
// keep overflow
auto hr3 = reduce(h, crop(2, 5));
BOOST_TEST_EQ(hr3, reduce(h, slice(1, 4, slice_mode::crop)));
BOOST_TEST_EQ(sum(hr3), 3);
BOOST_TEST_EQ(hr3.size(), 4); // flow bins are not physically removed, only zeroed
// remove overflow
auto hr4 = reduce(h, crop(2, 4));
BOOST_TEST_EQ(hr4, reduce(h, slice(1, 3, slice_mode::crop)));
BOOST_TEST_EQ(sum(hr4), 2);
BOOST_TEST_EQ(hr4.size(), 4); // flow bins are not physically removed, only zeroed
}
// mixed axis types // mixed axis types
{ {
R r(5, 0.0, 5.0); R r(5, 0.0, 5.0);