Repository: rapidsai/cudf (language: python)

Issue:
[BUG] `segmented_reduce` does not accept empty offsets
When the input is all empty, i.e., we have empty input values and empty offsets, `segmented_reduce` throws an exception:
```
C++ exception with description "CUDF failure at: ../../src/reductions/segmented/reductions.cpp:163: `offsets` should have at least 1 element."
```

This is wrong, as it should be able to handle the case of empty input.


Discussion / hints:
This appears to be working as designed. There are a couple of tests for the case where in the input is empty. Here is one:
https://github.com/rapidsai/cudf/blob/d93e9c267ac7a1a8792d9fc77d2ba8ab7be2683c/cpp/tests/reductions/segmented_reduction_tests.cpp#L1083
I think it reasonable to return an exception when no offsets are provided.
I think that test had to generate offsets because of this bug, not because the API was designed so. If a column is empty (data), having empty offsets should be the correct behavior. We implemented `make_empty_lists_column` (and also empty strings column) by that design.
Nope, this is not a bug but was intentional behavior as per a request from Spark found here: https://github.com/rapidsai/cudf/issues/10556#issuecomment-1126308790

However, I think it reasonable to return an empty column if both the input and offsets are empty.
I can create a PR for that shortly.

Fix patch:
diff --git a/cpp/src/reductions/segmented/reductions.cpp b/cpp/src/reductions/segmented/reductions.cpp
index c4f6c135dde..4843d95346f 100644
--- a/cpp/src/reductions/segmented/reductions.cpp
+++ b/cpp/src/reductions/segmented/reductions.cpp
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include <cudf/column/column_factories.hpp>
 #include <cudf/detail/aggregation/aggregation.hpp>
 #include <cudf/detail/nvtx/ranges.hpp>
 #include <cudf/reduction.hpp>
@@ -118,6 +119,11 @@ std::unique_ptr<column> segmented_reduce(column_view const& segmented_values,
     CUDF_FAIL(
       "Initial value is only supported for SUM, PRODUCT, MIN, MAX, ANY, and ALL aggregation types");
   }
+
+  if (segmented_values.is_empty() && offsets.empty()) {
+    return cudf::make_empty_column(output_dtype);
+  }
+
   CUDF_EXPECTS(offsets.size() > 0, "`offsets` should have at least 1 element.");
 
   return cudf::detail::aggregation_dispatcher(


Test patch:
diff --git a/cpp/tests/reductions/segmented_reduction_tests.cpp b/cpp/tests/reductions/segmented_reduction_tests.cpp
index bc0321bd40a..2281a517aa6 100644
--- a/cpp/tests/reductions/segmented_reduction_tests.cpp
+++ b/cpp/tests/reductions/segmented_reduction_tests.cpp
@@ -1122,6 +1122,26 @@ TEST_F(SegmentedReductionTestUntyped, EmptyInputWithOffsets)
   CUDF_TEST_EXPECT_COLUMNS_EQUAL(*result, expect_bool);
 }
 
+TEST_F(SegmentedReductionTestUntyped, EmptyInputEmptyOffsets)
+{
+  auto const str_empty = cudf::test::strings_column_wrapper{};
+  auto const int_empty = cudf::test::fixed_width_column_wrapper<cudf::size_type>{};
+  auto result =
+    cudf::segmented_reduce(str_empty,
+                           cudf::column_view{int_empty},
+                           *cudf::make_max_aggregation<cudf::segmented_reduce_aggregation>(),
+                           cudf::data_type{cudf::type_id::STRING},
+                           cudf::null_policy::EXCLUDE);
+  CUDF_TEST_EXPECT_COLUMNS_EQUAL(*result, str_empty);
+
+  result = cudf::segmented_reduce(int_empty,
+                                  cudf::column_view{int_empty},
+                                  *cudf::make_min_aggregation<cudf::segmented_reduce_aggregation>(),
+                                  cudf::data_type{cudf::type_id::INT32},
+                                  cudf::null_policy::INCLUDE);
+  CUDF_TEST_EXPECT_COLUMNS_EQUAL(*result, int_empty);
+}
+
 template <typename T>
 struct SegmentedReductionFixedPointTest : public cudf::test::BaseFixture {};